diff --git a/.docker/oidc-server-mock/cert/docker.pfx b/.docker/oidc-server-mock/cert/docker.pfx new file mode 100644 index 0000000..36f6258 Binary files /dev/null and b/.docker/oidc-server-mock/cert/docker.pfx differ diff --git a/.env b/.env index 86e3ba5..39d597a 100644 --- a/.env +++ b/.env @@ -15,7 +15,8 @@ APP_SECRET= # DATABASE_URL="mysql://app:!ChangeMe!@127.0.0.1:3306/app?serverVersion=8.0.32&charset=utf8mb4" # DATABASE_URL="mysql://app:!ChangeMe!@127.0.0.1:3306/app?serverVersion=10.11.2-MariaDB&charset=utf8mb4" # DATABASE_URL="postgresql://app:!ChangeMe!@127.0.0.1:5432/app?serverVersion=16&charset=utf8" -DATABASE_URL="mysql://db:db@mariadb:3306/db?serverVersion=10.11.14-MariaDB&charset=utf8" +# DATABASE_URL="mysql://db:db@mariadb:3306/db?serverVersion=10.11.14-MariaDB&charset=utf8" +DATABASE_URL="sqlite:///%kernel.project_dir%/var/app.db" ###< doctrine/doctrine-bundle ### ###> symfony/mercure-bundle ### @@ -28,4 +29,31 @@ MERCURE_PUBLIC_URL=https://example.com/.well-known/mercure MERCURE_JWT_SECRET="!ChangeThisMercureHubJWTSecretKey!" ###< symfony/mercure-bundle ### +###> nelmio/cors-bundle ### +CORS_ALLOW_ORIGIN='^https?://(localhost|127\.0\.0\.1)(:[0-9]+)?$' +###< nelmio/cors-bundle ### + +# Set this to a non-empty value in .env.local to disable the OIDC service. +# DOCKER_OIDC_DISABLE="" + DEFAULT_LOCALE=en + +SITE_TITLE="RPA Process Overview" +DEFAULT_URI=https://rpa-process-overview.local.itkdev.dk/ + +# https://github.com/itk-dev/openid-connect-bundle +# "admin" open id connect configuration variables (values provided by the OIDC IdP) +# For production, these must be overridden in .env.local. +ADMIN_OIDC_ALLOW_HTTP=true +ADMIN_OIDC_METADATA_URL=http://idp.rpa-process-overview.local.itkdev.dk/.well-known/openid-configuration +ADMIN_OIDC_CLIENT_ID=client-id +ADMIN_OIDC_CLIENT_SECRET=client-secret +ADMIN_OIDC_REDIRECT_URI=https://rpa-process-overview.local.itkdev.dk/ +ADMIN_OIDC_LEEWAY=30 +ADMIN_OIDC_ROLE_MAP='{ + "overview-manager": ["ROLE_OVERVIEW_MANAGER"] +}' + +# cli redirect url +OIDC_CLI_LOGIN_ROUTE=app_default +###< itk-dev/openid-connect-bundle ### diff --git a/.gitignore b/.gitignore index ca51fd0..1c43129 100644 --- a/.gitignore +++ b/.gitignore @@ -25,3 +25,4 @@ /assets/vendor/ ###< symfony/asset-mapper ### *.local +.idea diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..38411bb --- /dev/null +++ b/.prettierignore @@ -0,0 +1,2 @@ +# The Svelte standalone widgets have a life of their own … +widgets/ diff --git a/CHANGELOG.md b/CHANGELOG.md index ca18d71..e3bea80 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,29 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +* [PR-14](https://github.com/itk-dev/rpa-process-overview/pull/14) + Light mode added +* [PR-13](https://github.com/itk-dev/rpa-process-overview/pull/13) + Cleaned up user stuff +* [PR-11](https://github.com/itk-dev/rpa-process-overview/pull/11) + Add localization +* [PR-10](https://github.com/itk-dev/rpa-process-overview/pull/10) + Added OIDC login +* [PR-9](https://github.com/itk-dev/rpa-process-overview/pull/9) + Tailwind classes in twig and svelte files +* [PR-8](https://github.com/itk-dev/rpa-process-overview/pull/8) + Added users and security and form based login +* [PR-5](https://github.com/itk-dev/rpa-process-overview/pull/5) + Added multi-step form hacks +* [PR-7](https://github.com/itk-dev/rpa-process-overview/pull/7) + Cleaned up +* [PR-6](https://github.com/itk-dev/rpa-process-overview/pull/6) + Setup tailwind +* [PR-4](https://github.com/itk-dev/rpa-process-overview/pull/4) + Added and used `rsync` inside docker service * [PR-3](https://github.com/itk-dev/rpa-process-overview/pull/3) Mocked API with FastAPI and friends. +* [PR-2](https://github.com/rimi-itk/rpa-process-overview/pull/2) + Svelte [Unreleased]: https://github.com/rimi-itk/rpa-process-overview diff --git a/README.md b/README.md index 44a0caa..00ae682 100644 --- a/README.md +++ b/README.md @@ -6,27 +6,109 @@ task help ## Production +First, add a little config to make our tasks use the right docker compose setup: + ``` shell # .env.local TASK_DOCKER_COMPOSE='itkdev-docker-compose' TASK_COMPOSER_INSTALL_ARGS='--no-dev' ``` -## API Mock +Update the site by running: + +``` shell +task site:update +``` + +## Development + +Run ``` shell -docker compose up --build --detach --wait +task site:update +``` + +to get things started. + +Load fixtures with +``` shell +task fixtures:load +``` + +## API Mock + +We use a [FastAPI](https://fastapi.tiangolo.com) app to mock the RPA process overview API. + +``` shell curl "http://$(docker compose port api 8000)/openapi.json" -curl "http://$(docker compose port api 8000)/api/v1/process/" -curl "http://$(docker compose port api 8000)/api/v1/process/" --header 'x-api-key: a-not-so-secret-key' +curl "http://$(docker compose port api 8000)/api/v1/process" +curl "http://$(docker compose port api 8000)/api/v1/process" --header 'x-api-key: a-not-so-secret-key' ``` -Create some data: +Create some fixture data for the API: ``` shell -docker compose exec api uv run python -m src.api.create-data +task api:fixtures:load curl "http://$(docker compose port api 8000)/api/v1/process/" --header 'x-api-key: a-not-so-secret-key' ``` -See [api/README.md](api/README.md) for some more details. +See [api/README.md](api/README.md) for some more details (and [`docker-compose.api.yml`](docker-compose.api.yml) for the +docker compose setup). + +## CORS + +We use [NelmioCorsBundle](https://symfony.com/bundles/NelmioCorsBundle/current/index.html) for widget development. + +``` shell +curl "http://$(task --silent compose -- port nginx 8080)/group/1/overview/1/data" +``` + +``` shell name=cors-test-widget-dev +curl -H "Origin: http://127.0.0.1:3000/ProcessOverview?page=3" \ + -H "Access-Control-Request-Method: GET" \ + -X OPTIONS --verbose \ + "http://$(task --silent compose -- port nginx 8080)/group/1/overview/1/data" +``` + +## User management + +[Symfony supports OpenID Connect](https://symfony.com/doc/current/security/access_token.html#using-openid-connect-oidc), +but our IdP does not play well with that. Therefore, we use our own battle-tested [OpenId Connect +Bundle](https://github.com/itk-dev/openid-connect-bundle) for OIDC login. + +The bundle is configured with some environment variables: + +``` dotenv +# .env.local +ADMIN_OIDC_ALLOW_HTTP=false +# Get these from your IdP provider +ADMIN_OIDC_METADATA_URL=https://…/.well-known/openid-configuration +ADMIN_OIDC_CLIENT_ID=… +ADMIN_OIDC_CLIENT_SECRET=… + +ADMIN_OIDC_REDIRECT_URI=https://rpa-process-overview.example.com/ + +ADMIN_OIDC_ROLE_MAP='{ + "overview-manager": ["ROLE_OVERVIEW_MANAGER"] +}' +``` + +For local testing of OIDC login, we use [OpenId Connect Server Mock](https://github.com/Soluto/oidc-server-mock) (cf. +[`docker-compose.oidc.yml`](docker-compose.oidc.yml)) and the mock is running on +. + +The mock provides these users (cf. [`docker-compose.oidc.yml`](docker-compose.oidc.yml)): + +| Username | Password | Roles | +|------------------|------------------|------------------| +| admin | admin | administrator | +| overview-manager | overview-manager | overview-manager | +| user | user | user | + +> [!TIP] +> Set `DOCKER_OIDC_DISABLE` to a non-empty value in `.env.local` to disable the OIDC service, e.g. +> +> ``` dotend +> # .env.local +> DOCKER_OIDC_DISABLE=true diff --git a/TODO.md b/TODO.md new file mode 100644 index 0000000..ba39025 --- /dev/null +++ b/TODO.md @@ -0,0 +1,8 @@ +# RPA Process Overview + +- [ ] Handle pagination +- [ ] Search for process when creating/editing overview +- [ ] Figure out [how to build tailwind](https://symfony.com/bundles/TailwindBundle/current/index.html#deploying) and set + it up +- [x] Add light mode +- [x] Localizable texts in svelte diff --git a/Taskfile.yml b/Taskfile.yml index 06969aa..8ff130d 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -7,10 +7,14 @@ dotenv: [".env.local", ".env"] vars: DOCKER_COMPOSE: '{{.TASK_DOCKER_COMPOSE | default "docker compose" }}' - COMPOSER_INSTALL_ARGS: '{{.TASK_DOCKER_COMPOSE | default "" }}' + COMPOSER_INSTALL_ARGS: '{{.TASK_COMPOSER_INSTALL_ARGS | default "" }}' includes: coding-standards: ./task/Taskfile.coding-standards.yml + widgets: + # https://taskfile.dev/usage/#directory-of-included-taskfile + taskfile: ./widgets/Taskfile.yml + dir: ./widgets tasks: default: @@ -35,10 +39,7 @@ tasks: TASK_ARGS: pull - task: compose vars: - TASK_ARGS: up --detach --wait - - task: composer - vars: - TASK_ARGS: install {{.COMPOSER_INSTALL_ARGS}} + TASK_ARGS: up --build --detach --wait logs: desc: Show live logs @@ -72,9 +73,12 @@ tasks: desc: Update site deps: [start] cmds: + - task: composer + vars: + TASK_ARGS: install {{.COMPOSER_INSTALL_ARGS}} - task: console vars: - TASK_ARGS: doctrine:migrations:migrate + TASK_ARGS: doctrine:migrations:migrate --no-interaction - task: console vars: TASK_ARGS: cache:clear @@ -87,11 +91,51 @@ tasks: vars: TASK_ARGS: hautelook:fixtures:load --no-interaction + api:fixtures:load: + desc: Load API fixtures + prompt: Continue? + cmds: + - task: compose + vars: + TASK_ARGS: exec api uv run python -m src.api.fixtures + translations:extract: cmds: - # We need a translation from en to en (!) (without prefix) to be able to process placeholders in en. - - "DEFAULT_LOCALE=en task console -- translation:extract --clean --force en --prefix=''" - - "DEFAULT_LOCALE=en task console -- translation:extract --clean --force da" - # Mark default translations (prefixed with `__`) as “Needs work” in Danish translations - - gsed --in-place='' 's/__/__/' translations/*.da.*xlf + - task: script + vars: + TASK_ARGS: "{{.TASK}}" silent: true + + script: + cmds: + - task: compose + vars: + TASK_ARGS: run --rm phpfpm task/scripts/{{.TASK_ARGS}} + internal: true + + build:widgets: + desc: Build widgets for production and copy to public folder + cmds: + - task: widgets:build + + # Build tailwind + - task: console + vars: + TASK_ARGS: tailwind:build + + # Copy result into public folder + - task: compose + vars: + TASK_ARGS: exec phpfpm rm -fr {{.PUBLIC_DIR}} + - task: compose + vars: + # rsync is weird when it comes to including and excluding files … + # https://stackoverflow.com/a/11111793 + TASK_ARGS: exec phpfpm rsync -azv {{.BUILD_DIR}} --include '*/' --include '**/*.css' --include '**/*.js' --exclude '*' --delete {{.PUBLIC_DIR}} + - task: compose + vars: + TASK_ARGS: exec phpfpm find {{.PUBLIC_DIR}} -type f + + vars: + BUILD_DIR: widgets/static/dist/ + PUBLIC_DIR: public/widgets diff --git a/api/README.md b/api/README.md index 78d0a88..4c0df0f 100644 --- a/api/README.md +++ b/api/README.md @@ -1,5 +1,8 @@ # API +> [!WARNING] +> Don't use this API mock for production! + ## Database ``` mermaid @@ -61,7 +64,14 @@ pip install ``` ``` shell name=update-run-step -curl --silent --verbose --location 'http://127.0.0.1:8000/api/v1/process/1/run/3/step/2' --header 'content-type: application/json' --data ' +curl --silent --verbose --location 'http://127.0.0.1:8000/api/v1/process/1/run/2/step/5' --header 'content-type: application/json' --data ' +{ + "status":"SUCCESS", + "started_at": "2025-09-25" +} +' + +curl --silent --verbose --location 'http://127.0.0.1:8000/api/v1/process/1/run/2/step/6' --header 'content-type: application/json' --data ' { "status":"FAILED", "started_at": "2025-09-25", @@ -71,13 +81,6 @@ curl --silent --verbose --location 'http://127.0.0.1:8000/api/v1/process/1/run/3 } } ' - -curl --silent --verbose --location 'http://127.0.0.1:8000/api/v1/process/1/run/3/step/0' --header 'content-type: application/json' --data ' -{ - "status":"SUCCESS", - "started_at": "2025-09-25" -} -' ``` ## Security @@ -87,7 +90,7 @@ Define API keys in `.env.local`, e.g. ``` dotenv # .env.local # Get a token from https://generate-random.org/api-token-generator or some such … -# Notice that the values must not be enclose in single quotes! +# Notice that the values must not be enclosed in single quotes and must not contain spaces! API_KEYS_READ=["a-not-so-secret-key", "759568492f338454603821a04810eabf"] API_KEYS_WRITE=["3825e7be2d1ca130063171d8362ad4996e3a0df1e9f6dd2a4dc6bebf38bfc205"] ``` @@ -97,7 +100,7 @@ Restart the API to load the updated config. Use a key: ``` shell -curl http://127.0.0.1:8000/api/v1/process/ --header 'x-api-key: a-not-so-secret-key' +curl http://127.0.0.1:8000/api/v1/process --header 'x-api-key: a-not-so-secret-key' ``` > [!TIP] diff --git a/api/Taskfile.yml b/api/Taskfile.yml index cbc1054..f8631cd 100644 --- a/api/Taskfile.yml +++ b/api/Taskfile.yml @@ -38,10 +38,15 @@ tasks: fixtures:load: prompt: Really reset data? cmds: - - uv run python -m src.api.create-data + - uv run python -m src.api.fixtures lint: cmds: - ruff format src - - ruff check --select ALL check src --fix + - ruff check --select ALL src --fix # - ruff check --select ALL check src + + sql: + desc: Talk to the database, e.g. `task {{.TASK}} -- "SELECT * FROM process LIMIT 10"` + cmds: + - sqlite3 -header -column database-api.db -- {{.CLI_ARGS}} diff --git a/api/pyproject.toml b/api/pyproject.toml index caadc04..92c4512 100644 --- a/api/pyproject.toml +++ b/api/pyproject.toml @@ -23,5 +23,6 @@ docstring-code-format = true [dependency-groups] dev = [ "faker>=37.8.0", + "python-lsp-server[all]>=1.13.1", "ruff>=0.13.1", ] diff --git a/api/src/api/__init__.py b/api/src/api/__init__.py index 87f4d5d..ab0288f 100644 --- a/api/src/api/__init__.py +++ b/api/src/api/__init__.py @@ -4,16 +4,24 @@ # @todo Resolve this. # ruff: noqa: FAST002 B008 -from typing import Any +from typing import Annotated, Any -from fastapi import Depends, FastAPI, HTTPException, Request, Response, Security +from fastapi import Depends, FastAPI, HTTPException, Path, Query, Request, Response, Security from fastapi_pagination import Page, add_pagination from fastapi_pagination.ext.sqlmodel import paginate from sqlmodel import Session, select from .database import create_db_and_tables, engine from .exception import HTTPNotFoundException, UpdateError -from .models import Process, ProcessPublic, ProcessRun, ProcessRunPublic, ProcessStepRun, ProcessStepRunUpdate +from .models import ( + Process, + ProcessPublic, + ProcessRun, + ProcessRunPublic, + ProcessStepRun, + ProcessStepRunUpdate, + StepRunStatus, +) from .security import get_api_key, get_api_key_write description = """ @@ -41,36 +49,53 @@ def get_session() -> Session: yield session -@app.get(API_PATH_PREFIX + "/process/") +description = """ +Return only processes with these IDs, e.g `/process?id=42&id=87`. + +Invalid (i.e. non-existing) IDs are silently ignored. +""" + + +@app.get(API_PATH_PREFIX + "/process") def read_process_list( *, _: str = Security(get_api_key), request: Request, response: Response, session: Session = Depends(get_session), - q: str | None = None, + ids: Annotated[ + list[int], + Query( + alias="id", + description=description, + ), + ] = [], # noqa: B006 + q: Annotated[str, Query(description="Search query")] = "", ) -> Page[ProcessPublic]: """Get process list. - # Parameters - - * a - * b + ``` shell + /process + /process?q=name + /process?id=…&id=… + ``` """ query = select(Process).order_by(Process.id) - if q is not None: - query = query.filter(Process.name.like(f"%{q}%")) + if len(q) > 0: + query = query.filter(Process.search_index.like(f"%{q}%")) + if ids is not None and len(ids) > 0: + query = query.filter(Process.id.in_(ids)) return _set_pagination_links(request, response, paginate(session, query)) -@app.get(API_PATH_PREFIX + "/process/{process_id}") +@app.get(API_PATH_PREFIX + "/process/{process}") def read_process( *, _: str = Security(get_api_key), session: Session = Depends(get_session), - process_id: int, + process_id: Annotated[int, Path(alias="process")], ) -> ProcessPublic: """Read process.""" process = session.get(Process, process_id) @@ -80,15 +105,40 @@ def read_process( return process -@app.get(API_PATH_PREFIX + "/process/{process_id}/run") -def read_process_run_list( +description_status = ( + """Return only runs with these statusses, e.g `/process/87/run?status=FAILED&status=PENDING`.""" +) + + +description_ids = """Return only runs with these IDs, e.g `/process?id=42&id=87`. + +Invalid (i.e. non-existing) IDs are silently ignored. +""" + + +@app.get(API_PATH_PREFIX + "/process/{process}/run") +def read_process_run_list( # noqa: PLR0913 *, _: str = Security(get_api_key), request: Request, response: Response, session: Session = Depends(get_session), - process_id: int, - q: str | None = None, + process_id: Annotated[int, Path(alias="process")], + status: Annotated[ + list[StepRunStatus], + Query( + alias="status", + description=description_status, + ), + ] = [], # noqa: B006 + ids: Annotated[ + list[int], + Query( + alias="id", + description=description_ids, + ), + ] = [], # noqa: B006 + q: str = "", ) -> Page[ProcessRunPublic]: """Get process list.""" process = session.get(Process, process_id) @@ -97,8 +147,12 @@ def read_process_run_list( query = select(ProcessRun).filter(ProcessRun.process == process) + if status is not None and len(status) > 0: + query = query.filter(ProcessRun.status.in_(status)) if q is not None: - query = query.filter(ProcessRun.meta.like(f"%{q}%")) + query = query.filter(ProcessRun.search_index.like(f"%{q}%")) + if ids is not None and len(ids) > 0: + query = query.filter(ProcessRun.id.in_(ids)) return _set_pagination_links(request, response, paginate(session, query)) @@ -123,13 +177,13 @@ def read_process_run( return run -@app.post(API_PATH_PREFIX + "/process/{process_id}/run/{run_id}/retry") +@app.post(API_PATH_PREFIX + "/process/{process}/run/{run}/retry") def retry_process_run( *, _: str = Security(get_api_key_write), session: Session = Depends(get_session), - process_id: int, - run_id: int, + process_id: Annotated[int, Path(alias="process")], + run_id: Annotated[int, Path(alias="run")], ) -> dict[str, Any]: """Retry process run.""" process = session.get(Process, process_id) @@ -143,15 +197,15 @@ def retry_process_run( return {"ok": True} -@app.post(API_PATH_PREFIX + "/process/{process_id}/run/{run_id}/step/{step_index}") +@app.post(API_PATH_PREFIX + "/process/{process}/run/{run}/step/{step}") def update_process_run( *, _: str = Security(get_api_key_write), update: ProcessStepRunUpdate, session: Session = Depends(get_session), - process_id: int, - run_id: int, - step_index: int, + process_id: Annotated[int, Path(alias="process")], + run_id: Annotated[int, Path(alias="run")], + step_index: Annotated[int, Path(alias="step")], ) -> ProcessRunPublic: """Update a process run.""" process = session.get(Process, process_id) @@ -167,20 +221,19 @@ def update_process_run( except IndexError as e: raise HTTPNotFoundException(detail="Step not found") from e - item = session.query(ProcessStepRun).filter_by(run=run, step_index=step_index).one_or_none() + item = session.query(ProcessStepRun).filter_by(run=run, step_index=step.index).one_or_none() if item is None: item = ProcessStepRun( process=process, run=run, step=step, - # @todo Set this when step is set. - step_index=step.index, ) + try: item.apply_update(update) except UpdateError as e: - raise HTTPException(status_code=400, detail="Cannot update item") from e + raise HTTPException(status_code=400, detail=f"Cannot update item: {e}") from e session.add(item) session.commit() diff --git a/api/src/api/fixtures.py b/api/src/api/fixtures.py index 1f37da3..c8f92d3 100644 --- a/api/src/api/fixtures.py +++ b/api/src/api/fixtures.py @@ -69,7 +69,7 @@ def create_data(self, seed: int = 19750523) -> None: ) session.add(run) - failed_step_index = fake.pyint(0, number_of_steps + 1) + failed_step_index = fake.pyint(-1, number_of_steps + 1) started_at = fake.past_datetime() for index in range(number_of_steps): status = StepRunStatus.SUCCESS if index < failed_step_index else StepRunStatus.PENDING @@ -93,13 +93,13 @@ def create_data(self, seed: int = 19750523) -> None: process=process, run=run, step=steps[index], - step_index=steps[index].index, failure=failure, ) session.add(step_run) - if status == StepRunStatus.FAILED: - break + # @todo Should we generate pending steps? + # if status == StepRunStatus.FAILED: + # break # noqa: ERA001 session.commit() diff --git a/api/src/api/mixins.py b/api/src/api/mixins.py index 53fb617..0f32b30 100644 --- a/api/src/api/mixins.py +++ b/api/src/api/mixins.py @@ -1,6 +1,7 @@ """Mixins.""" # Lifted from https://github.com/iloveitaly/activemodel/blob/master/activemodel/mixins/timestamps.py +from abc import ABC, abstractmethod from datetime import datetime import sqlalchemy as sa @@ -33,3 +34,17 @@ class TimestampsMixin: sa_type=sa.DateTime(timezone=True), sa_column_kwargs={"onupdate": sa.func.now(), "server_default": sa.func.now()}, ) + + +class SearchableMixin(ABC): + """SearchableMixin.""" + + search_index: str | None = Field(default=None) + + def update_search_index(self) -> None: + """Update the search index.""" + self.search_index = self._get_search_index() + + @abstractmethod + def _get_search_index(self) -> str: + pass diff --git a/api/src/api/models.py b/api/src/api/models.py index f2b3641..560bdce 100644 --- a/api/src/api/models.py +++ b/api/src/api/models.py @@ -2,23 +2,14 @@ import enum from datetime import datetime -from typing import Any +from typing import Any, Self -from sqlalchemy import JSON +from sqlalchemy import JSON, event from sqlalchemy.orm import RelationshipProperty -from sqlmodel import Column, Field, Relationship, SQLModel +from sqlmodel import Column, Field, Relationship, Session, SQLModel from .exception import UpdateError -from .mixins import TimestampsMixin - - -class StepRunStatus(str, enum.Enum): - """Status values for a process step run.""" - - PENDING = "PENDING" - SUCCESS = "SUCCESS" - FAILED = "FAILED" - +from .mixins import SearchableMixin, TimestampsMixin # Process @@ -30,7 +21,7 @@ class ProcessBase(SQLModel): meta: dict[str, Any] = Field(sa_column=Column(JSON)) -class Process(ProcessBase, TimestampsMixin, table=True): +class Process(ProcessBase, SearchableMixin, TimestampsMixin, table=True): """Process.""" __tablename__ = "process" @@ -43,6 +34,9 @@ class Process(ProcessBase, TimestampsMixin, table=True): ) runs: list["ProcessRun"] = Relationship(back_populates="process") + def _get_search_index(self) -> str: + return self.name + class ProcessPublic(ProcessBase): """ProcessPublic.""" @@ -83,14 +77,34 @@ class ProcessStepPublic(ProcessStepBase): # Process run +class StepRunStatus(str, enum.Enum): + """Status values for a process step run.""" + + PENDING = "PENDING" + SUCCESS = "SUCCESS" + FAILED = "FAILED" + + class ProcessRunBase(SQLModel): """ProcessRunBase.""" meta: dict[str, Any] = Field(sa_column=Column(JSON)) process_id: int | None = Field(default=None, foreign_key="process.id") + # Reflect the status of steps + status: StepRunStatus | None + def update_status(self) -> Self: + """Update run status based on status of steps.""" + self.status = StepRunStatus.SUCCESS + for step in self.steps: + if step.status != StepRunStatus.SUCCESS: + self.status = step.status + break -class ProcessRun(ProcessRunBase, TimestampsMixin, table=True): + return self + + +class ProcessRun(ProcessRunBase, SearchableMixin, TimestampsMixin, table=True): """ProcessRun.""" __tablename__ = "process_run" @@ -103,6 +117,9 @@ class ProcessRun(ProcessRunBase, TimestampsMixin, table=True): ) process: Process | None = Relationship(back_populates="runs") + def _get_search_index(self) -> str: + return " ".join(list(self.meta.values())) + class ProcessRunPublic(ProcessRunBase): """ProcessRunPublic.""" @@ -147,10 +164,11 @@ class ProcessStepRun(ProcessStepRunBase, TimestampsMixin, table=True): run: ProcessRun | None = Relationship(back_populates="steps") step: ProcessStep | None = Relationship() - def apply_update(self, update: ProcessStepRunUpdate) -> "ProcessStepRun": + def apply_update(self, update: ProcessStepRunUpdate) -> Self: """Apply an update.""" if self.status == StepRunStatus.SUCCESS: - raise UpdateError(detail="Cannot update successful item") + msg = f"Cannot update item with status {self.status.value}" + raise UpdateError(msg) self.status = update.status self.started_at = update.started_at @@ -158,3 +176,29 @@ def apply_update(self, update: ProcessStepRunUpdate) -> "ProcessStepRun": self.failure = update.failure if update.status == StepRunStatus.FAILED else None return self + + +# https://docs.sqlalchemy.org/en/20/orm/events.html +@event.listens_for(Process, "before_insert") +@event.listens_for(Process, "before_update") +@event.listens_for(ProcessRun, "before_insert") +@event.listens_for(ProcessRun, "before_update") +@event.listens_for(ProcessStepRun, "before_insert") +@event.listens_for(ProcessStepRun, "before_update") +def before_update(mapper, connection, target: Process | ProcessRun | ProcessStepRun) -> None: # noqa: ANN001 ARG001 + """Do stuff when models are created and updated.""" + if isinstance(target, SearchableMixin): + target.update_search_index() + + if isinstance(target, ProcessStepRun): + target.step_index = target.step.index if target.step is not None else -1 + + +# https://docs.sqlalchemy.org/en/20/orm/session_events.html#before-flush +# https://docs.sqlalchemy.org/en/20/orm/events.html#sqlalchemy.orm.SessionEvents.before_flush +@event.listens_for(Session, "before_flush") +def _before_flush(session: Session, flush_context, instances) -> None: # noqa: ANN001 ARG001 + for obj in session.dirty.union(session.new): + if isinstance(obj, ProcessStepRun): + obj.run.update_status() + session.add(obj.run) diff --git a/api/uv.lock b/api/uv.lock index 17b6359..1dc641c 100644 --- a/api/uv.lock +++ b/api/uv.lock @@ -24,6 +24,48 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/15/b3/9b1a8074496371342ec1e796a96f99c82c945a339cd81a8e73de28b4cf9e/anyio-4.11.0-py3-none-any.whl", hash = "sha256:0287e96f4d26d4149305414d4e3bc32f0dcd0862365a4bddea19d7a1ec38c4fc", size = 109097, upload-time = "2025-09-23T09:19:10.601Z" }, ] +[[package]] +name = "astroid" +version = "3.3.11" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/18/74/dfb75f9ccd592bbedb175d4a32fc643cf569d7c218508bfbd6ea7ef9c091/astroid-3.3.11.tar.gz", hash = "sha256:1e5a5011af2920c7c67a53f65d536d65bfa7116feeaf2354d8b94f29573bb0ce", size = 400439, upload-time = "2025-07-13T18:04:23.177Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/af/0f/3b8fdc946b4d9cc8cc1e8af42c4e409468c84441b933d037e101b3d72d86/astroid-3.3.11-py3-none-any.whl", hash = "sha256:54c760ae8322ece1abd213057c4b5bba7c49818853fc901ef09719a60dbf9dec", size = 275612, upload-time = "2025-07-13T18:04:21.07Z" }, +] + +[[package]] +name = "autopep8" +version = "2.0.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pycodestyle" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e0/8a/9be661f5400867a09706e29f5ab99a59987fd3a4c337757365e7491fa90b/autopep8-2.0.4.tar.gz", hash = "sha256:2913064abd97b3419d1cc83ea71f042cb821f87e45b9c88cad5ad3c4ea87fe0c", size = 116472, upload-time = "2023-08-26T13:49:59.375Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d8/f2/e63c9f9c485cd90df8e4e7ae90fa3be2469c9641888558c7b45fa98a76f8/autopep8-2.0.4-py2.py3-none-any.whl", hash = "sha256:067959ca4a07b24dbd5345efa8325f5f58da4298dab0dde0443d5ed765de80cb", size = 45340, upload-time = "2023-08-26T13:49:56.111Z" }, +] + +[[package]] +name = "black" +version = "25.9.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, + { name = "mypy-extensions" }, + { name = "packaging" }, + { name = "pathspec" }, + { name = "platformdirs" }, + { name = "pytokens" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/4b/43/20b5c90612d7bdb2bdbcceeb53d588acca3bb8f0e4c5d5c751a2c8fdd55a/black-25.9.0.tar.gz", hash = "sha256:0474bca9a0dd1b51791fcc507a4e02078a1c63f6d4e4ae5544b9848c7adfb619", size = 648393, upload-time = "2025-09-19T00:27:37.758Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/48/99/3acfea65f5e79f45472c45f87ec13037b506522719cd9d4ac86484ff51ac/black-25.9.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0172a012f725b792c358d57fe7b6b6e8e67375dd157f64fa7a3097b3ed3e2175", size = 1742165, upload-time = "2025-09-19T00:34:10.402Z" }, + { url = "https://files.pythonhosted.org/packages/3a/18/799285282c8236a79f25d590f0222dbd6850e14b060dfaa3e720241fd772/black-25.9.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:3bec74ee60f8dfef564b573a96b8930f7b6a538e846123d5ad77ba14a8d7a64f", size = 1581259, upload-time = "2025-09-19T00:32:49.685Z" }, + { url = "https://files.pythonhosted.org/packages/f1/ce/883ec4b6303acdeca93ee06b7622f1fa383c6b3765294824165d49b1a86b/black-25.9.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b756fc75871cb1bcac5499552d771822fd9db5a2bb8db2a7247936ca48f39831", size = 1655583, upload-time = "2025-09-19T00:30:44.505Z" }, + { url = "https://files.pythonhosted.org/packages/21/17/5c253aa80a0639ccc427a5c7144534b661505ae2b5a10b77ebe13fa25334/black-25.9.0-cp313-cp313-win_amd64.whl", hash = "sha256:846d58e3ce7879ec1ffe816bb9df6d006cd9590515ed5d17db14e17666b2b357", size = 1343428, upload-time = "2025-09-19T00:32:13.839Z" }, + { url = "https://files.pythonhosted.org/packages/1b/46/863c90dcd3f9d41b109b7f19032ae0db021f0b2a81482ba0a1e28c84de86/black-25.9.0-py3-none-any.whl", hash = "sha256:474b34c1342cdc157d307b56c4c65bce916480c4a8f6551fdc6bf9b486a7c4ae", size = 203363, upload-time = "2025-09-19T00:27:35.724Z" }, +] + [[package]] name = "certifi" version = "2025.8.3" @@ -54,6 +96,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, ] +[[package]] +name = "dill" +version = "0.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/12/80/630b4b88364e9a8c8c5797f4602d0f76ef820909ee32f0bacb9f90654042/dill-0.4.0.tar.gz", hash = "sha256:0633f1d2df477324f53a895b02c901fb961bdbf65a17122586ea7019292cbcf0", size = 186976, upload-time = "2025-04-16T00:41:48.867Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/50/3d/9373ad9c56321fdab5b41197068e1d8c25883b3fea29dd361f9b55116869/dill-0.4.0-py3-none-any.whl", hash = "sha256:44f54bf6412c2c8464c14e8243eb163690a9800dbe2c367330883b19c7561049", size = 119668, upload-time = "2025-04-16T00:41:47.671Z" }, +] + [[package]] name = "dnspython" version = "2.8.0" @@ -63,6 +114,19 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ba/5a/18ad964b0086c6e62e2e7500f7edc89e3faa45033c71c1893d34eed2b2de/dnspython-2.8.0-py3-none-any.whl", hash = "sha256:01d9bbc4a2d76bf0db7c1f729812ded6d912bd318d3b1cf81d30c0f845dbf3af", size = 331094, upload-time = "2025-09-07T18:57:58.071Z" }, ] +[[package]] +name = "docstring-to-markdown" +version = "0.17" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "importlib-metadata" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/52/d8/8abe80d62c5dce1075578031bcfde07e735bcf0afe2886dd48b470162ab4/docstring_to_markdown-0.17.tar.gz", hash = "sha256:df72a112294c7492487c9da2451cae0faeee06e86008245c188c5761c9590ca3", size = 32260, upload-time = "2025-05-02T15:09:07.932Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/56/7b/af3d0da15bed3a8665419bb3a630585756920f4ad67abfdfef26240ebcc0/docstring_to_markdown-0.17-py3-none-any.whl", hash = "sha256:fd7d5094aa83943bf5f9e1a13701866b7c452eac19765380dead666e36d3711c", size = 23479, upload-time = "2025-05-02T15:09:06.676Z" }, +] + [[package]] name = "email-validator" version = "2.3.0" @@ -164,6 +228,20 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/83/6e/7b6e63c3c004c4805fe8795f8355b30cc72f23aa2162f4072e6035ef1e84/fastapi_pagination-0.14.1-py3-none-any.whl", hash = "sha256:e5b698cab368b525f3b2ea2605c77dc22b00f50f67ac7d382dce8d3243fe60dc", size = 52534, upload-time = "2025-09-03T10:08:18.952Z" }, ] +[[package]] +name = "flake8" +version = "7.1.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mccabe" }, + { name = "pycodestyle" }, + { name = "pyflakes" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/58/16/3f2a0bb700ad65ac9663262905a025917c020a3f92f014d2ba8964b4602c/flake8-7.1.2.tar.gz", hash = "sha256:c586ffd0b41540951ae41af572e6790dbd49fc12b3aa2541685d253d9bd504bd", size = 48119, upload-time = "2025-02-16T18:45:44.296Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/35/f8/08d37b2cd89da306e3520bd27f8a85692122b42b56c0c2c3784ff09c022f/flake8-7.1.2-py2.py3-none-any.whl", hash = "sha256:1cbc62e65536f65e6d754dfe6f1bada7f5cf392d6f5db3c2b85892466c3e7c1a", size = 57745, upload-time = "2025-02-16T18:45:42.351Z" }, +] + [[package]] name = "greenlet" version = "3.2.4" @@ -249,6 +327,39 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442, upload-time = "2024-09-15T18:07:37.964Z" }, ] +[[package]] +name = "importlib-metadata" +version = "8.7.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "zipp" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/76/66/650a33bd90f786193e4de4b3ad86ea60b53c89b669a5c7be931fac31cdb0/importlib_metadata-8.7.0.tar.gz", hash = "sha256:d13b81ad223b890aa16c5471f2ac3056cf76c5f10f82d6f9292f0b415f389000", size = 56641, upload-time = "2025-04-27T15:29:01.736Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/20/b0/36bd937216ec521246249be3bf9855081de4c5e06a0c9b4219dbeda50373/importlib_metadata-8.7.0-py3-none-any.whl", hash = "sha256:e5dd1551894c77868a30651cef00984d50e1002d06942a7101d34870c5f02afd", size = 27656, upload-time = "2025-04-27T15:29:00.214Z" }, +] + +[[package]] +name = "isort" +version = "6.0.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b8/21/1e2a441f74a653a144224d7d21afe8f4169e6c7c20bb13aec3a2dc3815e0/isort-6.0.1.tar.gz", hash = "sha256:1cb5df28dfbc742e490c5e41bad6da41b805b0a8be7bc93cd0fb2a8a890ac450", size = 821955, upload-time = "2025-02-26T21:13:16.955Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c1/11/114d0a5f4dabbdcedc1125dee0888514c3c3b16d3e9facad87ed96fad97c/isort-6.0.1-py3-none-any.whl", hash = "sha256:2dc5d7f65c9678d94c88dfc29161a320eec67328bc97aad576874cb4be1e9615", size = 94186, upload-time = "2025-02-26T21:13:14.911Z" }, +] + +[[package]] +name = "jedi" +version = "0.19.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "parso" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/72/3a/79a912fbd4d8dd6fbb02bf69afd3bb72cf0c729bb3063c6f4498603db17a/jedi-0.19.2.tar.gz", hash = "sha256:4770dc3de41bde3966b02eb84fbcf557fb33cce26ad23da12c742fb50ecb11f0", size = 1231287, upload-time = "2024-11-11T01:41:42.873Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c0/5a/9cac0c82afec3d09ccd97c8b6502d48f165f9124db81b4bcb90b4af974ee/jedi-0.19.2-py2.py3-none-any.whl", hash = "sha256:a8ef22bde8490f57fe5c7681a3c83cb58874daf72b4784de3cce5b6ef6edb5b9", size = 1572278, upload-time = "2024-11-11T01:41:40.175Z" }, +] + [[package]] name = "jinja2" version = "3.1.6" @@ -301,6 +412,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/4f/65/6079a46068dfceaeabb5dcad6d674f5f5c61a6fa5673746f42a9f4c233b3/MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f", size = 15739, upload-time = "2024-10-18T15:21:42.784Z" }, ] +[[package]] +name = "mccabe" +version = "0.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e7/ff/0ffefdcac38932a54d2b5eed4e0ba8a408f215002cd178ad1df0f2806ff8/mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325", size = 9658, upload-time = "2022-01-24T01:14:51.113Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/27/1a/1f68f9ba0c207934b35b86a8ca3aad8395a3d6dd7921c0686e23853ff5a9/mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e", size = 7350, upload-time = "2022-01-24T01:14:49.62Z" }, +] + [[package]] name = "mdurl" version = "0.1.2" @@ -310,6 +430,69 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979, upload-time = "2022-08-14T12:40:09.779Z" }, ] +[[package]] +name = "mypy-extensions" +version = "1.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a2/6e/371856a3fb9d31ca8dac321cda606860fa4548858c0cc45d9d1d4ca2628b/mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558", size = 6343, upload-time = "2025-04-22T14:54:24.164Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/79/7b/2c79738432f5c924bef5071f933bcc9efd0473bac3b4aa584a6f7c1c8df8/mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505", size = 4963, upload-time = "2025-04-22T14:54:22.983Z" }, +] + +[[package]] +name = "packaging" +version = "25.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a1/d4/1fc4078c65507b51b96ca8f8c3ba19e6a61c8253c72794544580a7b6c24d/packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f", size = 165727, upload-time = "2025-04-19T11:48:59.673Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", size = 66469, upload-time = "2025-04-19T11:48:57.875Z" }, +] + +[[package]] +name = "parso" +version = "0.8.5" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d4/de/53e0bcf53d13e005bd8c92e7855142494f41171b34c2536b86187474184d/parso-0.8.5.tar.gz", hash = "sha256:034d7354a9a018bdce352f48b2a8a450f05e9d6ee85db84764e9b6bd96dafe5a", size = 401205, upload-time = "2025-08-23T15:15:28.028Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/16/32/f8e3c85d1d5250232a5d3477a2a28cc291968ff175caeadaf3cc19ce0e4a/parso-0.8.5-py2.py3-none-any.whl", hash = "sha256:646204b5ee239c396d040b90f9e272e9a8017c630092bf59980beb62fd033887", size = 106668, upload-time = "2025-08-23T15:15:25.663Z" }, +] + +[[package]] +name = "pathspec" +version = "0.12.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ca/bc/f35b8446f4531a7cb215605d100cd88b7ac6f44ab3fc94870c120ab3adbf/pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712", size = 51043, upload-time = "2023-12-10T22:30:45Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cc/20/ff623b09d963f88bfde16306a54e12ee5ea43e9b597108672ff3a408aad6/pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08", size = 31191, upload-time = "2023-12-10T22:30:43.14Z" }, +] + +[[package]] +name = "platformdirs" +version = "4.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/23/e8/21db9c9987b0e728855bd57bff6984f67952bea55d6f75e055c46b5383e8/platformdirs-4.4.0.tar.gz", hash = "sha256:ca753cf4d81dc309bc67b0ea38fd15dc97bc30ce419a7f58d13eb3bf14c4febf", size = 21634, upload-time = "2025-08-26T14:32:04.268Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/40/4b/2028861e724d3bd36227adfa20d3fd24c3fc6d52032f4a93c133be5d17ce/platformdirs-4.4.0-py3-none-any.whl", hash = "sha256:abd01743f24e5287cd7a5db3752faf1a2d65353f38ec26d98e25a6db65958c85", size = 18654, upload-time = "2025-08-26T14:32:02.735Z" }, +] + +[[package]] +name = "pluggy" +version = "1.6.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" }, +] + +[[package]] +name = "pycodestyle" +version = "2.12.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/43/aa/210b2c9aedd8c1cbeea31a50e42050ad56187754b34eb214c46709445801/pycodestyle-2.12.1.tar.gz", hash = "sha256:6838eae08bbce4f6accd5d5572075c63626a15ee3e6f842df996bf62f6d73521", size = 39232, upload-time = "2024-08-04T20:26:54.576Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3a/d8/a211b3f85e99a0daa2ddec96c949cac6824bd305b040571b82a03dd62636/pycodestyle-2.12.1-py2.py3-none-any.whl", hash = "sha256:46f0fb92069a7c28ab7bb558f05bfc0110dac69a0cd23c61ea0040283a9d78b3", size = 31284, upload-time = "2024-08-04T20:26:53.173Z" }, +] + [[package]] name = "pydantic" version = "2.11.9" @@ -358,6 +541,27 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/6f/9a/e73262f6c6656262b5fdd723ad90f518f579b7bc8622e43a942eec53c938/pydantic_core-2.33.2-cp313-cp313t-win_amd64.whl", hash = "sha256:c2fc0a768ef76c15ab9238afa6da7f69895bb5d1ee83aeea2e3509af4472d0b9", size = 1935777, upload-time = "2025-04-23T18:32:25.088Z" }, ] +[[package]] +name = "pydocstyle" +version = "6.3.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "snowballstemmer" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e9/5c/d5385ca59fd065e3c6a5fe19f9bc9d5ea7f2509fa8c9c22fb6b2031dd953/pydocstyle-6.3.0.tar.gz", hash = "sha256:7ce43f0c0ac87b07494eb9c0b462c0b73e6ff276807f204d6b53edc72b7e44e1", size = 36796, upload-time = "2023-01-17T20:29:19.838Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/36/ea/99ddefac41971acad68f14114f38261c1f27dac0b3ec529824ebc739bdaa/pydocstyle-6.3.0-py3-none-any.whl", hash = "sha256:118762d452a49d6b05e194ef344a55822987a462831ade91ec5c06fd2169d019", size = 38038, upload-time = "2023-01-17T20:29:18.094Z" }, +] + +[[package]] +name = "pyflakes" +version = "3.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/57/f9/669d8c9c86613c9d568757c7f5824bd3197d7b1c6c27553bc5618a27cce2/pyflakes-3.2.0.tar.gz", hash = "sha256:1c61603ff154621fb2a9172037d84dca3500def8c8b630657d1701f026f8af3f", size = 63788, upload-time = "2024-01-05T00:28:47.703Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d4/d7/f1b7db88d8e4417c5d47adad627a93547f44bdc9028372dbd2313f34a855/pyflakes-3.2.0-py2.py3-none-any.whl", hash = "sha256:84b5be138a2dfbb40689ca07e2152deb896a65c3a3e24c251c5c62489568074a", size = 62725, upload-time = "2024-01-05T00:28:45.903Z" }, +] + [[package]] name = "pygments" version = "2.19.2" @@ -367,6 +571,24 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217, upload-time = "2025-06-21T13:39:07.939Z" }, ] +[[package]] +name = "pylint" +version = "3.3.8" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "astroid" }, + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "dill" }, + { name = "isort" }, + { name = "mccabe" }, + { name = "platformdirs" }, + { name = "tomlkit" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/9d/58/1f614a84d3295c542e9f6e2c764533eea3f318f4592dc1ea06c797114767/pylint-3.3.8.tar.gz", hash = "sha256:26698de19941363037e2937d3db9ed94fb3303fdadf7d98847875345a8bb6b05", size = 1523947, upload-time = "2025-08-09T09:12:57.234Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2d/1a/711e93a7ab6c392e349428ea56e794a3902bb4e0284c1997cff2d7efdbc1/pylint-3.3.8-py3-none-any.whl", hash = "sha256:7ef94aa692a600e82fabdd17102b73fc226758218c97473c7ad67bd4cb905d83", size = 523153, upload-time = "2025-08-09T09:12:54.836Z" }, +] + [[package]] name = "python-dotenv" version = "1.1.1" @@ -376,6 +598,49 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/5f/ed/539768cf28c661b5b068d66d96a2f155c4971a5d55684a514c1a0e0dec2f/python_dotenv-1.1.1-py3-none-any.whl", hash = "sha256:31f23644fe2602f88ff55e1f5c79ba497e01224ee7737937930c448e4d0e24dc", size = 20556, upload-time = "2025-06-24T04:21:06.073Z" }, ] +[[package]] +name = "python-lsp-jsonrpc" +version = "1.1.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "ujson" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/48/b6/fd92e2ea4635d88966bb42c20198df1a981340f07843b5e3c6694ba3557b/python-lsp-jsonrpc-1.1.2.tar.gz", hash = "sha256:4688e453eef55cd952bff762c705cedefa12055c0aec17a06f595bcc002cc912", size = 15298, upload-time = "2023-09-23T17:48:30.451Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cb/d9/656659d5b5d5f402b2b174cd0ba9bc827e07ce3c0bf88da65424baf64af8/python_lsp_jsonrpc-1.1.2-py3-none-any.whl", hash = "sha256:7339c2e9630ae98903fdaea1ace8c47fba0484983794d6aafd0bd8989be2b03c", size = 8805, upload-time = "2023-09-23T17:48:28.804Z" }, +] + +[[package]] +name = "python-lsp-server" +version = "1.13.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "black" }, + { name = "docstring-to-markdown" }, + { name = "jedi" }, + { name = "pluggy" }, + { name = "python-lsp-jsonrpc" }, + { name = "ujson" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ca/92/bd60cbe7d7d6c90e5e556a90497aa1892a3f779d9915026eca6e37a0b59b/python_lsp_server-1.13.1.tar.gz", hash = "sha256:bfa3d6bbca3fc3e6d0137b27cd1eabee65783a8d4314c36e1e230c603419afa3", size = 120484, upload-time = "2025-08-26T16:51:07.927Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/36/03/2884cf7bd092d8a5a71a406971fd2edf5c6171147ca2d4afb3f2a7f8c0f1/python_lsp_server-1.13.1-py3-none-any.whl", hash = "sha256:fadf45275d12a9d9a13e36717a8383cee8e7cffe8a30698d38bfb3fe71b5cdcd", size = 76748, upload-time = "2025-08-26T16:51:05.873Z" }, +] + +[package.optional-dependencies] +all = [ + { name = "autopep8" }, + { name = "flake8" }, + { name = "mccabe" }, + { name = "pycodestyle" }, + { name = "pydocstyle" }, + { name = "pyflakes" }, + { name = "pylint" }, + { name = "rope" }, + { name = "whatthepatch" }, + { name = "yapf" }, +] + [[package]] name = "python-multipart" version = "0.0.20" @@ -385,6 +650,32 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/45/58/38b5afbc1a800eeea951b9285d3912613f2603bdf897a4ab0f4bd7f405fc/python_multipart-0.0.20-py3-none-any.whl", hash = "sha256:8a62d3a8335e06589fe01f2a3e178cdcc632f3fbe0d492ad9ee0ec35aab1f104", size = 24546, upload-time = "2024-12-16T19:45:44.423Z" }, ] +[[package]] +name = "pytokens" +version = "0.1.10" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/30/5f/e959a442435e24f6fb5a01aec6c657079ceaca1b3baf18561c3728d681da/pytokens-0.1.10.tar.gz", hash = "sha256:c9a4bfa0be1d26aebce03e6884ba454e842f186a59ea43a6d3b25af58223c044", size = 12171, upload-time = "2025-02-19T14:51:22.001Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/60/e5/63bed382f6a7a5ba70e7e132b8b7b8abbcf4888ffa6be4877698dcfbed7d/pytokens-0.1.10-py3-none-any.whl", hash = "sha256:db7b72284e480e69fb085d9f251f66b3d2df8b7166059261258ff35f50fb711b", size = 12046, upload-time = "2025-02-19T14:51:18.694Z" }, +] + +[[package]] +name = "pytoolconfig" +version = "1.3.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "packaging" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/18/dc/abf70d2c2bcac20e8c71a7cdf6d44e4ddba4edf65acb179248d554d743db/pytoolconfig-1.3.1.tar.gz", hash = "sha256:51e6bd1a6f108238ae6aab6a65e5eed5e75d456be1c2bf29b04e5c1e7d7adbae", size = 16655, upload-time = "2024-01-11T16:25:11.914Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/92/44/da239917f5711ca7105f7d7f9e2765716dd883b241529beafc0f28504725/pytoolconfig-1.3.1-py3-none-any.whl", hash = "sha256:5d8cea8ae1996938ec3eaf44567bbc5ef1bc900742190c439a44a704d6e1b62b", size = 17022, upload-time = "2024-01-11T16:25:10.589Z" }, +] + +[package.optional-dependencies] +global = [ + { name = "platformdirs" }, +] + [[package]] name = "pyyaml" version = "6.0.2" @@ -461,6 +752,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/0b/92/186693c8f838d670510ac1dfb35afbe964320fbffb343ba18f3d24441941/rignore-0.6.4-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6971ac9fdd5a0bd299a181096f091c4f3fd286643adceba98eccc03c688a6637", size = 974663, upload-time = "2025-07-19T19:23:28.24Z" }, ] +[[package]] +name = "rope" +version = "1.14.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pytoolconfig", extra = ["global"] }, +] +sdist = { url = "https://files.pythonhosted.org/packages/74/3a/85e60d154f26ecdc1d47a63ac58bd9f32a5a9f3f771f6672197f02a00ade/rope-1.14.0.tar.gz", hash = "sha256:8803e3b667315044f6270b0c69a10c0679f9f322ed8efe6245a93ceb7658da69", size = 296801, upload-time = "2025-07-12T17:46:07.786Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/75/35/130469d1901da2b3a5a377539b4ffcd8a5c983f1c9e3ba5ffdd8d71ae314/rope-1.14.0-py3-none-any.whl", hash = "sha256:00a7ea8c0c376fc0b053b2f2f8ef3bfb8b50fecf1ebf3eb80e4f8bd7f1941918", size = 207143, upload-time = "2025-07-12T17:46:05.928Z" }, +] + [[package]] name = "rpa-process-overview-api" version = "0.1.0" @@ -474,6 +777,7 @@ dependencies = [ [package.dev-dependencies] dev = [ { name = "faker" }, + { name = "python-lsp-server", extra = ["all"] }, { name = "ruff" }, ] @@ -487,6 +791,7 @@ requires-dist = [ [package.metadata.requires-dev] dev = [ { name = "faker", specifier = ">=37.8.0" }, + { name = "python-lsp-server", extras = ["all"], specifier = ">=1.13.1" }, { name = "ruff", specifier = ">=0.13.1" }, ] @@ -547,6 +852,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235, upload-time = "2024-02-25T23:20:01.196Z" }, ] +[[package]] +name = "snowballstemmer" +version = "3.0.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/75/a7/9810d872919697c9d01295633f5d574fb416d47e535f258272ca1f01f447/snowballstemmer-3.0.1.tar.gz", hash = "sha256:6d5eeeec8e9f84d4d56b847692bacf79bc2c8e90c7f80ca4444ff8b6f2e52895", size = 105575, upload-time = "2025-05-09T16:34:51.843Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c8/78/3565d011c61f5a43488987ee32b6f3f656e7f107ac2782dd57bdd7d91d9a/snowballstemmer-3.0.1-py3-none-any.whl", hash = "sha256:6cd7b3897da8d6c9ffb968a6781fa6532dce9c3618a4b127d920dab764a19064", size = 103274, upload-time = "2025-05-09T16:34:50.371Z" }, +] + [[package]] name = "sqlalchemy" version = "2.0.43" @@ -593,6 +907,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/be/72/2db2f49247d0a18b4f1bb9a5a39a0162869acf235f3a96418363947b3d46/starlette-0.48.0-py3-none-any.whl", hash = "sha256:0764ca97b097582558ecb498132ed0c7d942f233f365b86ba37770e026510659", size = 73736, upload-time = "2025-09-13T08:41:03.869Z" }, ] +[[package]] +name = "tomlkit" +version = "0.13.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/cc/18/0bbf3884e9eaa38819ebe46a7bd25dcd56b67434402b66a58c4b8e552575/tomlkit-0.13.3.tar.gz", hash = "sha256:430cf247ee57df2b94ee3fbe588e71d362a941ebb545dec29b53961d61add2a1", size = 185207, upload-time = "2025-06-05T07:13:44.947Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bd/75/8539d011f6be8e29f339c42e633aae3cb73bffa95dd0f9adec09b9c58e85/tomlkit-0.13.3-py3-none-any.whl", hash = "sha256:c89c649d79ee40629a9fda55f8ace8c6a1b42deb912b2a8fd8d942ddadb606b0", size = 38901, upload-time = "2025-06-05T07:13:43.546Z" }, +] + [[package]] name = "typer" version = "0.19.2" @@ -638,6 +961,47 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/5c/23/c7abc0ca0a1526a0774eca151daeb8de62ec457e77262b66b359c3c7679e/tzdata-2025.2-py2.py3-none-any.whl", hash = "sha256:1a403fada01ff9221ca8044d701868fa132215d84beb92242d9acd2147f667a8", size = 347839, upload-time = "2025-03-23T13:54:41.845Z" }, ] +[[package]] +name = "ujson" +version = "5.11.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/43/d9/3f17e3c5773fb4941c68d9a37a47b1a79c9649d6c56aefbed87cc409d18a/ujson-5.11.0.tar.gz", hash = "sha256:e204ae6f909f099ba6b6b942131cee359ddda2b6e4ea39c12eb8b991fe2010e0", size = 7156583, upload-time = "2025-08-20T11:57:02.452Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1c/ec/2de9dd371d52c377abc05d2b725645326c4562fc87296a8907c7bcdf2db7/ujson-5.11.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:109f59885041b14ee9569bf0bb3f98579c3fa0652317b355669939e5fc5ede53", size = 55435, upload-time = "2025-08-20T11:55:50.243Z" }, + { url = "https://files.pythonhosted.org/packages/5b/a4/f611f816eac3a581d8a4372f6967c3ed41eddbae4008d1d77f223f1a4e0a/ujson-5.11.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a31c6b8004438e8c20fc55ac1c0e07dad42941db24176fe9acf2815971f8e752", size = 53193, upload-time = "2025-08-20T11:55:51.373Z" }, + { url = "https://files.pythonhosted.org/packages/e9/c5/c161940967184de96f5cbbbcce45b562a4bf851d60f4c677704b1770136d/ujson-5.11.0-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:78c684fb21255b9b90320ba7e199780f653e03f6c2528663768965f4126a5b50", size = 57603, upload-time = "2025-08-20T11:55:52.583Z" }, + { url = "https://files.pythonhosted.org/packages/2b/d6/c7b2444238f5b2e2d0e3dab300b9ddc3606e4b1f0e4bed5a48157cebc792/ujson-5.11.0-cp313-cp313-manylinux_2_24_i686.manylinux_2_28_i686.whl", hash = "sha256:4c9f5d6a27d035dd90a146f7761c2272cf7103de5127c9ab9c4cd39ea61e878a", size = 59794, upload-time = "2025-08-20T11:55:53.69Z" }, + { url = "https://files.pythonhosted.org/packages/fe/a3/292551f936d3d02d9af148f53e1bc04306b00a7cf1fcbb86fa0d1c887242/ujson-5.11.0-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:837da4d27fed5fdc1b630bd18f519744b23a0b5ada1bbde1a36ba463f2900c03", size = 57363, upload-time = "2025-08-20T11:55:54.843Z" }, + { url = "https://files.pythonhosted.org/packages/90/a6/82cfa70448831b1a9e73f882225980b5c689bf539ec6400b31656a60ea46/ujson-5.11.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:787aff4a84da301b7f3bac09bc696e2e5670df829c6f8ecf39916b4e7e24e701", size = 1036311, upload-time = "2025-08-20T11:55:56.197Z" }, + { url = "https://files.pythonhosted.org/packages/84/5c/96e2266be50f21e9b27acaee8ca8f23ea0b85cb998c33d4f53147687839b/ujson-5.11.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:6dd703c3e86dc6f7044c5ac0b3ae079ed96bf297974598116aa5fb7f655c3a60", size = 1195783, upload-time = "2025-08-20T11:55:58.081Z" }, + { url = "https://files.pythonhosted.org/packages/8d/20/78abe3d808cf3bb3e76f71fca46cd208317bf461c905d79f0d26b9df20f1/ujson-5.11.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:3772e4fe6b0c1e025ba3c50841a0ca4786825a4894c8411bf8d3afe3a8061328", size = 1088822, upload-time = "2025-08-20T11:55:59.469Z" }, + { url = "https://files.pythonhosted.org/packages/d8/50/8856e24bec5e2fc7f775d867aeb7a3f137359356200ac44658f1f2c834b2/ujson-5.11.0-cp313-cp313-win32.whl", hash = "sha256:8fa2af7c1459204b7a42e98263b069bd535ea0cd978b4d6982f35af5a04a4241", size = 39753, upload-time = "2025-08-20T11:56:01.345Z" }, + { url = "https://files.pythonhosted.org/packages/5b/d8/1baee0f4179a4d0f5ce086832147b6cc9b7731c24ca08e14a3fdb8d39c32/ujson-5.11.0-cp313-cp313-win_amd64.whl", hash = "sha256:34032aeca4510a7c7102bd5933f59a37f63891f30a0706fb46487ab6f0edf8f0", size = 43866, upload-time = "2025-08-20T11:56:02.552Z" }, + { url = "https://files.pythonhosted.org/packages/a9/8c/6d85ef5be82c6d66adced3ec5ef23353ed710a11f70b0b6a836878396334/ujson-5.11.0-cp313-cp313-win_arm64.whl", hash = "sha256:ce076f2df2e1aa62b685086fbad67f2b1d3048369664b4cdccc50707325401f9", size = 38363, upload-time = "2025-08-20T11:56:03.688Z" }, + { url = "https://files.pythonhosted.org/packages/28/08/4518146f4984d112764b1dfa6fb7bad691c44a401adadaa5e23ccd930053/ujson-5.11.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:65724738c73645db88f70ba1f2e6fb678f913281804d5da2fd02c8c5839af302", size = 55462, upload-time = "2025-08-20T11:56:04.873Z" }, + { url = "https://files.pythonhosted.org/packages/29/37/2107b9a62168867a692654d8766b81bd2fd1e1ba13e2ec90555861e02b0c/ujson-5.11.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:29113c003ca33ab71b1b480bde952fbab2a0b6b03a4ee4c3d71687cdcbd1a29d", size = 53246, upload-time = "2025-08-20T11:56:06.054Z" }, + { url = "https://files.pythonhosted.org/packages/9b/f8/25583c70f83788edbe3ca62ce6c1b79eff465d78dec5eb2b2b56b3e98b33/ujson-5.11.0-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c44c703842024d796b4c78542a6fcd5c3cb948b9fc2a73ee65b9c86a22ee3638", size = 57631, upload-time = "2025-08-20T11:56:07.374Z" }, + { url = "https://files.pythonhosted.org/packages/ed/ca/19b3a632933a09d696f10dc1b0dfa1d692e65ad507d12340116ce4f67967/ujson-5.11.0-cp314-cp314-manylinux_2_24_i686.manylinux_2_28_i686.whl", hash = "sha256:e750c436fb90edf85585f5c62a35b35082502383840962c6983403d1bd96a02c", size = 59877, upload-time = "2025-08-20T11:56:08.534Z" }, + { url = "https://files.pythonhosted.org/packages/55/7a/4572af5324ad4b2bfdd2321e898a527050290147b4ea337a79a0e4e87ec7/ujson-5.11.0-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f278b31a7c52eb0947b2db55a5133fbc46b6f0ef49972cd1a80843b72e135aba", size = 57363, upload-time = "2025-08-20T11:56:09.758Z" }, + { url = "https://files.pythonhosted.org/packages/7b/71/a2b8c19cf4e1efe53cf439cdf7198ac60ae15471d2f1040b490c1f0f831f/ujson-5.11.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ab2cb8351d976e788669c8281465d44d4e94413718af497b4e7342d7b2f78018", size = 1036394, upload-time = "2025-08-20T11:56:11.168Z" }, + { url = "https://files.pythonhosted.org/packages/7a/3e/7b98668cba3bb3735929c31b999b374ebc02c19dfa98dfebaeeb5c8597ca/ujson-5.11.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:090b4d11b380ae25453100b722d0609d5051ffe98f80ec52853ccf8249dfd840", size = 1195837, upload-time = "2025-08-20T11:56:12.6Z" }, + { url = "https://files.pythonhosted.org/packages/a1/ea/8870f208c20b43571a5c409ebb2fe9b9dba5f494e9e60f9314ac01ea8f78/ujson-5.11.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:80017e870d882d5517d28995b62e4e518a894f932f1e242cbc802a2fd64d365c", size = 1088837, upload-time = "2025-08-20T11:56:14.15Z" }, + { url = "https://files.pythonhosted.org/packages/63/b6/c0e6607e37fa47929920a685a968c6b990a802dec65e9c5181e97845985d/ujson-5.11.0-cp314-cp314-win32.whl", hash = "sha256:1d663b96eb34c93392e9caae19c099ec4133ba21654b081956613327f0e973ac", size = 41022, upload-time = "2025-08-20T11:56:15.509Z" }, + { url = "https://files.pythonhosted.org/packages/4e/56/f4fe86b4c9000affd63e9219e59b222dc48b01c534533093e798bf617a7e/ujson-5.11.0-cp314-cp314-win_amd64.whl", hash = "sha256:849e65b696f0d242833f1df4182096cedc50d414215d1371fca85c541fbff629", size = 45111, upload-time = "2025-08-20T11:56:16.597Z" }, + { url = "https://files.pythonhosted.org/packages/0a/f3/669437f0280308db4783b12a6d88c00730b394327d8334cc7a32ef218e64/ujson-5.11.0-cp314-cp314-win_arm64.whl", hash = "sha256:e73df8648c9470af2b6a6bf5250d4744ad2cf3d774dcf8c6e31f018bdd04d764", size = 39682, upload-time = "2025-08-20T11:56:17.763Z" }, + { url = "https://files.pythonhosted.org/packages/6e/cd/e9809b064a89fe5c4184649adeb13c1b98652db3f8518980b04227358574/ujson-5.11.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:de6e88f62796372fba1de973c11138f197d3e0e1d80bcb2b8aae1e826096d433", size = 55759, upload-time = "2025-08-20T11:56:18.882Z" }, + { url = "https://files.pythonhosted.org/packages/1b/be/ae26a6321179ebbb3a2e2685b9007c71bcda41ad7a77bbbe164005e956fc/ujson-5.11.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:49e56ef8066f11b80d620985ae36869a3ff7e4b74c3b6129182ec5d1df0255f3", size = 53634, upload-time = "2025-08-20T11:56:20.012Z" }, + { url = "https://files.pythonhosted.org/packages/ae/e9/fb4a220ee6939db099f4cfeeae796ecb91e7584ad4d445d4ca7f994a9135/ujson-5.11.0-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1a325fd2c3a056cf6c8e023f74a0c478dd282a93141356ae7f16d5309f5ff823", size = 58547, upload-time = "2025-08-20T11:56:21.175Z" }, + { url = "https://files.pythonhosted.org/packages/bd/f8/fc4b952b8f5fea09ea3397a0bd0ad019e474b204cabcb947cead5d4d1ffc/ujson-5.11.0-cp314-cp314t-manylinux_2_24_i686.manylinux_2_28_i686.whl", hash = "sha256:a0af6574fc1d9d53f4ff371f58c96673e6d988ed2b5bf666a6143c782fa007e9", size = 60489, upload-time = "2025-08-20T11:56:22.342Z" }, + { url = "https://files.pythonhosted.org/packages/2e/e5/af5491dfda4f8b77e24cf3da68ee0d1552f99a13e5c622f4cef1380925c3/ujson-5.11.0-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:10f29e71ecf4ecd93a6610bd8efa8e7b6467454a363c3d6416db65de883eb076", size = 58035, upload-time = "2025-08-20T11:56:23.92Z" }, + { url = "https://files.pythonhosted.org/packages/c4/09/0945349dd41f25cc8c38d78ace49f14c5052c5bbb7257d2f466fa7bdb533/ujson-5.11.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:1a0a9b76a89827a592656fe12e000cf4f12da9692f51a841a4a07aa4c7ecc41c", size = 1037212, upload-time = "2025-08-20T11:56:25.274Z" }, + { url = "https://files.pythonhosted.org/packages/49/44/8e04496acb3d5a1cbee3a54828d9652f67a37523efa3d3b18a347339680a/ujson-5.11.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:b16930f6a0753cdc7d637b33b4e8f10d5e351e1fb83872ba6375f1e87be39746", size = 1196500, upload-time = "2025-08-20T11:56:27.517Z" }, + { url = "https://files.pythonhosted.org/packages/64/ae/4bc825860d679a0f208a19af2f39206dfd804ace2403330fdc3170334a2f/ujson-5.11.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:04c41afc195fd477a59db3a84d5b83a871bd648ef371cf8c6f43072d89144eef", size = 1089487, upload-time = "2025-08-20T11:56:29.07Z" }, + { url = "https://files.pythonhosted.org/packages/30/ed/5a057199fb0a5deabe0957073a1c1c1c02a3e99476cd03daee98ea21fa57/ujson-5.11.0-cp314-cp314t-win32.whl", hash = "sha256:aa6d7a5e09217ff93234e050e3e380da62b084e26b9f2e277d2606406a2fc2e5", size = 41859, upload-time = "2025-08-20T11:56:30.495Z" }, + { url = "https://files.pythonhosted.org/packages/aa/03/b19c6176bdf1dc13ed84b886e99677a52764861b6cc023d5e7b6ebda249d/ujson-5.11.0-cp314-cp314t-win_amd64.whl", hash = "sha256:48055e1061c1bb1f79e75b4ac39e821f3f35a9b82de17fce92c3140149009bec", size = 46183, upload-time = "2025-08-20T11:56:31.574Z" }, + { url = "https://files.pythonhosted.org/packages/5d/ca/a0413a3874b2dc1708b8796ca895bf363292f9c70b2e8ca482b7dbc0259d/ujson-5.11.0-cp314-cp314t-win_arm64.whl", hash = "sha256:1194b943e951092db611011cb8dbdb6cf94a3b816ed07906e14d3bc6ce0e90ab", size = 40264, upload-time = "2025-08-20T11:56:32.773Z" }, +] + [[package]] name = "urllib3" version = "2.5.0" @@ -758,3 +1122,33 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/1b/6c/c65773d6cab416a64d191d6ee8a8b1c68a09970ea6909d16965d26bfed1e/websockets-15.0.1-cp313-cp313-win_amd64.whl", hash = "sha256:e09473f095a819042ecb2ab9465aee615bd9c2028e4ef7d933600a8401c79561", size = 176837, upload-time = "2025-03-05T20:02:55.237Z" }, { url = "https://files.pythonhosted.org/packages/fa/a8/5b41e0da817d64113292ab1f8247140aac61cbf6cfd085d6a0fa77f4984f/websockets-15.0.1-py3-none-any.whl", hash = "sha256:f7a866fbc1e97b5c617ee4116daaa09b722101d4a3c170c787450ba409f9736f", size = 169743, upload-time = "2025-03-05T20:03:39.41Z" }, ] + +[[package]] +name = "whatthepatch" +version = "1.0.7" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/06/28/55bc3e107a56fdcf7d5022cb32b8c21d98a9cc2df5cd9f3b93e10419099e/whatthepatch-1.0.7.tar.gz", hash = "sha256:9eefb4ebea5200408e02d413d2b4bc28daea6b78bb4b4d53431af7245f7d7edf", size = 34612, upload-time = "2024-11-16T17:21:22.153Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8e/93/af1d6ccb69ab6b5a00e03fa0cefa563f9862412667776ea15dd4eece3a90/whatthepatch-1.0.7-py3-none-any.whl", hash = "sha256:1b6f655fd31091c001c209529dfaabbabdbad438f5de14e3951266ea0fc6e7ed", size = 11964, upload-time = "2024-11-16T17:21:20.761Z" }, +] + +[[package]] +name = "yapf" +version = "0.43.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "platformdirs" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/23/97/b6f296d1e9cc1ec25c7604178b48532fa5901f721bcf1b8d8148b13e5588/yapf-0.43.0.tar.gz", hash = "sha256:00d3aa24bfedff9420b2e0d5d9f5ab6d9d4268e72afbf59bb3fa542781d5218e", size = 254907, upload-time = "2024-11-14T00:11:41.584Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/37/81/6acd6601f61e31cfb8729d3da6d5df966f80f374b78eff83760714487338/yapf-0.43.0-py3-none-any.whl", hash = "sha256:224faffbc39c428cb095818cf6ef5511fdab6f7430a10783fdfb292ccf2852ca", size = 256158, upload-time = "2024-11-14T00:11:39.37Z" }, +] + +[[package]] +name = "zipp" +version = "3.23.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e3/02/0f2892c661036d50ede074e376733dca2ae7c6eb617489437771209d4180/zipp-3.23.0.tar.gz", hash = "sha256:a07157588a12518c9d4034df3fbbee09c814741a33ff63c05fa29d26a2404166", size = 25547, upload-time = "2025-06-08T17:06:39.4Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2e/54/647ade08bf0db230bfea292f893923872fd20be6ac6f53b2b936ba839d75/zipp-3.23.0-py3-none-any.whl", hash = "sha256:071652d6115ed432f5ce1d34c336c0adfd6a884660d1e9712a256d3d3bd4b14e", size = 10276, upload-time = "2025-06-08T17:06:38.034Z" }, +] diff --git a/assets/styles/app.css b/assets/styles/app.css index e69de29..f1d8c73 100644 --- a/assets/styles/app.css +++ b/assets/styles/app.css @@ -0,0 +1 @@ +@import "tailwindcss"; diff --git a/assets/widgets/.idea/workspace.xml b/assets/widgets/.idea/workspace.xml new file mode 100644 index 0000000..bfaebf0 --- /dev/null +++ b/assets/widgets/.idea/workspace.xml @@ -0,0 +1,97 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + { + "associatedIndex": 6 +} + + + + + + + { + "keyToString": { + "ModuleVcsDetector.initialDetectionPerformed": "true", + "RunOnceActivity.ShowReadmeOnStart": "true", + "RunOnceActivity.git.unshallow": "true", + "git-widget-placeholder": "feature/svelte", + "node.js.detected.package.eslint": "true", + "node.js.selected.package.eslint": "(autodetect)", + "nodejs_package_manager_path": "npm", + "ts.external.directory.path": "/Users/rimi/ITK/github/itk-dev/rpa-process-overview/assets/widgets/node_modules/typescript/lib", + "vue.rearranger.settings.migration": "true" + } +} + + + + + + + + + + + + + + + 1758104430353 + + + + + + \ No newline at end of file diff --git a/composer.json b/composer.json index 73fde18..3253a13 100644 --- a/composer.json +++ b/composer.json @@ -11,29 +11,36 @@ "doctrine/doctrine-migrations-bundle": "^3.4.2", "doctrine/orm": "^3.5.2", "easycorp/easyadmin-bundle": "^4.25.1", + "itk-dev/openid-connect-bundle": "^4.0", + "league/uri-components": "^7.5.1", "runtime/frankenphp-symfony": "^0.2.0", + "stof/doctrine-extensions-bundle": "^1.14", "symfony/asset": "~7.3.0", - "symfony/asset-mapper": "~7.3.0", - "symfony/console": "~7.3.0", - "symfony/dotenv": "~7.3.0", + "symfony/asset-mapper": "~7.3.4", + "symfony/console": "~7.3.4", + "symfony/dotenv": "~7.3.2", "symfony/flex": "^2.8.2", - "symfony/framework-bundle": "~7.3.0", + "symfony/framework-bundle": "~7.3.4", "symfony/mercure-bundle": "^0.3.9", - "symfony/runtime": "~7.3.0", - "symfony/translation": "~7.3.0", - "symfony/twig-bundle": "~7.3.0", - "symfony/yaml": "~7.3.0", - "twig/extra-bundle": "^2.12 || ^3.0", - "twig/twig": "^2.12 || ^3.0" + "symfony/runtime": "~7.3.4", + "symfony/security-bundle": "~7.3.0", + "symfony/translation": "~7.3.4", + "symfony/twig-bundle": "~7.3.4", + "symfony/yaml": "~7.3.3", + "symfonycasts/tailwind-bundle": "^0.11.1", + "twig/extra-bundle": "^2.12 || ^3.21", + "twig/twig": "^2.12 || ^3.21.1" }, "require-dev": { - "ergebnis/composer-normalize": "^2.48", - "friendsofphp/php-cs-fixer": "^3.87", + "ergebnis/composer-normalize": "^2.48.2", + "friendsofphp/php-cs-fixer": "^3.88.2", "hautelook/alice-bundle": "^2.15.1", + "nelmio/cors-bundle": "^2.5", + "symfony/debug-bundle": "~7.3.4", "symfony/maker-bundle": "^1.64", "symfony/stopwatch": "~7.3.0", - "symfony/web-profiler-bundle": "~7.3.0", - "vincentlanglet/twig-cs-fixer": "^3.9" + "symfony/web-profiler-bundle": "~7.3.4", + "vincentlanglet/twig-cs-fixer": "^3.10" }, "replace": { "symfony/polyfill-ctype": "*", diff --git a/composer.lock b/composer.lock index c5f36d8..7f1d885 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "a35b8458c58ddb03095a05c8043d1fcd", + "content-hash": "9ab9315fdca2ad1a44b0e4087c7b94f1", "packages": [ { "name": "composer/semver", @@ -1301,78 +1301,1045 @@ ], "time": "2025-09-10T05:00:12+00:00" }, + { + "name": "firebase/php-jwt", + "version": "v6.11.1", + "source": { + "type": "git", + "url": "https://github.com/firebase/php-jwt.git", + "reference": "d1e91ecf8c598d073d0995afa8cd5c75c6e19e66" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/firebase/php-jwt/zipball/d1e91ecf8c598d073d0995afa8cd5c75c6e19e66", + "reference": "d1e91ecf8c598d073d0995afa8cd5c75c6e19e66", + "shasum": "" + }, + "require": { + "php": "^8.0" + }, + "require-dev": { + "guzzlehttp/guzzle": "^7.4", + "phpspec/prophecy-phpunit": "^2.0", + "phpunit/phpunit": "^9.5", + "psr/cache": "^2.0||^3.0", + "psr/http-client": "^1.0", + "psr/http-factory": "^1.0" + }, + "suggest": { + "ext-sodium": "Support EdDSA (Ed25519) signatures", + "paragonie/sodium_compat": "Support EdDSA (Ed25519) signatures when libsodium is not present" + }, + "type": "library", + "autoload": { + "psr-4": { + "Firebase\\JWT\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Neuman Vong", + "email": "neuman+pear@twilio.com", + "role": "Developer" + }, + { + "name": "Anant Narayanan", + "email": "anant@php.net", + "role": "Developer" + } + ], + "description": "A simple library to encode and decode JSON Web Tokens (JWT) in PHP. Should conform to the current spec.", + "homepage": "https://github.com/firebase/php-jwt", + "keywords": [ + "jwt", + "php" + ], + "support": { + "issues": "https://github.com/firebase/php-jwt/issues", + "source": "https://github.com/firebase/php-jwt/tree/v6.11.1" + }, + "time": "2025-04-09T20:32:01+00:00" + }, + { + "name": "gedmo/doctrine-extensions", + "version": "v3.21.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine-extensions/DoctrineExtensions.git", + "reference": "eb53dfcb2b592327b76ac5226fbb003d32aea37e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine-extensions/DoctrineExtensions/zipball/eb53dfcb2b592327b76ac5226fbb003d32aea37e", + "reference": "eb53dfcb2b592327b76ac5226fbb003d32aea37e", + "shasum": "" + }, + "require": { + "doctrine/collections": "^1.2 || ^2.0", + "doctrine/deprecations": "^1.0", + "doctrine/event-manager": "^1.2 || ^2.0", + "doctrine/persistence": "^2.2 || ^3.0 || ^4.0", + "php": "^7.4 || ^8.0", + "psr/cache": "^1 || ^2 || ^3", + "psr/clock": "^1", + "symfony/cache": "^5.4 || ^6.0 || ^7.0", + "symfony/string": "^5.4 || ^6.0 || ^7.0" + }, + "conflict": { + "behat/transliterator": "<1.2 || >=2.0", + "doctrine/annotations": "<1.13 || >=3.0", + "doctrine/common": "<2.13 || >=4.0", + "doctrine/dbal": "<3.7 || >=5.0", + "doctrine/mongodb-odm": "<2.3 || >=3.0", + "doctrine/orm": "<2.20 || >=3.0 <3.3 || >=4.0" + }, + "require-dev": { + "behat/transliterator": "^1.2", + "doctrine/annotations": "^1.13 || ^2.0", + "doctrine/cache": "^1.11 || ^2.0", + "doctrine/common": "^2.13 || ^3.0", + "doctrine/dbal": "^3.7 || ^4.0", + "doctrine/doctrine-bundle": "^2.3", + "doctrine/mongodb-odm": "^2.3", + "doctrine/orm": "^2.20 || ^3.3", + "friendsofphp/php-cs-fixer": "^3.70", + "nesbot/carbon": "^2.71 || ^3.0", + "phpstan/phpstan": "^2.1.1", + "phpstan/phpstan-doctrine": "^2.0.1", + "phpstan/phpstan-phpunit": "^2.0.3", + "phpunit/phpunit": "^9.6", + "rector/rector": "^2.0.6", + "symfony/console": "^5.4 || ^6.0 || ^7.0", + "symfony/doctrine-bridge": "^5.4 || ^6.0 || ^7.0", + "symfony/phpunit-bridge": "^6.4 || ^7.0", + "symfony/uid": "^5.4 || ^6.0 || ^7.0", + "symfony/yaml": "^5.4 || ^6.0 || ^7.0" + }, + "suggest": { + "doctrine/mongodb-odm": "to use the extensions with the MongoDB ODM", + "doctrine/orm": "to use the extensions with the ORM" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Gedmo\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Gediminas Morkevicius", + "email": "gediminas.morkevicius@gmail.com" + }, + { + "name": "Gustavo Falco", + "email": "comfortablynumb84@gmail.com" + }, + { + "name": "David Buchmann", + "email": "david@liip.ch" + } + ], + "description": "Doctrine behavioral extensions", + "homepage": "http://gediminasm.org/", + "keywords": [ + "Blameable", + "behaviors", + "doctrine", + "extensions", + "gedmo", + "loggable", + "nestedset", + "odm", + "orm", + "sluggable", + "sortable", + "timestampable", + "translatable", + "tree", + "uploadable" + ], + "support": { + "docs": "https://github.com/doctrine-extensions/DoctrineExtensions/tree/main/doc", + "issues": "https://github.com/doctrine-extensions/DoctrineExtensions/issues", + "source": "https://github.com/doctrine-extensions/DoctrineExtensions/tree/v3.21.0" + }, + "funding": [ + { + "url": "https://github.com/l3pp4rd", + "type": "github" + }, + { + "url": "https://github.com/mbabker", + "type": "github" + }, + { + "url": "https://github.com/phansys", + "type": "github" + }, + { + "url": "https://github.com/stof", + "type": "github" + } + ], + "time": "2025-09-22T17:04:34+00:00" + }, + { + "name": "guzzlehttp/guzzle", + "version": "7.10.0", + "source": { + "type": "git", + "url": "https://github.com/guzzle/guzzle.git", + "reference": "b51ac707cfa420b7bfd4e4d5e510ba8008e822b4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/b51ac707cfa420b7bfd4e4d5e510ba8008e822b4", + "reference": "b51ac707cfa420b7bfd4e4d5e510ba8008e822b4", + "shasum": "" + }, + "require": { + "ext-json": "*", + "guzzlehttp/promises": "^2.3", + "guzzlehttp/psr7": "^2.8", + "php": "^7.2.5 || ^8.0", + "psr/http-client": "^1.0", + "symfony/deprecation-contracts": "^2.2 || ^3.0" + }, + "provide": { + "psr/http-client-implementation": "1.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.2", + "ext-curl": "*", + "guzzle/client-integration-tests": "3.0.2", + "php-http/message-factory": "^1.1", + "phpunit/phpunit": "^8.5.39 || ^9.6.20", + "psr/log": "^1.1 || ^2.0 || ^3.0" + }, + "suggest": { + "ext-curl": "Required for CURL handler support", + "ext-intl": "Required for Internationalized Domain Name (IDN) support", + "psr/log": "Required for using the Log middleware" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + } + }, + "autoload": { + "files": [ + "src/functions_include.php" + ], + "psr-4": { + "GuzzleHttp\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "Jeremy Lindblom", + "email": "jeremeamia@gmail.com", + "homepage": "https://github.com/jeremeamia" + }, + { + "name": "George Mponos", + "email": "gmponos@gmail.com", + "homepage": "https://github.com/gmponos" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://github.com/sagikazarmark" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" + } + ], + "description": "Guzzle is a PHP HTTP client library", + "keywords": [ + "client", + "curl", + "framework", + "http", + "http client", + "psr-18", + "psr-7", + "rest", + "web service" + ], + "support": { + "issues": "https://github.com/guzzle/guzzle/issues", + "source": "https://github.com/guzzle/guzzle/tree/7.10.0" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/guzzle", + "type": "tidelift" + } + ], + "time": "2025-08-23T22:36:01+00:00" + }, + { + "name": "guzzlehttp/promises", + "version": "2.3.0", + "source": { + "type": "git", + "url": "https://github.com/guzzle/promises.git", + "reference": "481557b130ef3790cf82b713667b43030dc9c957" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/promises/zipball/481557b130ef3790cf82b713667b43030dc9c957", + "reference": "481557b130ef3790cf82b713667b43030dc9c957", + "shasum": "" + }, + "require": { + "php": "^7.2.5 || ^8.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.2", + "phpunit/phpunit": "^8.5.44 || ^9.6.25" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\Promise\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" + } + ], + "description": "Guzzle promises library", + "keywords": [ + "promise" + ], + "support": { + "issues": "https://github.com/guzzle/promises/issues", + "source": "https://github.com/guzzle/promises/tree/2.3.0" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/promises", + "type": "tidelift" + } + ], + "time": "2025-08-22T14:34:08+00:00" + }, + { + "name": "guzzlehttp/psr7", + "version": "2.8.0", + "source": { + "type": "git", + "url": "https://github.com/guzzle/psr7.git", + "reference": "21dc724a0583619cd1652f673303492272778051" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/21dc724a0583619cd1652f673303492272778051", + "reference": "21dc724a0583619cd1652f673303492272778051", + "shasum": "" + }, + "require": { + "php": "^7.2.5 || ^8.0", + "psr/http-factory": "^1.0", + "psr/http-message": "^1.1 || ^2.0", + "ralouphie/getallheaders": "^3.0" + }, + "provide": { + "psr/http-factory-implementation": "1.0", + "psr/http-message-implementation": "1.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.2", + "http-interop/http-factory-tests": "0.9.0", + "phpunit/phpunit": "^8.5.44 || ^9.6.25" + }, + "suggest": { + "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\Psr7\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "George Mponos", + "email": "gmponos@gmail.com", + "homepage": "https://github.com/gmponos" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://github.com/sagikazarmark" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://sagikazarmark.hu" + } + ], + "description": "PSR-7 message implementation that also provides common utility methods", + "keywords": [ + "http", + "message", + "psr-7", + "request", + "response", + "stream", + "uri", + "url" + ], + "support": { + "issues": "https://github.com/guzzle/psr7/issues", + "source": "https://github.com/guzzle/psr7/tree/2.8.0" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/psr7", + "type": "tidelift" + } + ], + "time": "2025-08-23T21:21:41+00:00" + }, + { + "name": "itk-dev/openid-connect", + "version": "4.0.1", + "source": { + "type": "git", + "url": "https://github.com/itk-dev/openid-connect.git", + "reference": "8f3b8c0cc4abc7e91c1ee0f3e978deee6806da6d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/itk-dev/openid-connect/zipball/8f3b8c0cc4abc7e91c1ee0f3e978deee6806da6d", + "reference": "8f3b8c0cc4abc7e91c1ee0f3e978deee6806da6d", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-openssl": "*", + "firebase/php-jwt": "^6.8", + "league/oauth2-client": "^2.6", + "php": "^8.3", + "psr/cache": "^2.0 || ^3.0", + "psr/http-client": "^1.0", + "robrichards/xmlseclibs": "^3.1" + }, + "require-dev": { + "dealerdirect/phpcodesniffer-composer-installer": "^1.0", + "escapestudios/symfony2-coding-standard": "^3.12", + "mockery/mockery": "^1.4", + "phpstan/phpstan": "^2.1", + "phpunit/php-code-coverage": "^11.0", + "phpunit/phpunit": "^11.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "ItkDev\\OpenIdConnect\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jeppe Kuhlmann Andersen", + "email": "jekua@aarhus.dk" + }, + { + "name": "Ture Gjørup", + "email": "tug@aarhus.dk" + }, + { + "name": "Lars Steen Risom", + "email": "lats@aarhus.dk" + } + ], + "description": "OpenID connect configuration package", + "support": { + "issues": "https://github.com/itk-dev/openid-connect/issues", + "source": "https://github.com/itk-dev/openid-connect/tree/4.0.1" + }, + "time": "2025-01-13T09:01:13+00:00" + }, + { + "name": "itk-dev/openid-connect-bundle", + "version": "4.0.1", + "source": { + "type": "git", + "url": "https://github.com/itk-dev/openid-connect-bundle.git", + "reference": "be2dff91f114ba26926b5d59e9db3632cf809e7d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/itk-dev/openid-connect-bundle/zipball/be2dff91f114ba26926b5d59e9db3632cf809e7d", + "reference": "be2dff91f114ba26926b5d59e9db3632cf809e7d", + "shasum": "" + }, + "require": { + "doctrine/orm": "^2.8|^3.0", + "ext-json": "*", + "ext-openssl": "*", + "itk-dev/openid-connect": "^4.0", + "php": "^8.3", + "symfony/cache": "^6.4|^7.0", + "symfony/framework-bundle": "^6.4.13|^7.0", + "symfony/security-bundle": "^6.4.13|^7.0", + "symfony/uid": "^6.4|^7.0", + "symfony/yaml": "^6.4|^7.0" + }, + "require-dev": { + "ergebnis/composer-normalize": "^2.28", + "escapestudios/symfony2-coding-standard": "^3.12", + "friendsofphp/php-cs-fixer": "^3.11", + "kubawerlos/php-cs-fixer-custom-fixers": "^3.11", + "phpstan/phpstan": "^2.1", + "phpunit/phpunit": "^11.0", + "rector/rector": "^2.0", + "symfony/runtime": "^6.4.13|^7.0" + }, + "type": "symfony-bundle", + "autoload": { + "psr-4": { + "ItkDev\\OpenIdConnectBundle\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jeppe Kuhlmann Andersen", + "email": "jekua@aarhus.dk" + }, + { + "name": "Ture Gjørup", + "email": "tug@aarhus.dk" + } + ], + "description": "Symfony bundle for openid-connect", + "support": { + "issues": "https://github.com/itk-dev/openid-connect-bundle/issues", + "source": "https://github.com/itk-dev/openid-connect-bundle/tree/4.0.1" + }, + "time": "2025-01-16T21:14:01+00:00" + }, { "name": "lcobucci/jwt", "version": "5.5.0", "source": { "type": "git", - "url": "https://github.com/lcobucci/jwt.git", - "reference": "a835af59b030d3f2967725697cf88300f579088e" + "url": "https://github.com/lcobucci/jwt.git", + "reference": "a835af59b030d3f2967725697cf88300f579088e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/lcobucci/jwt/zipball/a835af59b030d3f2967725697cf88300f579088e", + "reference": "a835af59b030d3f2967725697cf88300f579088e", + "shasum": "" + }, + "require": { + "ext-openssl": "*", + "ext-sodium": "*", + "php": "~8.2.0 || ~8.3.0 || ~8.4.0", + "psr/clock": "^1.0" + }, + "require-dev": { + "infection/infection": "^0.29", + "lcobucci/clock": "^3.2", + "lcobucci/coding-standard": "^11.0", + "phpbench/phpbench": "^1.2", + "phpstan/extension-installer": "^1.2", + "phpstan/phpstan": "^1.10.7", + "phpstan/phpstan-deprecation-rules": "^1.1.3", + "phpstan/phpstan-phpunit": "^1.3.10", + "phpstan/phpstan-strict-rules": "^1.5.0", + "phpunit/phpunit": "^11.1" + }, + "suggest": { + "lcobucci/clock": ">= 3.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "Lcobucci\\JWT\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Luís Cobucci", + "email": "lcobucci@gmail.com", + "role": "Developer" + } + ], + "description": "A simple library to work with JSON Web Token and JSON Web Signature", + "keywords": [ + "JWS", + "jwt" + ], + "support": { + "issues": "https://github.com/lcobucci/jwt/issues", + "source": "https://github.com/lcobucci/jwt/tree/5.5.0" + }, + "funding": [ + { + "url": "https://github.com/lcobucci", + "type": "github" + }, + { + "url": "https://www.patreon.com/lcobucci", + "type": "patreon" + } + ], + "time": "2025-01-26T21:29:45+00:00" + }, + { + "name": "league/oauth2-client", + "version": "2.8.1", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/oauth2-client.git", + "reference": "9df2924ca644736c835fc60466a3a60390d334f9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/oauth2-client/zipball/9df2924ca644736c835fc60466a3a60390d334f9", + "reference": "9df2924ca644736c835fc60466a3a60390d334f9", + "shasum": "" + }, + "require": { + "ext-json": "*", + "guzzlehttp/guzzle": "^6.5.8 || ^7.4.5", + "php": "^7.1 || >=8.0.0 <8.5.0" + }, + "require-dev": { + "mockery/mockery": "^1.3.5", + "php-parallel-lint/php-parallel-lint": "^1.4", + "phpunit/phpunit": "^7 || ^8 || ^9 || ^10 || ^11", + "squizlabs/php_codesniffer": "^3.11" + }, + "type": "library", + "autoload": { + "psr-4": { + "League\\OAuth2\\Client\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Alex Bilbie", + "email": "hello@alexbilbie.com", + "homepage": "http://www.alexbilbie.com", + "role": "Developer" + }, + { + "name": "Woody Gilk", + "homepage": "https://github.com/shadowhand", + "role": "Contributor" + } + ], + "description": "OAuth 2.0 Client Library", + "keywords": [ + "Authentication", + "SSO", + "authorization", + "identity", + "idp", + "oauth", + "oauth2", + "single sign on" + ], + "support": { + "issues": "https://github.com/thephpleague/oauth2-client/issues", + "source": "https://github.com/thephpleague/oauth2-client/tree/2.8.1" + }, + "time": "2025-02-26T04:37:30+00:00" + }, + { + "name": "league/uri", + "version": "7.5.1", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/uri.git", + "reference": "81fb5145d2644324614cc532b28efd0215bda430" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/uri/zipball/81fb5145d2644324614cc532b28efd0215bda430", + "reference": "81fb5145d2644324614cc532b28efd0215bda430", + "shasum": "" + }, + "require": { + "league/uri-interfaces": "^7.5", + "php": "^8.1" + }, + "conflict": { + "league/uri-schemes": "^1.0" + }, + "suggest": { + "ext-bcmath": "to improve IPV4 host parsing", + "ext-fileinfo": "to create Data URI from file contennts", + "ext-gmp": "to improve IPV4 host parsing", + "ext-intl": "to handle IDN host with the best performance", + "jeremykendall/php-domain-parser": "to resolve Public Suffix and Top Level Domain", + "league/uri-components": "Needed to easily manipulate URI objects components", + "php-64bit": "to improve IPV4 host parsing", + "symfony/polyfill-intl-idn": "to handle IDN host via the Symfony polyfill if ext-intl is not present" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "7.x-dev" + } + }, + "autoload": { + "psr-4": { + "League\\Uri\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ignace Nyamagana Butera", + "email": "nyamsprod@gmail.com", + "homepage": "https://nyamsprod.com" + } + ], + "description": "URI manipulation library", + "homepage": "https://uri.thephpleague.com", + "keywords": [ + "data-uri", + "file-uri", + "ftp", + "hostname", + "http", + "https", + "middleware", + "parse_str", + "parse_url", + "psr-7", + "query-string", + "querystring", + "rfc3986", + "rfc3987", + "rfc6570", + "uri", + "uri-template", + "url", + "ws" + ], + "support": { + "docs": "https://uri.thephpleague.com", + "forum": "https://thephpleague.slack.com", + "issues": "https://github.com/thephpleague/uri-src/issues", + "source": "https://github.com/thephpleague/uri/tree/7.5.1" + }, + "funding": [ + { + "url": "https://github.com/sponsors/nyamsprod", + "type": "github" + } + ], + "time": "2024-12-08T08:40:02+00:00" + }, + { + "name": "league/uri-components", + "version": "7.5.1", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/uri-components.git", + "reference": "4aabf0e2f2f9421ffcacab35be33e4fb5e63c44f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/lcobucci/jwt/zipball/a835af59b030d3f2967725697cf88300f579088e", - "reference": "a835af59b030d3f2967725697cf88300f579088e", + "url": "https://api.github.com/repos/thephpleague/uri-components/zipball/4aabf0e2f2f9421ffcacab35be33e4fb5e63c44f", + "reference": "4aabf0e2f2f9421ffcacab35be33e4fb5e63c44f", "shasum": "" }, "require": { - "ext-openssl": "*", - "ext-sodium": "*", - "php": "~8.2.0 || ~8.3.0 || ~8.4.0", - "psr/clock": "^1.0" - }, - "require-dev": { - "infection/infection": "^0.29", - "lcobucci/clock": "^3.2", - "lcobucci/coding-standard": "^11.0", - "phpbench/phpbench": "^1.2", - "phpstan/extension-installer": "^1.2", - "phpstan/phpstan": "^1.10.7", - "phpstan/phpstan-deprecation-rules": "^1.1.3", - "phpstan/phpstan-phpunit": "^1.3.10", - "phpstan/phpstan-strict-rules": "^1.5.0", - "phpunit/phpunit": "^11.1" + "league/uri": "^7.5", + "php": "^8.1" }, "suggest": { - "lcobucci/clock": ">= 3.2" + "ext-bcmath": "to improve IPV4 host parsing", + "ext-fileinfo": "to create Data URI from file contennts", + "ext-gmp": "to improve IPV4 host parsing", + "ext-intl": "to handle IDN host with the best performance", + "ext-mbstring": "to use the sorting algorithm of URLSearchParams", + "jeremykendall/php-domain-parser": "to resolve Public Suffix and Top Level Domain", + "php-64bit": "to improve IPV4 host parsing", + "symfony/polyfill-intl-idn": "to handle IDN host via the Symfony polyfill if ext-intl is not present" }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "7.x-dev" + } + }, "autoload": { "psr-4": { - "Lcobucci\\JWT\\": "src" + "League\\Uri\\": "" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Luís Cobucci", - "email": "lcobucci@gmail.com", - "role": "Developer" + "name": "Ignace Nyamagana Butera", + "email": "nyamsprod@gmail.com", + "homepage": "https://nyamsprod.com" } ], - "description": "A simple library to work with JSON Web Token and JSON Web Signature", + "description": "URI components manipulation library", + "homepage": "http://uri.thephpleague.com", "keywords": [ - "JWS", - "jwt" + "authority", + "components", + "fragment", + "host", + "middleware", + "modifier", + "path", + "port", + "query", + "rfc3986", + "scheme", + "uri", + "url", + "userinfo" ], "support": { - "issues": "https://github.com/lcobucci/jwt/issues", - "source": "https://github.com/lcobucci/jwt/tree/5.5.0" + "docs": "https://uri.thephpleague.com", + "forum": "https://thephpleague.slack.com", + "issues": "https://github.com/thephpleague/uri-src/issues", + "source": "https://github.com/thephpleague/uri-components/tree/7.5.1" }, "funding": [ { - "url": "https://github.com/lcobucci", + "url": "https://github.com/nyamsprod", "type": "github" - }, + } + ], + "time": "2024-12-08T08:40:02+00:00" + }, + { + "name": "league/uri-interfaces", + "version": "7.5.0", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/uri-interfaces.git", + "reference": "08cfc6c4f3d811584fb09c37e2849e6a7f9b0742" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/uri-interfaces/zipball/08cfc6c4f3d811584fb09c37e2849e6a7f9b0742", + "reference": "08cfc6c4f3d811584fb09c37e2849e6a7f9b0742", + "shasum": "" + }, + "require": { + "ext-filter": "*", + "php": "^8.1", + "psr/http-factory": "^1", + "psr/http-message": "^1.1 || ^2.0" + }, + "suggest": { + "ext-bcmath": "to improve IPV4 host parsing", + "ext-gmp": "to improve IPV4 host parsing", + "ext-intl": "to handle IDN host with the best performance", + "php-64bit": "to improve IPV4 host parsing", + "symfony/polyfill-intl-idn": "to handle IDN host via the Symfony polyfill if ext-intl is not present" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "7.x-dev" + } + }, + "autoload": { + "psr-4": { + "League\\Uri\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ { - "url": "https://www.patreon.com/lcobucci", - "type": "patreon" + "name": "Ignace Nyamagana Butera", + "email": "nyamsprod@gmail.com", + "homepage": "https://nyamsprod.com" } ], - "time": "2025-01-26T21:29:45+00:00" + "description": "Common interfaces and classes for URI representation and interaction", + "homepage": "https://uri.thephpleague.com", + "keywords": [ + "data-uri", + "file-uri", + "ftp", + "hostname", + "http", + "https", + "parse_str", + "parse_url", + "psr-7", + "query-string", + "querystring", + "rfc3986", + "rfc3987", + "rfc6570", + "uri", + "url", + "ws" + ], + "support": { + "docs": "https://uri.thephpleague.com", + "forum": "https://thephpleague.slack.com", + "issues": "https://github.com/thephpleague/uri-src/issues", + "source": "https://github.com/thephpleague/uri-interfaces/tree/7.5.0" + }, + "funding": [ + { + "url": "https://github.com/sponsors/nyamsprod", + "type": "github" + } + ], + "time": "2024-12-08T08:18:47+00:00" }, { "name": "psr/cache", @@ -1414,36 +2381,193 @@ ], "description": "Common interface for caching libraries", "keywords": [ - "cache", + "cache", + "psr", + "psr-6" + ], + "support": { + "source": "https://github.com/php-fig/cache/tree/3.0.0" + }, + "time": "2021-02-03T23:26:27+00:00" + }, + { + "name": "psr/clock", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/clock.git", + "reference": "e41a24703d4560fd0acb709162f73b8adfc3aa0d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/clock/zipball/e41a24703d4560fd0acb709162f73b8adfc3aa0d", + "reference": "e41a24703d4560fd0acb709162f73b8adfc3aa0d", + "shasum": "" + }, + "require": { + "php": "^7.0 || ^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Psr\\Clock\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for reading the clock.", + "homepage": "https://github.com/php-fig/clock", + "keywords": [ + "clock", + "now", + "psr", + "psr-20", + "time" + ], + "support": { + "issues": "https://github.com/php-fig/clock/issues", + "source": "https://github.com/php-fig/clock/tree/1.0.0" + }, + "time": "2022-11-25T14:36:26+00:00" + }, + { + "name": "psr/container", + "version": "2.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/container.git", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "shasum": "" + }, + "require": { + "php": ">=7.4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Container\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common Container Interface (PHP FIG PSR-11)", + "homepage": "https://github.com/php-fig/container", + "keywords": [ + "PSR-11", + "container", + "container-interface", + "container-interop", + "psr" + ], + "support": { + "issues": "https://github.com/php-fig/container/issues", + "source": "https://github.com/php-fig/container/tree/2.0.2" + }, + "time": "2021-11-05T16:47:00+00:00" + }, + { + "name": "psr/event-dispatcher", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/event-dispatcher.git", + "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/event-dispatcher/zipball/dbefd12671e8a14ec7f180cab83036ed26714bb0", + "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0", + "shasum": "" + }, + "require": { + "php": ">=7.2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\EventDispatcher\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Standard interfaces for event handling.", + "keywords": [ + "events", "psr", - "psr-6" + "psr-14" ], "support": { - "source": "https://github.com/php-fig/cache/tree/3.0.0" + "issues": "https://github.com/php-fig/event-dispatcher/issues", + "source": "https://github.com/php-fig/event-dispatcher/tree/1.0.0" }, - "time": "2021-02-03T23:26:27+00:00" + "time": "2019-01-08T18:20:26+00:00" }, { - "name": "psr/clock", - "version": "1.0.0", + "name": "psr/http-client", + "version": "1.0.3", "source": { "type": "git", - "url": "https://github.com/php-fig/clock.git", - "reference": "e41a24703d4560fd0acb709162f73b8adfc3aa0d" + "url": "https://github.com/php-fig/http-client.git", + "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/clock/zipball/e41a24703d4560fd0acb709162f73b8adfc3aa0d", - "reference": "e41a24703d4560fd0acb709162f73b8adfc3aa0d", + "url": "https://api.github.com/repos/php-fig/http-client/zipball/bb5906edc1c324c9a05aa0873d40117941e5fa90", + "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90", "shasum": "" }, "require": { - "php": "^7.0 || ^8.0" + "php": "^7.0 || ^8.0", + "psr/http-message": "^1.0 || ^2.0" }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, "autoload": { "psr-4": { - "Psr\\Clock\\": "src/" + "Psr\\Http\\Client\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -1456,47 +2580,46 @@ "homepage": "https://www.php-fig.org/" } ], - "description": "Common interface for reading the clock.", - "homepage": "https://github.com/php-fig/clock", + "description": "Common interface for HTTP clients", + "homepage": "https://github.com/php-fig/http-client", "keywords": [ - "clock", - "now", + "http", + "http-client", "psr", - "psr-20", - "time" + "psr-18" ], "support": { - "issues": "https://github.com/php-fig/clock/issues", - "source": "https://github.com/php-fig/clock/tree/1.0.0" + "source": "https://github.com/php-fig/http-client" }, - "time": "2022-11-25T14:36:26+00:00" + "time": "2023-09-23T14:17:50+00:00" }, { - "name": "psr/container", - "version": "2.0.2", + "name": "psr/http-factory", + "version": "1.1.0", "source": { "type": "git", - "url": "https://github.com/php-fig/container.git", - "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963" + "url": "https://github.com/php-fig/http-factory.git", + "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963", - "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "url": "https://api.github.com/repos/php-fig/http-factory/zipball/2b4765fddfe3b508ac62f829e852b1501d3f6e8a", + "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a", "shasum": "" }, "require": { - "php": ">=7.4.0" + "php": ">=7.1", + "psr/http-message": "^1.0 || ^2.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0.x-dev" + "dev-master": "1.0.x-dev" } }, "autoload": { "psr-4": { - "Psr\\Container\\": "src/" + "Psr\\Http\\Message\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -1509,47 +2632,48 @@ "homepage": "https://www.php-fig.org/" } ], - "description": "Common Container Interface (PHP FIG PSR-11)", - "homepage": "https://github.com/php-fig/container", + "description": "PSR-17: Common interfaces for PSR-7 HTTP message factories", "keywords": [ - "PSR-11", - "container", - "container-interface", - "container-interop", - "psr" + "factory", + "http", + "message", + "psr", + "psr-17", + "psr-7", + "request", + "response" ], "support": { - "issues": "https://github.com/php-fig/container/issues", - "source": "https://github.com/php-fig/container/tree/2.0.2" + "source": "https://github.com/php-fig/http-factory" }, - "time": "2021-11-05T16:47:00+00:00" + "time": "2024-04-15T12:06:14+00:00" }, { - "name": "psr/event-dispatcher", - "version": "1.0.0", + "name": "psr/http-message", + "version": "2.0", "source": { "type": "git", - "url": "https://github.com/php-fig/event-dispatcher.git", - "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0" + "url": "https://github.com/php-fig/http-message.git", + "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/event-dispatcher/zipball/dbefd12671e8a14ec7f180cab83036ed26714bb0", - "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0", + "url": "https://api.github.com/repos/php-fig/http-message/zipball/402d35bcb92c70c026d1a6a9883f06b2ead23d71", + "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71", "shasum": "" }, "require": { - "php": ">=7.2.0" + "php": "^7.2 || ^8.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-master": "2.0.x-dev" } }, "autoload": { "psr-4": { - "Psr\\EventDispatcher\\": "src/" + "Psr\\Http\\Message\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -1559,20 +2683,23 @@ "authors": [ { "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" + "homepage": "https://www.php-fig.org/" } ], - "description": "Standard interfaces for event handling.", + "description": "Common interface for HTTP messages", + "homepage": "https://github.com/php-fig/http-message", "keywords": [ - "events", + "http", + "http-message", "psr", - "psr-14" + "psr-7", + "request", + "response" ], "support": { - "issues": "https://github.com/php-fig/event-dispatcher/issues", - "source": "https://github.com/php-fig/event-dispatcher/tree/1.0.0" + "source": "https://github.com/php-fig/http-message/tree/2.0" }, - "time": "2019-01-08T18:20:26+00:00" + "time": "2023-04-04T09:54:51+00:00" }, { "name": "psr/link", @@ -1680,6 +2807,92 @@ }, "time": "2024-09-11T13:17:53+00:00" }, + { + "name": "ralouphie/getallheaders", + "version": "3.0.3", + "source": { + "type": "git", + "url": "https://github.com/ralouphie/getallheaders.git", + "reference": "120b605dfeb996808c31b6477290a714d356e822" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822", + "reference": "120b605dfeb996808c31b6477290a714d356e822", + "shasum": "" + }, + "require": { + "php": ">=5.6" + }, + "require-dev": { + "php-coveralls/php-coveralls": "^2.1", + "phpunit/phpunit": "^5 || ^6.5" + }, + "type": "library", + "autoload": { + "files": [ + "src/getallheaders.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ralph Khattar", + "email": "ralph.khattar@gmail.com" + } + ], + "description": "A polyfill for getallheaders.", + "support": { + "issues": "https://github.com/ralouphie/getallheaders/issues", + "source": "https://github.com/ralouphie/getallheaders/tree/develop" + }, + "time": "2019-03-08T08:55:37+00:00" + }, + { + "name": "robrichards/xmlseclibs", + "version": "3.1.3", + "source": { + "type": "git", + "url": "https://github.com/robrichards/xmlseclibs.git", + "reference": "2bdfd742624d739dfadbd415f00181b4a77aaf07" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/robrichards/xmlseclibs/zipball/2bdfd742624d739dfadbd415f00181b4a77aaf07", + "reference": "2bdfd742624d739dfadbd415f00181b4a77aaf07", + "shasum": "" + }, + "require": { + "ext-openssl": "*", + "php": ">= 5.4" + }, + "type": "library", + "autoload": { + "psr-4": { + "RobRichards\\XMLSecLibs\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "A PHP library for XML Security", + "homepage": "https://github.com/robrichards/xmlseclibs", + "keywords": [ + "security", + "signature", + "xml", + "xmldsig" + ], + "support": { + "issues": "https://github.com/robrichards/xmlseclibs/issues", + "source": "https://github.com/robrichards/xmlseclibs/tree/3.1.3" + }, + "time": "2024-11-20T21:13:56+00:00" + }, { "name": "runtime/frankenphp-symfony", "version": "0.2.0", @@ -1732,6 +2945,87 @@ ], "time": "2023-12-12T12:06:11+00:00" }, + { + "name": "stof/doctrine-extensions-bundle", + "version": "v1.14.0", + "source": { + "type": "git", + "url": "https://github.com/stof/StofDoctrineExtensionsBundle.git", + "reference": "bdf3eb10baeb497ac5985b8f78a6cf55862c2662" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/stof/StofDoctrineExtensionsBundle/zipball/bdf3eb10baeb497ac5985b8f78a6cf55862c2662", + "reference": "bdf3eb10baeb497ac5985b8f78a6cf55862c2662", + "shasum": "" + }, + "require": { + "gedmo/doctrine-extensions": "^3.20.0", + "php": "^8.1", + "symfony/cache": "^6.4 || ^7.0", + "symfony/config": "^6.4 || ^7.0", + "symfony/dependency-injection": "^6.4 || ^7.0", + "symfony/event-dispatcher": "^6.4 || ^7.0", + "symfony/http-kernel": "^6.4 || ^7.0", + "symfony/translation-contracts": "^2.5 || ^3.5" + }, + "require-dev": { + "phpstan/phpstan": "^2.1", + "phpstan/phpstan-deprecation-rules": "^2.0", + "phpstan/phpstan-phpunit": "^2.0", + "phpstan/phpstan-strict-rules": "^2.0", + "phpstan/phpstan-symfony": "^2.0", + "symfony/mime": "^6.4 || ^7.0", + "symfony/phpunit-bridge": "^v6.4.1 || ^7.0.1", + "symfony/security-core": "^6.4 || ^7.0" + }, + "suggest": { + "doctrine/doctrine-bundle": "to use the ORM extensions", + "doctrine/mongodb-odm-bundle": "to use the MongoDB ODM extensions", + "symfony/mime": "To use the Mime component integration for Uploadable" + }, + "type": "symfony-bundle", + "extra": { + "branch-alias": { + "dev-main": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Stof\\DoctrineExtensionsBundle\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Christophe Coevoet", + "email": "stof@notk.org" + } + ], + "description": "Integration of the gedmo/doctrine-extensions with Symfony", + "homepage": "https://github.com/stof/StofDoctrineExtensionsBundle", + "keywords": [ + "behaviors", + "doctrine2", + "extensions", + "gedmo", + "loggable", + "nestedset", + "sluggable", + "sortable", + "timestampable", + "translatable", + "tree" + ], + "support": { + "issues": "https://github.com/stof/StofDoctrineExtensionsBundle/issues", + "source": "https://github.com/stof/StofDoctrineExtensionsBundle/tree/v1.14.0" + }, + "time": "2025-05-01T08:00:32+00:00" + }, { "name": "symfony/asset", "version": "v7.3.0", @@ -1803,16 +3097,16 @@ }, { "name": "symfony/asset-mapper", - "version": "v7.3.3", + "version": "v7.3.4", "source": { "type": "git", "url": "https://github.com/symfony/asset-mapper.git", - "reference": "bb8ebdfe10cce2365785b88b2d1a422f9bfedd1f" + "reference": "0c40c579e27244616cf0fe4d1759aab2ceb99a9a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/asset-mapper/zipball/bb8ebdfe10cce2365785b88b2d1a422f9bfedd1f", - "reference": "bb8ebdfe10cce2365785b88b2d1a422f9bfedd1f", + "url": "https://api.github.com/repos/symfony/asset-mapper/zipball/0c40c579e27244616cf0fe4d1759aab2ceb99a9a", + "reference": "0c40c579e27244616cf0fe4d1759aab2ceb99a9a", "shasum": "" }, "require": { @@ -1863,7 +3157,7 @@ "description": "Maps directories of assets & makes them available in a public directory with versioned filenames.", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/asset-mapper/tree/v7.3.3" + "source": "https://github.com/symfony/asset-mapper/tree/v7.3.4" }, "funding": [ { @@ -1883,20 +3177,20 @@ "type": "tidelift" } ], - "time": "2025-08-04T15:15:28+00:00" + "time": "2025-09-22T15:31:00+00:00" }, { "name": "symfony/cache", - "version": "v7.3.2", + "version": "v7.3.4", "source": { "type": "git", "url": "https://github.com/symfony/cache.git", - "reference": "6621a2bee5373e3e972b2ae5dbedd5ac899d8cb6" + "reference": "bf8afc8ffd4bfd3d9c373e417f041d9f1e5b863f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/cache/zipball/6621a2bee5373e3e972b2ae5dbedd5ac899d8cb6", - "reference": "6621a2bee5373e3e972b2ae5dbedd5ac899d8cb6", + "url": "https://api.github.com/repos/symfony/cache/zipball/bf8afc8ffd4bfd3d9c373e417f041d9f1e5b863f", + "reference": "bf8afc8ffd4bfd3d9c373e417f041d9f1e5b863f", "shasum": "" }, "require": { @@ -1965,7 +3259,7 @@ "psr6" ], "support": { - "source": "https://github.com/symfony/cache/tree/v7.3.2" + "source": "https://github.com/symfony/cache/tree/v7.3.4" }, "funding": [ { @@ -1985,7 +3279,7 @@ "type": "tidelift" } ], - "time": "2025-07-30T17:13:41+00:00" + "time": "2025-09-11T10:12:26+00:00" }, { "name": "symfony/cache-contracts", @@ -2139,16 +3433,16 @@ }, { "name": "symfony/config", - "version": "v7.3.2", + "version": "v7.3.4", "source": { "type": "git", "url": "https://github.com/symfony/config.git", - "reference": "faef36e271bbeb74a9d733be4b56419b157762e2" + "reference": "8a09223170046d2cfda3d2e11af01df2c641e961" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/config/zipball/faef36e271bbeb74a9d733be4b56419b157762e2", - "reference": "faef36e271bbeb74a9d733be4b56419b157762e2", + "url": "https://api.github.com/repos/symfony/config/zipball/8a09223170046d2cfda3d2e11af01df2c641e961", + "reference": "8a09223170046d2cfda3d2e11af01df2c641e961", "shasum": "" }, "require": { @@ -2194,7 +3488,7 @@ "description": "Helps you find, load, combine, autofill and validate configuration values of any kind", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/config/tree/v7.3.2" + "source": "https://github.com/symfony/config/tree/v7.3.4" }, "funding": [ { @@ -2214,20 +3508,20 @@ "type": "tidelift" } ], - "time": "2025-07-26T13:55:06+00:00" + "time": "2025-09-22T12:46:16+00:00" }, { "name": "symfony/console", - "version": "v7.3.3", + "version": "v7.3.4", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "cb0102a1c5ac3807cf3fdf8bea96007df7fdbea7" + "reference": "2b9c5fafbac0399a20a2e82429e2bd735dcfb7db" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/cb0102a1c5ac3807cf3fdf8bea96007df7fdbea7", - "reference": "cb0102a1c5ac3807cf3fdf8bea96007df7fdbea7", + "url": "https://api.github.com/repos/symfony/console/zipball/2b9c5fafbac0399a20a2e82429e2bd735dcfb7db", + "reference": "2b9c5fafbac0399a20a2e82429e2bd735dcfb7db", "shasum": "" }, "require": { @@ -2292,7 +3586,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v7.3.3" + "source": "https://github.com/symfony/console/tree/v7.3.4" }, "funding": [ { @@ -2312,20 +3606,20 @@ "type": "tidelift" } ], - "time": "2025-08-25T06:35:40+00:00" + "time": "2025-09-22T15:31:00+00:00" }, { "name": "symfony/dependency-injection", - "version": "v7.3.3", + "version": "v7.3.4", "source": { "type": "git", "url": "https://github.com/symfony/dependency-injection.git", - "reference": "ab6c38dad5da9b15b1f7afb2f5c5814112e70261" + "reference": "82119812ab0bf3425c1234d413efd1b19bb92ae4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/ab6c38dad5da9b15b1f7afb2f5c5814112e70261", - "reference": "ab6c38dad5da9b15b1f7afb2f5c5814112e70261", + "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/82119812ab0bf3425c1234d413efd1b19bb92ae4", + "reference": "82119812ab0bf3425c1234d413efd1b19bb92ae4", "shasum": "" }, "require": { @@ -2376,7 +3670,7 @@ "description": "Allows you to standardize and centralize the way objects are constructed in your application", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/dependency-injection/tree/v7.3.3" + "source": "https://github.com/symfony/dependency-injection/tree/v7.3.4" }, "funding": [ { @@ -2396,7 +3690,7 @@ "type": "tidelift" } ], - "time": "2025-08-14T09:54:27+00:00" + "time": "2025-09-11T10:12:26+00:00" }, { "name": "symfony/deprecation-contracts", @@ -2467,16 +3761,16 @@ }, { "name": "symfony/doctrine-bridge", - "version": "v7.3.3", + "version": "v7.3.4", "source": { "type": "git", "url": "https://github.com/symfony/doctrine-bridge.git", - "reference": "b371ded46da25415e1a3a7422e4acd2ec34214c5" + "reference": "21cd48c34a47a0d0e303a590a67c3450fde55888" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/doctrine-bridge/zipball/b371ded46da25415e1a3a7422e4acd2ec34214c5", - "reference": "b371ded46da25415e1a3a7422e4acd2ec34214c5", + "url": "https://api.github.com/repos/symfony/doctrine-bridge/zipball/21cd48c34a47a0d0e303a590a67c3450fde55888", + "reference": "21cd48c34a47a0d0e303a590a67c3450fde55888", "shasum": "" }, "require": { @@ -2556,7 +3850,7 @@ "description": "Provides integration for Doctrine with various Symfony components", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/doctrine-bridge/tree/v7.3.3" + "source": "https://github.com/symfony/doctrine-bridge/tree/v7.3.4" }, "funding": [ { @@ -2576,7 +3870,7 @@ "type": "tidelift" } ], - "time": "2025-08-18T13:10:53+00:00" + "time": "2025-09-24T09:56:23+00:00" }, { "name": "symfony/dotenv", @@ -2658,16 +3952,16 @@ }, { "name": "symfony/error-handler", - "version": "v7.3.2", + "version": "v7.3.4", "source": { "type": "git", "url": "https://github.com/symfony/error-handler.git", - "reference": "0b31a944fcd8759ae294da4d2808cbc53aebd0c3" + "reference": "99f81bc944ab8e5dae4f21b4ca9972698bbad0e4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/error-handler/zipball/0b31a944fcd8759ae294da4d2808cbc53aebd0c3", - "reference": "0b31a944fcd8759ae294da4d2808cbc53aebd0c3", + "url": "https://api.github.com/repos/symfony/error-handler/zipball/99f81bc944ab8e5dae4f21b4ca9972698bbad0e4", + "reference": "99f81bc944ab8e5dae4f21b4ca9972698bbad0e4", "shasum": "" }, "require": { @@ -2715,7 +4009,7 @@ "description": "Provides tools to manage errors and ease debugging PHP code", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/error-handler/tree/v7.3.2" + "source": "https://github.com/symfony/error-handler/tree/v7.3.4" }, "funding": [ { @@ -2735,7 +4029,7 @@ "type": "tidelift" } ], - "time": "2025-07-07T08:17:57+00:00" + "time": "2025-09-11T10:12:26+00:00" }, { "name": "symfony/event-dispatcher", @@ -3109,16 +4403,16 @@ }, { "name": "symfony/form", - "version": "v7.3.3", + "version": "v7.3.4", "source": { "type": "git", "url": "https://github.com/symfony/form.git", - "reference": "f151b4a027fa67769268b80111f7fdb63edb5e79" + "reference": "7b3eee0f4d4dfd1ff1be70a27474197330c61736" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/form/zipball/f151b4a027fa67769268b80111f7fdb63edb5e79", - "reference": "f151b4a027fa67769268b80111f7fdb63edb5e79", + "url": "https://api.github.com/repos/symfony/form/zipball/7b3eee0f4d4dfd1ff1be70a27474197330c61736", + "reference": "7b3eee0f4d4dfd1ff1be70a27474197330c61736", "shasum": "" }, "require": { @@ -3186,7 +4480,7 @@ "description": "Allows to easily create, process and reuse HTML forms", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/form/tree/v7.3.3" + "source": "https://github.com/symfony/form/tree/v7.3.4" }, "funding": [ { @@ -3206,20 +4500,20 @@ "type": "tidelift" } ], - "time": "2025-08-18T13:10:53+00:00" + "time": "2025-09-22T15:31:00+00:00" }, { "name": "symfony/framework-bundle", - "version": "v7.3.3", + "version": "v7.3.4", "source": { "type": "git", "url": "https://github.com/symfony/framework-bundle.git", - "reference": "19ec4ab6be90322ed190e041e2404a976ed22571" + "reference": "b13e7cec5a144c8dba6f4233a2c53c00bc29e140" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/framework-bundle/zipball/19ec4ab6be90322ed190e041e2404a976ed22571", - "reference": "19ec4ab6be90322ed190e041e2404a976ed22571", + "url": "https://api.github.com/repos/symfony/framework-bundle/zipball/b13e7cec5a144c8dba6f4233a2c53c00bc29e140", + "reference": "b13e7cec5a144c8dba6f4233a2c53c00bc29e140", "shasum": "" }, "require": { @@ -3344,7 +4638,7 @@ "description": "Provides a tight integration between Symfony components and the Symfony full-stack framework", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/framework-bundle/tree/v7.3.3" + "source": "https://github.com/symfony/framework-bundle/tree/v7.3.4" }, "funding": [ { @@ -3364,20 +4658,20 @@ "type": "tidelift" } ], - "time": "2025-08-27T07:45:05+00:00" + "time": "2025-09-17T05:51:54+00:00" }, { "name": "symfony/http-client", - "version": "v7.3.3", + "version": "v7.3.4", "source": { "type": "git", "url": "https://github.com/symfony/http-client.git", - "reference": "333b9bd7639cbdaecd25a3a48a9d2dcfaa86e019" + "reference": "4b62871a01c49457cf2a8e560af7ee8a94b87a62" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-client/zipball/333b9bd7639cbdaecd25a3a48a9d2dcfaa86e019", - "reference": "333b9bd7639cbdaecd25a3a48a9d2dcfaa86e019", + "url": "https://api.github.com/repos/symfony/http-client/zipball/4b62871a01c49457cf2a8e560af7ee8a94b87a62", + "reference": "4b62871a01c49457cf2a8e560af7ee8a94b87a62", "shasum": "" }, "require": { @@ -3444,7 +4738,7 @@ "http" ], "support": { - "source": "https://github.com/symfony/http-client/tree/v7.3.3" + "source": "https://github.com/symfony/http-client/tree/v7.3.4" }, "funding": [ { @@ -3464,7 +4758,7 @@ "type": "tidelift" } ], - "time": "2025-08-27T07:45:05+00:00" + "time": "2025-09-11T10:12:26+00:00" }, { "name": "symfony/http-client-contracts", @@ -3546,16 +4840,16 @@ }, { "name": "symfony/http-foundation", - "version": "v7.3.3", + "version": "v7.3.4", "source": { "type": "git", "url": "https://github.com/symfony/http-foundation.git", - "reference": "7475561ec27020196c49bb7c4f178d33d7d3dc00" + "reference": "c061c7c18918b1b64268771aad04b40be41dd2e6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/7475561ec27020196c49bb7c4f178d33d7d3dc00", - "reference": "7475561ec27020196c49bb7c4f178d33d7d3dc00", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/c061c7c18918b1b64268771aad04b40be41dd2e6", + "reference": "c061c7c18918b1b64268771aad04b40be41dd2e6", "shasum": "" }, "require": { @@ -3605,7 +4899,7 @@ "description": "Defines an object-oriented layer for the HTTP specification", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/http-foundation/tree/v7.3.3" + "source": "https://github.com/symfony/http-foundation/tree/v7.3.4" }, "funding": [ { @@ -3625,20 +4919,20 @@ "type": "tidelift" } ], - "time": "2025-08-20T08:04:18+00:00" + "time": "2025-09-16T08:38:17+00:00" }, { "name": "symfony/http-kernel", - "version": "v7.3.3", + "version": "v7.3.4", "source": { "type": "git", "url": "https://github.com/symfony/http-kernel.git", - "reference": "72c304de37e1a1cec6d5d12b81187ebd4850a17b" + "reference": "b796dffea7821f035047235e076b60ca2446e3cf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-kernel/zipball/72c304de37e1a1cec6d5d12b81187ebd4850a17b", - "reference": "72c304de37e1a1cec6d5d12b81187ebd4850a17b", + "url": "https://api.github.com/repos/symfony/http-kernel/zipball/b796dffea7821f035047235e076b60ca2446e3cf", + "reference": "b796dffea7821f035047235e076b60ca2446e3cf", "shasum": "" }, "require": { @@ -3723,7 +5017,7 @@ "description": "Provides a structured process for converting a Request into a Response", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/http-kernel/tree/v7.3.3" + "source": "https://github.com/symfony/http-kernel/tree/v7.3.4" }, "funding": [ { @@ -3743,20 +5037,20 @@ "type": "tidelift" } ], - "time": "2025-08-29T08:23:45+00:00" + "time": "2025-09-27T12:32:17+00:00" }, { "name": "symfony/intl", - "version": "v7.3.3", + "version": "v7.3.4", "source": { "type": "git", "url": "https://github.com/symfony/intl.git", - "reference": "754d5ad02c889e380efc5a74fa3f6cfe56b7454d" + "reference": "e6db84864655885d9dac676a9d7dde0d904fda54" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/intl/zipball/754d5ad02c889e380efc5a74fa3f6cfe56b7454d", - "reference": "754d5ad02c889e380efc5a74fa3f6cfe56b7454d", + "url": "https://api.github.com/repos/symfony/intl/zipball/e6db84864655885d9dac676a9d7dde0d904fda54", + "reference": "e6db84864655885d9dac676a9d7dde0d904fda54", "shasum": "" }, "require": { @@ -3813,7 +5107,7 @@ "localization" ], "support": { - "source": "https://github.com/symfony/intl/tree/v7.3.3" + "source": "https://github.com/symfony/intl/tree/v7.3.4" }, "funding": [ { @@ -3833,7 +5127,7 @@ "type": "tidelift" } ], - "time": "2025-08-19T14:06:46+00:00" + "time": "2025-09-08T14:11:30+00:00" }, { "name": "symfony/mercure", @@ -4004,16 +5298,16 @@ }, { "name": "symfony/mime", - "version": "v7.3.2", + "version": "v7.3.4", "source": { "type": "git", "url": "https://github.com/symfony/mime.git", - "reference": "e0a0f859148daf1edf6c60b398eb40bfc96697d1" + "reference": "b1b828f69cbaf887fa835a091869e55df91d0e35" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/mime/zipball/e0a0f859148daf1edf6c60b398eb40bfc96697d1", - "reference": "e0a0f859148daf1edf6c60b398eb40bfc96697d1", + "url": "https://api.github.com/repos/symfony/mime/zipball/b1b828f69cbaf887fa835a091869e55df91d0e35", + "reference": "b1b828f69cbaf887fa835a091869e55df91d0e35", "shasum": "" }, "require": { @@ -4068,7 +5362,7 @@ "mime-type" ], "support": { - "source": "https://github.com/symfony/mime/tree/v7.3.2" + "source": "https://github.com/symfony/mime/tree/v7.3.4" }, "funding": [ { @@ -4088,7 +5382,7 @@ "type": "tidelift" } ], - "time": "2025-07-15T13:41:35+00:00" + "time": "2025-09-16T08:38:17+00:00" }, { "name": "symfony/options-resolver", @@ -4864,24 +6158,89 @@ ], "authors": [ { - "name": "Grégoire Pineau", - "email": "lyrixx@lyrixx.info" + "name": "Grégoire Pineau", + "email": "lyrixx@lyrixx.info" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for uuid functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "uuid" + ], + "support": { + "source": "https://github.com/symfony/polyfill-uuid/tree/v1.33.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-09T11:45:10+00:00" + }, + { + "name": "symfony/process", + "version": "v7.3.4", + "source": { + "type": "git", + "url": "https://github.com/symfony/process.git", + "reference": "f24f8f316367b30810810d4eb30c543d7003ff3b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/process/zipball/f24f8f316367b30810810d4eb30c543d7003ff3b", + "reference": "f24f8f316367b30810810d4eb30c543d7003ff3b", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Process\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony polyfill for uuid functions", + "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "polyfill", - "portable", - "uuid" - ], "support": { - "source": "https://github.com/symfony/polyfill-uuid/tree/v1.33.0" + "source": "https://github.com/symfony/process/tree/v7.3.4" }, "funding": [ { @@ -4901,7 +6260,7 @@ "type": "tidelift" } ], - "time": "2024-09-09T11:45:10+00:00" + "time": "2025-09-11T10:12:26+00:00" }, { "name": "symfony/property-access", @@ -4985,16 +6344,16 @@ }, { "name": "symfony/property-info", - "version": "v7.3.1", + "version": "v7.3.4", "source": { "type": "git", "url": "https://github.com/symfony/property-info.git", - "reference": "90586acbf2a6dd13bee4f09f09111c8bd4773970" + "reference": "7b6db23f23d13ada41e1cb484748a8ec028fbace" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/property-info/zipball/90586acbf2a6dd13bee4f09f09111c8bd4773970", - "reference": "90586acbf2a6dd13bee4f09f09111c8bd4773970", + "url": "https://api.github.com/repos/symfony/property-info/zipball/7b6db23f23d13ada41e1cb484748a8ec028fbace", + "reference": "7b6db23f23d13ada41e1cb484748a8ec028fbace", "shasum": "" }, "require": { @@ -5051,7 +6410,7 @@ "validator" ], "support": { - "source": "https://github.com/symfony/property-info/tree/v7.3.1" + "source": "https://github.com/symfony/property-info/tree/v7.3.4" }, "funding": [ { @@ -5062,25 +6421,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-06-27T19:55:54+00:00" + "time": "2025-09-15T13:55:54+00:00" }, { "name": "symfony/routing", - "version": "v7.3.2", + "version": "v7.3.4", "source": { "type": "git", "url": "https://github.com/symfony/routing.git", - "reference": "7614b8ca5fa89b9cd233e21b627bfc5774f586e4" + "reference": "8dc648e159e9bac02b703b9fbd937f19ba13d07c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/routing/zipball/7614b8ca5fa89b9cd233e21b627bfc5774f586e4", - "reference": "7614b8ca5fa89b9cd233e21b627bfc5774f586e4", + "url": "https://api.github.com/repos/symfony/routing/zipball/8dc648e159e9bac02b703b9fbd937f19ba13d07c", + "reference": "8dc648e159e9bac02b703b9fbd937f19ba13d07c", "shasum": "" }, "require": { @@ -5132,7 +6495,7 @@ "url" ], "support": { - "source": "https://github.com/symfony/routing/tree/v7.3.2" + "source": "https://github.com/symfony/routing/tree/v7.3.4" }, "funding": [ { @@ -5152,20 +6515,20 @@ "type": "tidelift" } ], - "time": "2025-07-15T11:36:08+00:00" + "time": "2025-09-11T10:12:26+00:00" }, { "name": "symfony/runtime", - "version": "v7.3.1", + "version": "v7.3.4", "source": { "type": "git", "url": "https://github.com/symfony/runtime.git", - "reference": "9516056d432f8acdac9458eb41b80097da7a05c9" + "reference": "3550e2711e30bfa5d808514781cd52d1cc1d9e9f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/runtime/zipball/9516056d432f8acdac9458eb41b80097da7a05c9", - "reference": "9516056d432f8acdac9458eb41b80097da7a05c9", + "url": "https://api.github.com/repos/symfony/runtime/zipball/3550e2711e30bfa5d808514781cd52d1cc1d9e9f", + "reference": "3550e2711e30bfa5d808514781cd52d1cc1d9e9f", "shasum": "" }, "require": { @@ -5215,7 +6578,7 @@ "runtime" ], "support": { - "source": "https://github.com/symfony/runtime/tree/v7.3.1" + "source": "https://github.com/symfony/runtime/tree/v7.3.4" }, "funding": [ { @@ -5226,25 +6589,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-06-13T07:48:40+00:00" + "time": "2025-09-11T15:31:28+00:00" }, { "name": "symfony/security-bundle", - "version": "v7.3.3", + "version": "v7.3.4", "source": { "type": "git", "url": "https://github.com/symfony/security-bundle.git", - "reference": "fbecca9a10af8d886e116f74e860e19b7583689c" + "reference": "f750d9abccbeaa433c56f6a4eb2073166476a75a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/security-bundle/zipball/fbecca9a10af8d886e116f74e860e19b7583689c", - "reference": "fbecca9a10af8d886e116f74e860e19b7583689c", + "url": "https://api.github.com/repos/symfony/security-bundle/zipball/f750d9abccbeaa433c56f6a4eb2073166476a75a", + "reference": "f750d9abccbeaa433c56f6a4eb2073166476a75a", "shasum": "" }, "require": { @@ -5321,7 +6688,7 @@ "description": "Provides a tight integration of the Security component into the Symfony full-stack framework", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/security-bundle/tree/v7.3.3" + "source": "https://github.com/symfony/security-bundle/tree/v7.3.4" }, "funding": [ { @@ -5341,20 +6708,20 @@ "type": "tidelift" } ], - "time": "2025-08-06T08:34:58+00:00" + "time": "2025-09-22T15:31:00+00:00" }, { "name": "symfony/security-core", - "version": "v7.3.3", + "version": "v7.3.4", "source": { "type": "git", "url": "https://github.com/symfony/security-core.git", - "reference": "4465a3b9cefbaebaeeeb98c2becfdb4b59d22488" + "reference": "68b9d3ca57615afde6152a1e1441fa035bea43f8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/security-core/zipball/4465a3b9cefbaebaeeeb98c2becfdb4b59d22488", - "reference": "4465a3b9cefbaebaeeeb98c2becfdb4b59d22488", + "url": "https://api.github.com/repos/symfony/security-core/zipball/68b9d3ca57615afde6152a1e1441fa035bea43f8", + "reference": "68b9d3ca57615afde6152a1e1441fa035bea43f8", "shasum": "" }, "require": { @@ -5412,7 +6779,7 @@ "description": "Symfony Security Component - Core Library", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/security-core/tree/v7.3.3" + "source": "https://github.com/symfony/security-core/tree/v7.3.4" }, "funding": [ { @@ -5432,7 +6799,7 @@ "type": "tidelift" } ], - "time": "2025-08-25T06:35:40+00:00" + "time": "2025-09-24T14:32:13+00:00" }, { "name": "symfony/security-csrf", @@ -5506,16 +6873,16 @@ }, { "name": "symfony/security-http", - "version": "v7.3.3", + "version": "v7.3.4", "source": { "type": "git", "url": "https://github.com/symfony/security-http.git", - "reference": "1bf0dc10f27d4776c47f18f98236c619793a9260" + "reference": "1cf54d0648ebab23bf9b8972617b79f1995e13a9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/security-http/zipball/1bf0dc10f27d4776c47f18f98236c619793a9260", - "reference": "1bf0dc10f27d4776c47f18f98236c619793a9260", + "url": "https://api.github.com/repos/symfony/security-http/zipball/1cf54d0648ebab23bf9b8972617b79f1995e13a9", + "reference": "1cf54d0648ebab23bf9b8972617b79f1995e13a9", "shasum": "" }, "require": { @@ -5574,7 +6941,7 @@ "description": "Symfony Security Component - HTTP Integration", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/security-http/tree/v7.3.3" + "source": "https://github.com/symfony/security-http/tree/v7.3.4" }, "funding": [ { @@ -5594,7 +6961,7 @@ "type": "tidelift" } ], - "time": "2025-08-25T06:35:40+00:00" + "time": "2025-09-09T17:06:44+00:00" }, { "name": "symfony/service-contracts", @@ -5743,16 +7110,16 @@ }, { "name": "symfony/string", - "version": "v7.3.3", + "version": "v7.3.4", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "17a426cce5fd1f0901fefa9b2a490d0038fd3c9c" + "reference": "f96476035142921000338bad71e5247fbc138872" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/17a426cce5fd1f0901fefa9b2a490d0038fd3c9c", - "reference": "17a426cce5fd1f0901fefa9b2a490d0038fd3c9c", + "url": "https://api.github.com/repos/symfony/string/zipball/f96476035142921000338bad71e5247fbc138872", + "reference": "f96476035142921000338bad71e5247fbc138872", "shasum": "" }, "require": { @@ -5767,7 +7134,6 @@ }, "require-dev": { "symfony/emoji": "^7.1", - "symfony/error-handler": "^6.4|^7.0", "symfony/http-client": "^6.4|^7.0", "symfony/intl": "^6.4|^7.0", "symfony/translation-contracts": "^2.5|^3.0", @@ -5810,7 +7176,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v7.3.3" + "source": "https://github.com/symfony/string/tree/v7.3.4" }, "funding": [ { @@ -5830,20 +7196,20 @@ "type": "tidelift" } ], - "time": "2025-08-25T06:35:40+00:00" + "time": "2025-09-11T14:36:48+00:00" }, { "name": "symfony/translation", - "version": "v7.3.3", + "version": "v7.3.4", "source": { "type": "git", "url": "https://github.com/symfony/translation.git", - "reference": "e0837b4cbcef63c754d89a4806575cada743a38d" + "reference": "ec25870502d0c7072d086e8ffba1420c85965174" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation/zipball/e0837b4cbcef63c754d89a4806575cada743a38d", - "reference": "e0837b4cbcef63c754d89a4806575cada743a38d", + "url": "https://api.github.com/repos/symfony/translation/zipball/ec25870502d0c7072d086e8ffba1420c85965174", + "reference": "ec25870502d0c7072d086e8ffba1420c85965174", "shasum": "" }, "require": { @@ -5910,7 +7276,7 @@ "description": "Provides tools to internationalize your application", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/translation/tree/v7.3.3" + "source": "https://github.com/symfony/translation/tree/v7.3.4" }, "funding": [ { @@ -5930,7 +7296,7 @@ "type": "tidelift" } ], - "time": "2025-08-01T21:02:37+00:00" + "time": "2025-09-07T11:39:36+00:00" }, { "name": "symfony/translation-contracts", @@ -6127,16 +7493,16 @@ }, { "name": "symfony/twig-bundle", - "version": "v7.3.2", + "version": "v7.3.4", "source": { "type": "git", "url": "https://github.com/symfony/twig-bundle.git", - "reference": "5d85220df4d8d79e6a9ca57eea6f70004de39657" + "reference": "da5c778a8416fcce5318737c4d944f6fa2bb3f81" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/twig-bundle/zipball/5d85220df4d8d79e6a9ca57eea6f70004de39657", - "reference": "5d85220df4d8d79e6a9ca57eea6f70004de39657", + "url": "https://api.github.com/repos/symfony/twig-bundle/zipball/da5c778a8416fcce5318737c4d944f6fa2bb3f81", + "reference": "da5c778a8416fcce5318737c4d944f6fa2bb3f81", "shasum": "" }, "require": { @@ -6191,7 +7557,7 @@ "description": "Provides a tight integration of Twig into the Symfony full-stack framework", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/twig-bundle/tree/v7.3.2" + "source": "https://github.com/symfony/twig-bundle/tree/v7.3.4" }, "funding": [ { @@ -6211,20 +7577,20 @@ "type": "tidelift" } ], - "time": "2025-07-10T08:47:49+00:00" + "time": "2025-09-10T12:00:31+00:00" }, { "name": "symfony/type-info", - "version": "v7.3.3", + "version": "v7.3.4", "source": { "type": "git", "url": "https://github.com/symfony/type-info.git", - "reference": "aa64b58ed04517d4d730202dd035895743c23273" + "reference": "d34eaeb57f39c8a9c97eb72a977c423207dfa35b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/type-info/zipball/aa64b58ed04517d4d730202dd035895743c23273", - "reference": "aa64b58ed04517d4d730202dd035895743c23273", + "url": "https://api.github.com/repos/symfony/type-info/zipball/d34eaeb57f39c8a9c97eb72a977c423207dfa35b", + "reference": "d34eaeb57f39c8a9c97eb72a977c423207dfa35b", "shasum": "" }, "require": { @@ -6274,7 +7640,7 @@ "type" ], "support": { - "source": "https://github.com/symfony/type-info/tree/v7.3.3" + "source": "https://github.com/symfony/type-info/tree/v7.3.4" }, "funding": [ { @@ -6294,7 +7660,7 @@ "type": "tidelift" } ], - "time": "2025-08-28T09:38:04+00:00" + "time": "2025-09-11T15:33:27+00:00" }, { "name": "symfony/uid", @@ -6459,16 +7825,16 @@ }, { "name": "symfony/validator", - "version": "v7.3.3", + "version": "v7.3.4", "source": { "type": "git", "url": "https://github.com/symfony/validator.git", - "reference": "a2f26d7c122393db75a2d41435ad8251250f8bc6" + "reference": "5e29a348b5fac2227b6938a54db006d673bb813a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/validator/zipball/a2f26d7c122393db75a2d41435ad8251250f8bc6", - "reference": "a2f26d7c122393db75a2d41435ad8251250f8bc6", + "url": "https://api.github.com/repos/symfony/validator/zipball/5e29a348b5fac2227b6938a54db006d673bb813a", + "reference": "5e29a348b5fac2227b6938a54db006d673bb813a", "shasum": "" }, "require": { @@ -6537,7 +7903,7 @@ "description": "Provides tools to validate values", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/validator/tree/v7.3.3" + "source": "https://github.com/symfony/validator/tree/v7.3.4" }, "funding": [ { @@ -6557,20 +7923,20 @@ "type": "tidelift" } ], - "time": "2025-08-27T11:34:33+00:00" + "time": "2025-09-24T06:32:27+00:00" }, { "name": "symfony/var-dumper", - "version": "v7.3.3", + "version": "v7.3.4", "source": { "type": "git", "url": "https://github.com/symfony/var-dumper.git", - "reference": "34d8d4c4b9597347306d1ec8eb4e1319b1e6986f" + "reference": "b8abe7daf2730d07dfd4b2ee1cecbf0dd2fbdabb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/34d8d4c4b9597347306d1ec8eb4e1319b1e6986f", - "reference": "34d8d4c4b9597347306d1ec8eb4e1319b1e6986f", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/b8abe7daf2730d07dfd4b2ee1cecbf0dd2fbdabb", + "reference": "b8abe7daf2730d07dfd4b2ee1cecbf0dd2fbdabb", "shasum": "" }, "require": { @@ -6624,7 +7990,7 @@ "dump" ], "support": { - "source": "https://github.com/symfony/var-dumper/tree/v7.3.3" + "source": "https://github.com/symfony/var-dumper/tree/v7.3.4" }, "funding": [ { @@ -6644,20 +8010,20 @@ "type": "tidelift" } ], - "time": "2025-08-13T11:49:31+00:00" + "time": "2025-09-11T10:12:26+00:00" }, { "name": "symfony/var-exporter", - "version": "v7.3.3", + "version": "v7.3.4", "source": { "type": "git", "url": "https://github.com/symfony/var-exporter.git", - "reference": "d4dfcd2a822cbedd7612eb6fbd260e46f87b7137" + "reference": "0f020b544a30a7fe8ba972e53ee48a74c0bc87f4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-exporter/zipball/d4dfcd2a822cbedd7612eb6fbd260e46f87b7137", - "reference": "d4dfcd2a822cbedd7612eb6fbd260e46f87b7137", + "url": "https://api.github.com/repos/symfony/var-exporter/zipball/0f020b544a30a7fe8ba972e53ee48a74c0bc87f4", + "reference": "0f020b544a30a7fe8ba972e53ee48a74c0bc87f4", "shasum": "" }, "require": { @@ -6705,7 +8071,7 @@ "serialize" ], "support": { - "source": "https://github.com/symfony/var-exporter/tree/v7.3.3" + "source": "https://github.com/symfony/var-exporter/tree/v7.3.4" }, "funding": [ { @@ -6725,7 +8091,7 @@ "type": "tidelift" } ], - "time": "2025-08-18T13:10:53+00:00" + "time": "2025-09-11T10:12:26+00:00" }, { "name": "symfony/web-link", @@ -6886,6 +8252,62 @@ ], "time": "2025-08-27T11:34:33+00:00" }, + { + "name": "symfonycasts/tailwind-bundle", + "version": "v0.11.1", + "source": { + "type": "git", + "url": "https://github.com/SymfonyCasts/tailwind-bundle.git", + "reference": "72590eec2aa532a73ce9394c6d3717fe3efd88f5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/SymfonyCasts/tailwind-bundle/zipball/72590eec2aa532a73ce9394c6d3717fe3efd88f5", + "reference": "72590eec2aa532a73ce9394c6d3717fe3efd88f5", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/asset-mapper": "^6.3|^7.0", + "symfony/cache": "^6.3|^7.0", + "symfony/console": "^5.4|^6.3|^7.0", + "symfony/deprecation-contracts": "^2.2|^3.0", + "symfony/http-client": "^5.4|^6.3|^7.0", + "symfony/process": "^5.4|^6.3|^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.6", + "symfony/filesystem": "^6.3|^7.0", + "symfony/framework-bundle": "^6.3|^7.0", + "symfony/phpunit-bridge": "^6.3.9|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfonycasts\\TailwindBundle\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ryan Weaver", + "homepage": "https://symfonycasts.com" + } + ], + "description": "Delightful Tailwind Support for Symfony + AssetMapper", + "keywords": [ + "asset-mapper", + "tailwind" + ], + "support": { + "issues": "https://github.com/SymfonyCasts/tailwind-bundle/issues", + "source": "https://github.com/SymfonyCasts/tailwind-bundle/tree/v0.11.1" + }, + "time": "2025-07-29T16:22:45+00:00" + }, { "name": "twig/extra-bundle", "version": "v3.21.0", @@ -8038,16 +9460,16 @@ }, { "name": "friendsofphp/php-cs-fixer", - "version": "v3.87.2", + "version": "v3.88.2", "source": { "type": "git", "url": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer.git", - "reference": "da5f0a7858c79b56fc0b8c36d3efcfe5f37f0992" + "reference": "a8d15584bafb0f0d9d938827840060fd4a3ebc99" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/da5f0a7858c79b56fc0b8c36d3efcfe5f37f0992", - "reference": "da5f0a7858c79b56fc0b8c36d3efcfe5f37f0992", + "url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/a8d15584bafb0f0d9d938827840060fd4a3ebc99", + "reference": "a8d15584bafb0f0d9d938827840060fd4a3ebc99", "shasum": "" }, "require": { @@ -8074,12 +9496,13 @@ "symfony/polyfill-mbstring": "^1.33", "symfony/polyfill-php80": "^1.33", "symfony/polyfill-php81": "^1.33", + "symfony/polyfill-php84": "^1.33", "symfony/process": "^5.4.47 || ^6.4.24 || ^7.2", "symfony/stopwatch": "^5.4.45 || ^6.4.24 || ^7.0" }, "require-dev": { "facile-it/paraunit": "^1.3.1 || ^2.7", - "infection/infection": "^0.29.14", + "infection/infection": "^0.31.0", "justinrainbow/json-schema": "^6.5", "keradus/cli-executor": "^2.2", "mikey179/vfsstream": "^1.6.12", @@ -8087,7 +9510,6 @@ "php-cs-fixer/phpunit-constraint-isidenticalstring": "^1.6", "php-cs-fixer/phpunit-constraint-xmlmatchesxsd": "^1.6", "phpunit/phpunit": "^9.6.25 || ^10.5.53 || ^11.5.34", - "symfony/polyfill-php84": "^1.33", "symfony/var-dumper": "^5.4.48 || ^6.4.24 || ^7.3.2", "symfony/yaml": "^5.4.45 || ^6.4.24 || ^7.3.2" }, @@ -8130,7 +9552,7 @@ ], "support": { "issues": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/issues", - "source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.87.2" + "source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.88.2" }, "funding": [ { @@ -8138,7 +9560,7 @@ "type": "github" } ], - "time": "2025-09-10T09:51:40+00:00" + "time": "2025-09-27T00:24:15+00:00" }, { "name": "hautelook/alice-bundle", @@ -8573,6 +9995,68 @@ ], "time": "2025-02-26T09:01:07+00:00" }, + { + "name": "nelmio/cors-bundle", + "version": "2.5.0", + "source": { + "type": "git", + "url": "https://github.com/nelmio/NelmioCorsBundle.git", + "reference": "3a526fe025cd20e04a6a11370cf5ab28dbb5a544" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nelmio/NelmioCorsBundle/zipball/3a526fe025cd20e04a6a11370cf5ab28dbb5a544", + "reference": "3a526fe025cd20e04a6a11370cf5ab28dbb5a544", + "shasum": "" + }, + "require": { + "psr/log": "^1.0 || ^2.0 || ^3.0", + "symfony/framework-bundle": "^5.4 || ^6.0 || ^7.0" + }, + "require-dev": { + "mockery/mockery": "^1.3.6", + "symfony/phpunit-bridge": "^5.4 || ^6.0 || ^7.0" + }, + "type": "symfony-bundle", + "extra": { + "branch-alias": { + "dev-master": "2.x-dev" + } + }, + "autoload": { + "psr-4": { + "Nelmio\\CorsBundle\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nelmio", + "homepage": "http://nelm.io" + }, + { + "name": "Symfony Community", + "homepage": "https://github.com/nelmio/NelmioCorsBundle/contributors" + } + ], + "description": "Adds CORS (Cross-Origin Resource Sharing) headers support in your Symfony application", + "keywords": [ + "api", + "cors", + "crossdomain" + ], + "support": { + "issues": "https://github.com/nelmio/NelmioCorsBundle/issues", + "source": "https://github.com/nelmio/NelmioCorsBundle/tree/2.5.0" + }, + "time": "2024-06-24T21:25:28+00:00" + }, { "name": "nikic/php-parser", "version": "v5.6.1", @@ -9318,16 +10802,16 @@ }, { "name": "sebastian/exporter", - "version": "7.0.0", + "version": "7.0.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "76432aafc58d50691a00d86d0632f1217a47b688" + "reference": "016951ae10980765e4e7aee491eb288c64e505b7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/76432aafc58d50691a00d86d0632f1217a47b688", - "reference": "76432aafc58d50691a00d86d0632f1217a47b688", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/016951ae10980765e4e7aee491eb288c64e505b7", + "reference": "016951ae10980765e4e7aee491eb288c64e505b7", "shasum": "" }, "require": { @@ -9384,15 +10868,27 @@ "support": { "issues": "https://github.com/sebastianbergmann/exporter/issues", "security": "https://github.com/sebastianbergmann/exporter/security/policy", - "source": "https://github.com/sebastianbergmann/exporter/tree/7.0.0" + "source": "https://github.com/sebastianbergmann/exporter/tree/7.0.2" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/exporter", + "type": "tidelift" } ], - "time": "2025-02-07T04:56:42+00:00" + "time": "2025-09-24T06:16:11+00:00" }, { "name": "sebastian/recursion-context", @@ -9470,6 +10966,81 @@ ], "time": "2025-08-13T04:44:59+00:00" }, + { + "name": "symfony/debug-bundle", + "version": "v7.3.4", + "source": { + "type": "git", + "url": "https://github.com/symfony/debug-bundle.git", + "reference": "30f922edd53dd85238f1f26dbb68a044109f8f0e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/debug-bundle/zipball/30f922edd53dd85238f1f26dbb68a044109f8f0e", + "reference": "30f922edd53dd85238f1f26dbb68a044109f8f0e", + "shasum": "" + }, + "require": { + "composer-runtime-api": ">=2.1", + "ext-xml": "*", + "php": ">=8.2", + "symfony/config": "^7.3", + "symfony/dependency-injection": "^6.4|^7.0", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/twig-bridge": "^6.4|^7.0", + "symfony/var-dumper": "^6.4|^7.0" + }, + "require-dev": { + "symfony/web-profiler-bundle": "^6.4|^7.0" + }, + "type": "symfony-bundle", + "autoload": { + "psr-4": { + "Symfony\\Bundle\\DebugBundle\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides a tight integration of the Symfony VarDumper component and the ServerLogCommand from MonologBridge into the Symfony full-stack framework", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/debug-bundle/tree/v7.3.4" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-09-10T12:00:31+00:00" + }, { "name": "symfony/maker-bundle", "version": "v1.64.0", @@ -9563,83 +11134,18 @@ ], "time": "2025-06-23T16:12:08+00:00" }, - { - "name": "symfony/process", - "version": "v7.3.3", - "source": { - "type": "git", - "url": "https://github.com/symfony/process.git", - "reference": "32241012d521e2e8a9d713adb0812bb773b907f1" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/32241012d521e2e8a9d713adb0812bb773b907f1", - "reference": "32241012d521e2e8a9d713adb0812bb773b907f1", - "shasum": "" - }, - "require": { - "php": ">=8.2" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\Process\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Executes commands in sub-processes", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/process/tree/v7.3.3" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2025-08-18T09:42:54+00:00" - }, { "name": "symfony/web-profiler-bundle", - "version": "v7.3.3", + "version": "v7.3.4", "source": { "type": "git", "url": "https://github.com/symfony/web-profiler-bundle.git", - "reference": "6ee224d6e9de787a47622b9ad4880e205ef16ad1" + "reference": "f305fa4add690bb7d6b14ab61f37c3bd061a3dd7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/web-profiler-bundle/zipball/6ee224d6e9de787a47622b9ad4880e205ef16ad1", - "reference": "6ee224d6e9de787a47622b9ad4880e205ef16ad1", + "url": "https://api.github.com/repos/symfony/web-profiler-bundle/zipball/f305fa4add690bb7d6b14ab61f37c3bd061a3dd7", + "reference": "f305fa4add690bb7d6b14ab61f37c3bd061a3dd7", "shasum": "" }, "require": { @@ -9695,7 +11201,7 @@ "dev" ], "support": { - "source": "https://github.com/symfony/web-profiler-bundle/tree/v7.3.3" + "source": "https://github.com/symfony/web-profiler-bundle/tree/v7.3.4" }, "funding": [ { @@ -9715,7 +11221,7 @@ "type": "tidelift" } ], - "time": "2025-08-19T13:44:55+00:00" + "time": "2025-09-25T08:03:55+00:00" }, { "name": "theofidry/alice-data-fixtures", @@ -9820,16 +11326,16 @@ }, { "name": "vincentlanglet/twig-cs-fixer", - "version": "3.9.0", + "version": "3.10.0", "source": { "type": "git", "url": "https://github.com/VincentLanglet/Twig-CS-Fixer.git", - "reference": "bd279a1178d31f1470a6801b42ba0b6ad4754930" + "reference": "ee9b6a31d2c2522b2773ecf31f5d29c2e26311a6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/VincentLanglet/Twig-CS-Fixer/zipball/bd279a1178d31f1470a6801b42ba0b6ad4754930", - "reference": "bd279a1178d31f1470a6801b42ba0b6ad4754930", + "url": "https://api.github.com/repos/VincentLanglet/Twig-CS-Fixer/zipball/ee9b6a31d2c2522b2773ecf31f5d29c2e26311a6", + "reference": "ee9b6a31d2c2522b2773ecf31f5d29c2e26311a6", "shasum": "" }, "require": { @@ -9837,10 +11343,10 @@ "ext-ctype": "*", "ext-json": "*", "php": ">=8.0", - "symfony/console": "^5.4.9 || ^6.4 || ^7.0", - "symfony/filesystem": "^5.4 || ^6.4 || ^7.0", - "symfony/finder": "^5.4 || ^6.4 || ^7.0", - "symfony/string": "^5.4.42 || ^6.4.10 || ~7.0.10 || ^7.1.3", + "symfony/console": "^5.4.9 || ^6.4 || ^7.0 || ^8.0", + "symfony/filesystem": "^5.4 || ^6.4 || ^7.0 || ^8.0", + "symfony/finder": "^5.4 || ^6.4 || ^7.0 || ^8.0", + "symfony/string": "^5.4.42 || ^6.4.10 || ~7.0.10 || ^7.1.3 || ^8.0", "twig/twig": "^3.4", "webmozart/assert": "^1.10" }, @@ -9858,8 +11364,8 @@ "phpunit/phpunit": "^9.5.26 || ^11.5.18 || ^12.1.3", "rector/rector": "^2.0.0", "shipmonk/composer-dependency-analyser": "^1.6", - "symfony/process": "^5.4 || ^6.4 || ^7.0", - "symfony/twig-bridge": "^5.4 || ^6.4 || ^7.0", + "symfony/process": "^5.4 || ^6.4 || ^7.0 || ^8.0", + "symfony/twig-bridge": "^5.4 || ^6.4 || ^7.0 || ^8.0", "symfony/ux-twig-component": "^2.2.0", "twig/cache-extra": "^3.2" }, @@ -9885,7 +11391,7 @@ "homepage": "https://github.com/VincentLanglet/Twig-CS-Fixer", "support": { "issues": "https://github.com/VincentLanglet/Twig-CS-Fixer/issues", - "source": "https://github.com/VincentLanglet/Twig-CS-Fixer/tree/3.9.0" + "source": "https://github.com/VincentLanglet/Twig-CS-Fixer/tree/3.10.0" }, "funding": [ { @@ -9893,7 +11399,7 @@ "type": "github" } ], - "time": "2025-08-01T08:21:57+00:00" + "time": "2025-09-15T11:28:55+00:00" }, { "name": "webmozart/assert", diff --git a/config/bundles.php b/config/bundles.php index c692d5b..155e49b 100644 --- a/config/bundles.php +++ b/config/bundles.php @@ -15,4 +15,9 @@ Fidry\AliceDataFixtures\Bridge\Symfony\FidryAliceDataFixturesBundle::class => ['dev' => true, 'test' => true], Hautelook\AliceBundle\HautelookAliceBundle::class => ['dev' => true, 'test' => true], Symfony\Bundle\WebProfilerBundle\WebProfilerBundle::class => ['dev' => true, 'test' => true], + Nelmio\CorsBundle\NelmioCorsBundle::class => ['dev' => true], + Symfony\Bundle\DebugBundle\DebugBundle::class => ['dev' => true], + Symfonycasts\TailwindBundle\SymfonycastsTailwindBundle::class => ['all' => true], + Stof\DoctrineExtensionsBundle\StofDoctrineExtensionsBundle::class => ['all' => true], + ItkDev\OpenIdConnectBundle\ItkDevOpenIdConnectBundle::class => ['all' => true], ]; diff --git a/config/packages/debug.yaml b/config/packages/debug.yaml new file mode 100644 index 0000000..54a4821 --- /dev/null +++ b/config/packages/debug.yaml @@ -0,0 +1,5 @@ +when@dev: + debug: + # Forwards VarDumper Data clones to a centralized server allowing to inspect dumps on CLI or in your browser. + # See the "server:dump" command to start a new server. + dump_destination: 'tcp://%env(VAR_DUMPER_SERVER)%' diff --git a/config/packages/itkdev_openid_connect.yaml b/config/packages/itkdev_openid_connect.yaml new file mode 100644 index 0000000..f995883 --- /dev/null +++ b/config/packages/itkdev_openid_connect.yaml @@ -0,0 +1,20 @@ +itkdev_openid_connect: + cache_options: + cache_pool: 'cache.app' # Cache item pool for caching discovery document and CLI login tokens + cli_login_options: + route: '%env(string:OIDC_CLI_LOGIN_ROUTE)%' # Redirect route for CLI login + user_provider: ~ # + openid_providers: + admin: + options: + metadata_url: '%env(string:ADMIN_OIDC_METADATA_URL)%' + client_id: '%env(string:ADMIN_OIDC_CLIENT_ID)%' + client_secret: '%env(string:ADMIN_OIDC_CLIENT_SECRET)%' + # Specify redirect URI + redirect_uri: '%env(string:ADMIN_OIDC_REDIRECT_URI)%' + # Optional: Specify leeway (seconds) to account for clock skew between provider and hosting + # Defaults to 10 + leeway: '%env(int:ADMIN_OIDC_LEEWAY)%' + # Optional: Allow (non-secure) http requests (used for mocking a IdP). NOT RECOMMENDED FOR PRODUCTION. + # Defaults to false + allow_http: '%env(bool:ADMIN_OIDC_ALLOW_HTTP)%' diff --git a/config/packages/nelmio_cors.yaml b/config/packages/nelmio_cors.yaml new file mode 100644 index 0000000..13e9e32 --- /dev/null +++ b/config/packages/nelmio_cors.yaml @@ -0,0 +1,11 @@ +when@dev: + nelmio_cors: + defaults: + origin_regex: true + allow_origin: ['%env(CORS_ALLOW_ORIGIN)%'] + allow_methods: ['GET', 'OPTIONS', 'POST', 'PUT', 'PATCH', 'DELETE'] + allow_headers: ['Content-Type', 'Authorization'] + expose_headers: ['Link'] + max_age: 3600 + paths: + '^/': null diff --git a/config/packages/routing.yaml b/config/packages/routing.yaml index 8166181..0f34f87 100644 --- a/config/packages/routing.yaml +++ b/config/packages/routing.yaml @@ -2,7 +2,7 @@ framework: router: # Configure how to generate URLs in non-HTTP contexts, such as CLI commands. # See https://symfony.com/doc/current/routing.html#generating-urls-in-commands - #default_uri: http://localhost + default_uri: '%env(DEFAULT_URI)%' when@prod: framework: diff --git a/config/packages/security.yaml b/config/packages/security.yaml index 367af25..a72f273 100644 --- a/config/packages/security.yaml +++ b/config/packages/security.yaml @@ -1,17 +1,21 @@ security: - # https://symfony.com/doc/current/security.html#registering-the-user-hashing-passwords - password_hashers: - Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface: 'auto' # https://symfony.com/doc/current/security.html#loading-the-user-the-user-provider providers: - users_in_memory: { memory: null } + app_user_provider: + entity: + class: App\Entity\User + property: email + firewalls: dev: pattern: ^/(_(profiler|wdt)|css|images|js)/ security: false main: lazy: true - provider: users_in_memory + custom_authenticators: + - App\Security\OidcAuthenticator + - ItkDev\OpenIdConnectBundle\Security\CliLoginTokenAuthenticator + entry_point: App\Security\OidcAuthenticator # activate different ways to authenticate # https://symfony.com/doc/current/security.html#the-firewall @@ -19,21 +23,16 @@ security: # https://symfony.com/doc/current/security/impersonating_user.html # switch_user: true + role_hierarchy: + !php/enum App\Entity\UserRole::Admin->value: + - !php/enum App\Entity\UserRole::User->value + !php/enum App\Entity\UserRole::OverviewManager->value: + - !php/enum App\Entity\UserRole::Admin->value + # Easy way to control access for large sections of your site # Note: Only the *first* access control that matches will be used access_control: - # - { path: ^/admin, roles: ROLE_ADMIN } - # - { path: ^/profile, roles: ROLE_USER } - -when@test: - security: - password_hashers: - # By default, password hashers are resource intensive and take time. This is - # important to generate secure password hashes. In tests however, secure hashes - # are not important, waste resources and increase test times. The following - # reduces the work factor to the lowest possible values. - Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface: - algorithm: auto - cost: 4 # Lowest possible value for bcrypt - time_cost: 3 # Lowest possible value for argon - memory_cost: 10 # Lowest possible value for argon + - { path: ^/admin, roles: !php/enum App\Entity\UserRole::Admin->value } + - { path: ^/login, roles: PUBLIC_ACCESS } + - { path: '^/openidconnect/login(/.+)?$', role: PUBLIC_ACCESS } + - { path: ^/, roles: !php/enum App\Entity\UserRole::User->value } diff --git a/config/packages/stof_doctrine_extensions.yaml b/config/packages/stof_doctrine_extensions.yaml new file mode 100644 index 0000000..45fe1b3 --- /dev/null +++ b/config/packages/stof_doctrine_extensions.yaml @@ -0,0 +1,8 @@ +# Read the documentation: https://symfony.com/doc/current/bundles/StofDoctrineExtensionsBundle/index.html +# See the official DoctrineExtensions documentation for more details: https://github.com/doctrine-extensions/DoctrineExtensions/tree/main/doc +stof_doctrine_extensions: + default_locale: '%env(DEFAULT_LOCALE)%' + orm: + default: + blameable: true + timestampable: true diff --git a/config/packages/symfonycasts_tailwind.yaml b/config/packages/symfonycasts_tailwind.yaml new file mode 100644 index 0000000..72510c1 --- /dev/null +++ b/config/packages/symfonycasts_tailwind.yaml @@ -0,0 +1,5 @@ +symfonycasts_tailwind: + # Specify the EXACT version of Tailwind CSS you want to use + binary_version: 'v4.1.11' + # Alternatively, you can specify the path to the binary that you manage yourself + #binary: 'node_modules/.bin/tailwindcss' diff --git a/config/packages/twig.yaml b/config/packages/twig.yaml index 3f795d9..90b7fe6 100644 --- a/config/packages/twig.yaml +++ b/config/packages/twig.yaml @@ -1,5 +1,7 @@ twig: file_name_pattern: '*.twig' + globals: + site_title: '%site_title%' when@test: twig: diff --git a/config/routes/itkdev_openid_connect.yaml b/config/routes/itkdev_openid_connect.yaml new file mode 100644 index 0000000..1c60325 --- /dev/null +++ b/config/routes/itkdev_openid_connect.yaml @@ -0,0 +1,3 @@ +itkdev_openid_connect: + resource: '@ItkDevOpenIdConnectBundle/src/Resources/config/routes.yaml' + prefix: '/openidconnect' # Prefix for bundle routes diff --git a/config/services.yaml b/config/services.yaml index 4467338..354770e 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -4,6 +4,7 @@ # Put parameters here that don't need to change on each machine where the app is deployed # https://symfony.com/doc/current/best_practices.html#use-parameters-for-application-configuration parameters: + site_title: '%env(SITE_TITLE)%' services: # default configuration for services in *this* file @@ -18,3 +19,8 @@ services: # add more service definitions when explicit configuration is needed # please note that last definitions always *replace* previous ones + + App\Security\OidcAuthenticator: + arguments: + $options: + role_map: '%env(json:ADMIN_OIDC_ROLE_MAP)%' diff --git a/docker-compose.oidc.yml b/docker-compose.oidc.yml new file mode 100644 index 0000000..ef39741 --- /dev/null +++ b/docker-compose.oidc.yml @@ -0,0 +1,123 @@ +services: + # https://github.com/Soluto/oidc-server-mock + oidc-idp: + image: ghcr.io/soluto/oidc-server-mock:latest + profiles: + - ${DOCKER_OIDC_DISABLE:-} + # Let this container be accessible both internally and externally on the same domain. + container_name: idp.${COMPOSE_DOMAIN} + networks: + - app + - frontend + ports: + - "80" + volumes: + - .:/tmp/config:ro + labels: + - "traefik.enable=true" + - "traefik.docker.network=frontend" + - "traefik.http.routers.${COMPOSE_PROJECT_NAME}-idp.rule=Host(`idp.${COMPOSE_DOMAIN}`)" + - "traefik.http.services.${COMPOSE_PROJECT_NAME}-idp.loadbalancer.server.port=443" + - "traefik.http.services.${COMPOSE_PROJECT_NAME}-idp.loadbalancer.server.scheme=https" + - "traefik.http.routers.${COMPOSE_PROJECT_NAME}-idp.middlewares=redirect-to-https" + - "traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https" + environment: + # https://github.com/Soluto/oidc-server-mock?tab=readme-ov-file#https + ASPNETCORE_URLS: https://+:443;http://+:80 + ASPNETCORE_Kestrel__Certificates__Default__Password: mock + ASPNETCORE_Kestrel__Certificates__Default__Path: /tmp/config/.docker/oidc-server-mock/cert/docker.pfx + + ASPNETCORE_ENVIRONMENT: Development + SERVER_OPTIONS_INLINE: | + AccessTokenJwtType: JWT + Discovery: + ShowKeySet: true + Authentication: + CookieSameSiteMode: Lax + CheckSessionCookieSameSiteMode: Lax + + LOGIN_OPTIONS_INLINE: | + { + "AllowRememberLogin": false + } + + LOGOUT_OPTIONS_INLINE: | + { + "AutomaticRedirectAfterSignOut": true + } + + CLIENTS_CONFIGURATION_INLINE: | + - ClientId: client-id + ClientSecrets: [client-secret] + Description: Mock IdP + AllowedGrantTypes: + # - client_credentials + # - implicit + - authorization_code + # https://github.com/Soluto/oidc-server-mock/issues/46#issuecomment-704963181 + RequireClientSecret: false + AllowAccessTokensViaBrowser: true + # https://github.com/Soluto/oidc-server-mock/issues/26#issuecomment-705022941 + AlwaysIncludeUserClaimsInIdToken: true + AllowedScopes: + - openid + - profile + - email + ClientClaimsPrefix: '' + RedirectUris: + - '*' + # https://github.com/Soluto/oidc-server-mock/issues/60 + PostLogoutRedirectUris: + - '*' + # https://github.com/Soluto/oidc-server-mock/issues/46#issuecomment-704845375 + RequirePkce: false + + # Needed to set custom claim types in "profile" + # https://github.com/Soluto/oidc-server-mock/issues/123#issuecomment-1427129278 + # https://github.com/Soluto/oidc-server-mock/blob/master/README.md#simple-configuration + # https://docs.docker.com/compose/compose-file/compose-file-v3/#environment + OVERRIDE_STANDARD_IDENTITY_RESOURCES: "true" + IDENTITY_RESOURCES_INLINE: | + # https://auth0.com/docs/get-started/apis/scopes/openid-connect-scopes#standard-claims + - Name: openid + ClaimTypes: + - sub + - Name: email + ClaimTypes: + - email + - Name: profile + ClaimTypes: + # Add your custom claims here + - name + - groups + - upn + + USERS_CONFIGURATION_INLINE: | + - SubjectId: 1 + Username: overview-manager + Password: overview-manager + Claims: + # Claims added here must be defined above in IDENTITY_RESOURCES_INLINE + - Type: email + Value: 'overview-manager@example.com' + ValueType: string + - Type: groups + Value: '["overview-manager"]' + ValueType: json + - Type: upn + Value: 'overview-manager@example.com' + ValueType: string + + - SubjectId: 2 + Username: user + Password: user + Claims: + - Type: email + Value: 'user@example.com' + ValueType: string + - Type: groups + Value: '["user"]' + ValueType: json + - Type: upn + Value: 'user@example.com' + ValueType: string diff --git a/docker-compose.override.yml b/docker-compose.override.yml index 0618562..55481c5 100644 --- a/docker-compose.override.yml +++ b/docker-compose.override.yml @@ -1,22 +1,50 @@ include: - docker-compose.api.yml + - docker-compose.oidc.yml services: phpfpm: image: itkdev/php8.4-fpm:latest + build: + # We need rsync to copy built widget files. + dockerfile_inline: | + FROM itkdev/php8.4-fpm:latest + + USER root + RUN apt-get update && \ + apt-get --yes install rsync + + USER deploy nginx: + ports: !override + - "8787:8080" labels: # HTTPS config - uncomment to enable redirect from :80 to :443 - "traefik.http.routers.${COMPOSE_PROJECT_NAME}.middlewares=redirect-to-https" - "traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https" - # https://github.com/dotronglong/faker/wiki/Getting-Started-%5BDocker%5D - faker: - image: dotronglong/faker:stable - networks: - - app + prettier: + # Prettier does not (yet, fcf. + # https://github.com/prettier/prettier/issues/15206) have an official + # docker image. + # https://hub.docker.com/r/jauderho/prettier is good candidate (cf. https://hub.docker.com/search?q=prettier&sort=updated_at&order=desc) + # image: jauderho/prettier + build: + # Custom image to include https://github.com/sveltejs/prettier-plugin-svelte + dockerfile_inline: | + # https://github.com/jauderho/dockerfiles/blob/main/prettier/Dockerfile + FROM node:24 + + RUN npm install --global prettier prettier-plugin-svelte + + WORKDIR /work + + # Help prettier find our globally installed plugin + # (cf. https://github.com/prettier/prettier/issues/15141#issuecomment-2624767782) + ENTRYPOINT ["prettier", "--plugin", "/usr/local/lib/node_modules/prettier-plugin-svelte/plugin.js"] + CMD ["--help"] volumes: - - ./mocks:/app/mocks - ports: - - "3030" + - ./:/work + profiles: + - dev diff --git a/docker-compose.server.override.yml b/docker-compose.server.override.yml new file mode 100644 index 0000000..93590d3 --- /dev/null +++ b/docker-compose.server.override.yml @@ -0,0 +1,3 @@ +services: + phpfpm: + image: itkdev/php8.4-fpm:alpine diff --git a/docs/API.md b/docs/API.md new file mode 100644 index 0000000..364371c --- /dev/null +++ b/docs/API.md @@ -0,0 +1,170 @@ +# API + +> [!CAUTION] +> This documentation is incomplete – and may be incorrect … + +The RPA process overview does not show data directly from the source … we fetch it and transform it … + +A `GET` request to `/group/11/overview/24/data?page=1&size=5`, say, will return JSON data in the following form[^1]: + +``` json5 +{ + // The main data + data: { + // The columns + columns: [ + { + label: "Borger", + data: "metadata.cpr", + type: "text" + }, + { + label: "Name", + data: "metadata.name", + type: "text" + }, + { + label: "Klinik", + data: "metadata.branch", + type: "text" + }, + { + label: "American.", + type: "step" + }, + { + label: "Surface later.", + type: "step" + }, + { + label: "Apply.", + type: "step" + }, + { + label: "Office wall.", + type: "step" + }, + { + label: "Tree back.", + type: "step" + }, + { + label: "Mrs others.", + type: "step" + } + ], + // The rows + rows: [ + [ + { + type: "text", + value: null + }, + { + type: "text", + value: null + }, + { + type: "text", + value: null + }, + { + created_at: "2025-09-30T08:31:29", + started_at: "2025-09-14T02:47:44.613627", + failure: null, + step_id: 8, + id: 176, + updated_at: "2025-09-30T08:31:29", + status: "SUCCESS", + finished_at: "2025-09-14T02:47:51.613627", + run_id: 26, + step_index: 0, + type: "step" + }, + { + created_at: "2025-09-30T08:31:29", + started_at: "2025-09-14T02:56:05.613627", + failure: { + code: 6, + message: "Southern white plan campaign.", + retryable: true, + occurred_at: "2025-09-14T02:56:14.613627" + }, + step_id: 9, + id: 177, + updated_at: "2025-09-30T08:31:29", + status: "FAILED", + finished_at: null, + run_id: 26, + step_index: 1, + type: "step" + }, + { + created_at: "2025-09-30T08:31:29", + started_at: null, + failure: null, + step_id: 10, + id: 178, + updated_at: "2025-09-30T08:31:29", + status: "PENDING", + finished_at: null, + run_id: 26, + step_index: 2, + type: "step" + }, + { + created_at: "2025-09-30T08:31:29", + started_at: null, + failure: null, + step_id: 11, + id: 179, + updated_at: "2025-09-30T08:31:29", + status: "PENDING", + finished_at: null, + run_id: 26, + step_index: 3, + type: "step" + }, + { + created_at: "2025-09-30T08:31:29", + started_at: null, + failure: null, + step_id: 12, + id: 180, + updated_at: "2025-09-30T08:31:29", + status: "PENDING", + finished_at: null, + run_id: 26, + step_index: 4, + type: "step" + }, + { + created_at: "2025-09-30T08:31:29", + started_at: null, + failure: null, + step_id: 13, + id: 181, + updated_at: "2025-09-30T08:31:29", + status: "PENDING", + finished_at: null, + run_id: 26, + step_index: 5, + type: "step" + } + ], + … + ] + }, + // Pagination URLs + links: { + self: "https://rpa-process-overview.local.itkdev.dk/group/11/overview/24/data?page=1&size=5", + next: "https://rpa-process-overview.local.itkdev.dk/group/11/overview/24/data?page=2&size=5" + }, + meta: { + // The total number of rows + total: 12 + } +} +``` + +[^1]: We present the data as [JSON5](https://json5.org) for improved readability. diff --git a/fixtures/process_overview.yaml b/fixtures/process_overview.yaml index 61ff89d..0bfc19d 100644 --- a/fixtures/process_overview.yaml +++ b/fixtures/process_overview.yaml @@ -1,3 +1,20 @@ +App\Entity\DataSource: + data_source_api: + label: API mock + url: http://api:8000 + options: |- + auth: + header: + x-api-key: a-not-so-secret-key + + data_source_api_2: + label: API mock (the same, but another) + url: http://api:8000 + options: |- + auth: + header: + x-api-key: a-not-so-secret-key + App\Entity\ProcessOverviewGroup: process_overview_group_1: label: Tandpleje - Udskrivningsprocesser @@ -9,11 +26,9 @@ App\Entity\ProcessOverview: process_overview_1: label: Udskrivning 22 år group: "@process_overview_group_1" + dataSource: "@data_source_api" + processId: 1 options: |- - data_source: - url: http://faker:3030/v1/process/overview/1 - # auth: - # basic: … metadata_columns: - label: Borger data: metadata.cpr @@ -22,32 +37,31 @@ App\Entity\ProcessOverview: # Missing # - label: Klinik # data: metadata.klinik + data: + default_query: + status: + - FAILED process_overview_2: label: Frit valg 0-15 år group: "@process_overview_group_1" + dataSource: "@data_source_api" + processId: 2 options: |- - data_source: - url: http://faker:3030/v1/process/overview/2 - # auth: - # basic: … metadata_columns: - label: Barn - data: metadata.cpr + data: meta.cpr - label: Name - data: metadata.name - # Missing - # - label: Klinik - # data: metadata.klinik + data: meta.name + - label: Klinik + data: meta.branch process_overview_3: label: En anden proces group: "@process_overview_group_2" + dataSource: "@data_source_api_2" + processId: 3 options: |- - data_source: - url: http://faker:3030/v1/process/overview/3 - # auth: - # basic: … metadata_columns: - label: Barn data: metadata.cpr @@ -56,3 +70,9 @@ App\Entity\ProcessOverview: # Missing # - label: Klinik # data: metadata.klinik + + process_overview_incomplete: + label: Incomplete (missing process ID) + group: "@process_overview_group_1" + dataSource: "@data_source_api" + options: |- diff --git a/fixtures/user.yaml b/fixtures/user.yaml new file mode 100644 index 0000000..0f85688 --- /dev/null +++ b/fixtures/user.yaml @@ -0,0 +1,10 @@ +App\Entity\User: + overview-manager: + email: overview-manager@example.com + roles: + - ROLE_OVERVIEW_MANAGER + + user: + email: user@example.com + roles: + - ROLE_USER diff --git a/migrations/Version20250916135221.php b/migrations/Version20250916135221.php deleted file mode 100644 index 8f15ef2..0000000 --- a/migrations/Version20250916135221.php +++ /dev/null @@ -1,39 +0,0 @@ -addSql('CREATE TABLE rpa_process_overview_process (id INT AUTO_INCREMENT NOT NULL, name VARCHAR(255) NOT NULL, rank INT NOT NULL, process_id INT NOT NULL, INDEX IDX_13CB2757EC2F574 (process_id), PRIMARY KEY (id)) DEFAULT CHARACTER SET utf8'); - $this->addSql('CREATE TABLE rpa_process_overview_process_overview (id INT AUTO_INCREMENT NOT NULL, label VARCHAR(255) NOT NULL, options LONGTEXT NOT NULL, group_id INT NOT NULL, INDEX IDX_437BDF18FE54D947 (group_id), PRIMARY KEY (id)) DEFAULT CHARACTER SET utf8'); - $this->addSql('CREATE TABLE rpa_process_overview_process_overview_group (id INT AUTO_INCREMENT NOT NULL, label VARCHAR(255) NOT NULL, PRIMARY KEY (id)) DEFAULT CHARACTER SET utf8'); - $this->addSql('ALTER TABLE rpa_process_overview_process ADD CONSTRAINT FK_13CB2757EC2F574 FOREIGN KEY (process_id) REFERENCES rpa_process_overview_process_overview (id)'); - $this->addSql('ALTER TABLE rpa_process_overview_process_overview ADD CONSTRAINT FK_437BDF18FE54D947 FOREIGN KEY (group_id) REFERENCES rpa_process_overview_process_overview_group (id)'); - } - - public function down(Schema $schema): void - { - // this down() migration is auto-generated, please modify it to your needs - $this->addSql('ALTER TABLE rpa_process_overview_process DROP FOREIGN KEY FK_13CB2757EC2F574'); - $this->addSql('ALTER TABLE rpa_process_overview_process_overview DROP FOREIGN KEY FK_437BDF18FE54D947'); - $this->addSql('DROP TABLE rpa_process_overview_process'); - $this->addSql('DROP TABLE rpa_process_overview_process_overview'); - $this->addSql('DROP TABLE rpa_process_overview_process_overview_group'); - } -} diff --git a/migrations/Version20250926120922.php b/migrations/Version20250926120922.php new file mode 100644 index 0000000..8b5337a --- /dev/null +++ b/migrations/Version20250926120922.php @@ -0,0 +1,40 @@ +addSql('CREATE TABLE data_source (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, label VARCHAR(255) NOT NULL, options CLOB NOT NULL)'); + $this->addSql('CREATE TABLE rpa_process_overview_process (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, name VARCHAR(255) NOT NULL, rank INTEGER NOT NULL, process_id INTEGER NOT NULL, CONSTRAINT FK_13CB2757EC2F574 FOREIGN KEY (process_id) REFERENCES rpa_process_overview_process_overview (id) NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('CREATE INDEX IDX_13CB2757EC2F574 ON rpa_process_overview_process (process_id)'); + $this->addSql('CREATE TABLE rpa_process_overview_process_overview (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, label VARCHAR(255) NOT NULL, options CLOB DEFAULT NULL, process_id VARCHAR(255) DEFAULT NULL, group_id INTEGER NOT NULL, data_source_id INTEGER NOT NULL, CONSTRAINT FK_437BDF18FE54D947 FOREIGN KEY (group_id) REFERENCES rpa_process_overview_process_overview_group (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_437BDF181A935C57 FOREIGN KEY (data_source_id) REFERENCES data_source (id) NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('CREATE INDEX IDX_437BDF18FE54D947 ON rpa_process_overview_process_overview (group_id)'); + $this->addSql('CREATE INDEX IDX_437BDF181A935C57 ON rpa_process_overview_process_overview (data_source_id)'); + $this->addSql('CREATE TABLE rpa_process_overview_process_overview_group (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, label VARCHAR(255) NOT NULL)'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('DROP TABLE data_source'); + $this->addSql('DROP TABLE rpa_process_overview_process'); + $this->addSql('DROP TABLE rpa_process_overview_process_overview'); + $this->addSql('DROP TABLE rpa_process_overview_process_overview_group'); + } +} diff --git a/migrations/Version20250926123121.php b/migrations/Version20250926123121.php new file mode 100644 index 0000000..6be7ff2 --- /dev/null +++ b/migrations/Version20250926123121.php @@ -0,0 +1,35 @@ +addSql('ALTER TABLE data_source ADD COLUMN url VARCHAR(255) NOT NULL'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('CREATE TEMPORARY TABLE __temp__data_source AS SELECT id, label, options FROM data_source'); + $this->addSql('DROP TABLE data_source'); + $this->addSql('CREATE TABLE data_source (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, label VARCHAR(255) NOT NULL, options CLOB NOT NULL)'); + $this->addSql('INSERT INTO data_source (id, label, options) SELECT id, label, options FROM __temp__data_source'); + $this->addSql('DROP TABLE __temp__data_source'); + } +} diff --git a/migrations/Version20250926123652.php b/migrations/Version20250926123652.php new file mode 100644 index 0000000..6257e8b --- /dev/null +++ b/migrations/Version20250926123652.php @@ -0,0 +1,39 @@ +addSql('CREATE TEMPORARY TABLE __temp__data_source AS SELECT id, label, options, url FROM data_source'); + $this->addSql('DROP TABLE data_source'); + $this->addSql('CREATE TABLE data_source (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, label VARCHAR(255) NOT NULL, options CLOB DEFAULT NULL, url VARCHAR(255) NOT NULL)'); + $this->addSql('INSERT INTO data_source (id, label, options, url) SELECT id, label, options, url FROM __temp__data_source'); + $this->addSql('DROP TABLE __temp__data_source'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('CREATE TEMPORARY TABLE __temp__data_source AS SELECT id, label, options, url FROM data_source'); + $this->addSql('DROP TABLE data_source'); + $this->addSql('CREATE TABLE data_source (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, label VARCHAR(255) NOT NULL, options CLOB NOT NULL, url VARCHAR(255) NOT NULL)'); + $this->addSql('INSERT INTO data_source (id, label, options, url) SELECT id, label, options, url FROM __temp__data_source'); + $this->addSql('DROP TABLE __temp__data_source'); + } +} diff --git a/migrations/Version20251001122515.php b/migrations/Version20251001122515.php new file mode 100644 index 0000000..165de4b --- /dev/null +++ b/migrations/Version20251001122515.php @@ -0,0 +1,32 @@ +addSql('CREATE TABLE user (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, email VARCHAR(180) NOT NULL, roles CLOB NOT NULL, password VARCHAR(255) NOT NULL, created_by VARCHAR(255) DEFAULT NULL, updated_by VARCHAR(255) DEFAULT NULL, created_at DATETIME NOT NULL, updated_at DATETIME NOT NULL)'); + $this->addSql('CREATE UNIQUE INDEX UNIQ_IDENTIFIER_EMAIL ON user (email)'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('DROP TABLE user'); + } +} diff --git a/migrations/Version20251001123027.php b/migrations/Version20251001123027.php new file mode 100644 index 0000000..8af73f0 --- /dev/null +++ b/migrations/Version20251001123027.php @@ -0,0 +1,58 @@ +addSql('ALTER TABLE data_source ADD COLUMN created_by VARCHAR(255) DEFAULT NULL'); + $this->addSql('ALTER TABLE data_source ADD COLUMN updated_by VARCHAR(255) DEFAULT NULL'); + $this->addSql('ALTER TABLE data_source ADD COLUMN created_at DATETIME NOT NULL'); + $this->addSql('ALTER TABLE data_source ADD COLUMN updated_at DATETIME NOT NULL'); + $this->addSql('ALTER TABLE rpa_process_overview_process_overview ADD COLUMN created_by VARCHAR(255) DEFAULT NULL'); + $this->addSql('ALTER TABLE rpa_process_overview_process_overview ADD COLUMN updated_by VARCHAR(255) DEFAULT NULL'); + $this->addSql('ALTER TABLE rpa_process_overview_process_overview ADD COLUMN created_at DATETIME NOT NULL'); + $this->addSql('ALTER TABLE rpa_process_overview_process_overview ADD COLUMN updated_at DATETIME NOT NULL'); + $this->addSql('ALTER TABLE rpa_process_overview_process_overview_group ADD COLUMN created_by VARCHAR(255) DEFAULT NULL'); + $this->addSql('ALTER TABLE rpa_process_overview_process_overview_group ADD COLUMN updated_by VARCHAR(255) DEFAULT NULL'); + $this->addSql('ALTER TABLE rpa_process_overview_process_overview_group ADD COLUMN created_at DATETIME NOT NULL'); + $this->addSql('ALTER TABLE rpa_process_overview_process_overview_group ADD COLUMN updated_at DATETIME NOT NULL'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('CREATE TEMPORARY TABLE __temp__data_source AS SELECT id, label, options, url FROM data_source'); + $this->addSql('DROP TABLE data_source'); + $this->addSql('CREATE TABLE data_source (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, label VARCHAR(255) NOT NULL, options CLOB DEFAULT NULL, url VARCHAR(255) NOT NULL)'); + $this->addSql('INSERT INTO data_source (id, label, options, url) SELECT id, label, options, url FROM __temp__data_source'); + $this->addSql('DROP TABLE __temp__data_source'); + $this->addSql('CREATE TEMPORARY TABLE __temp__rpa_process_overview_process_overview AS SELECT id, label, options, process_id, group_id, data_source_id FROM rpa_process_overview_process_overview'); + $this->addSql('DROP TABLE rpa_process_overview_process_overview'); + $this->addSql('CREATE TABLE rpa_process_overview_process_overview (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, label VARCHAR(255) NOT NULL, options CLOB DEFAULT NULL, process_id VARCHAR(255) DEFAULT NULL, group_id INTEGER NOT NULL, data_source_id INTEGER NOT NULL, CONSTRAINT FK_437BDF18FE54D947 FOREIGN KEY (group_id) REFERENCES rpa_process_overview_process_overview_group (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_437BDF181A935C57 FOREIGN KEY (data_source_id) REFERENCES data_source (id) NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO rpa_process_overview_process_overview (id, label, options, process_id, group_id, data_source_id) SELECT id, label, options, process_id, group_id, data_source_id FROM __temp__rpa_process_overview_process_overview'); + $this->addSql('DROP TABLE __temp__rpa_process_overview_process_overview'); + $this->addSql('CREATE INDEX IDX_437BDF18FE54D947 ON rpa_process_overview_process_overview (group_id)'); + $this->addSql('CREATE INDEX IDX_437BDF181A935C57 ON rpa_process_overview_process_overview (data_source_id)'); + $this->addSql('CREATE TEMPORARY TABLE __temp__rpa_process_overview_process_overview_group AS SELECT id, label FROM rpa_process_overview_process_overview_group'); + $this->addSql('DROP TABLE rpa_process_overview_process_overview_group'); + $this->addSql('CREATE TABLE rpa_process_overview_process_overview_group (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, label VARCHAR(255) NOT NULL)'); + $this->addSql('INSERT INTO rpa_process_overview_process_overview_group (id, label) SELECT id, label FROM __temp__rpa_process_overview_process_overview_group'); + $this->addSql('DROP TABLE __temp__rpa_process_overview_process_overview_group'); + } +} diff --git a/migrations/Version20251003082431.php b/migrations/Version20251003082431.php new file mode 100644 index 0000000..40aee35 --- /dev/null +++ b/migrations/Version20251003082431.php @@ -0,0 +1,36 @@ +addSql('CREATE TEMPORARY TABLE __temp__user AS SELECT id, email, roles, created_by, updated_by, created_at, updated_at FROM user'); + $this->addSql('DROP TABLE user'); + $this->addSql('CREATE TABLE user (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, email VARCHAR(180) NOT NULL, roles CLOB NOT NULL, created_by VARCHAR(255) DEFAULT NULL, updated_by VARCHAR(255) DEFAULT NULL, created_at DATETIME NOT NULL, updated_at DATETIME NOT NULL)'); + $this->addSql('INSERT INTO user (id, email, roles, created_by, updated_by, created_at, updated_at) SELECT id, email, roles, created_by, updated_by, created_at, updated_at FROM __temp__user'); + $this->addSql('DROP TABLE __temp__user'); + $this->addSql('CREATE UNIQUE INDEX UNIQ_IDENTIFIER_EMAIL ON user (email)'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('ALTER TABLE user ADD COLUMN password VARCHAR(255) NOT NULL'); + } +} diff --git a/mocks/process-overview-1.json b/mocks/process-overview-1.json deleted file mode 100644 index a1c78e6..0000000 --- a/mocks/process-overview-1.json +++ /dev/null @@ -1,171 +0,0 @@ -{ - "request": { - "method": "GET", - "path": "/v1/process/overview/1" - }, - "response": { - "body": [ - { - "process_id": "1", - "person": { - "cpr": "0101011234", - "name": "Anna Hansen" - }, - "metadata": { - "cpr": "0101011234", - "name": "Anna Hansen" - }, - "steps": [ - { - "id": "1", - "name": "Digital Post udsendt", - "status": "SUCCESS", - "started_at": "2025-09-15T10:00:00Z", - "finished_at": "2025-09-15T10:05:00Z", - "failure": null - }, - { - "id": "2", - "name": "Formular indsendt", - "status": "FAILED", - "started_at": "2025-09-15T10:05:10Z", - "finished_at": "2025-09-15T10:10:00Z", - "failure": { - "code": "Fejlkode", - "message": "Fejlbesked", - "retryable": true, - "occurred_at": "2025-09-15T10:10:00Z" - } - }, - { - "id": "3", - "name": "Tandklinik registreret i Solteq", - "status": "PENDING", - "started_at": null, - "finished_at": null, - "failure": null - }, - { - "id": "4", - "name": "Formular journaliseret", - "status": "PENDING", - "started_at": null, - "finished_at": null, - "failure": null - }, - { - "id": "5", - "name": "Borger fyldt 22 år", - "status": "PENDING", - "started_at": null, - "finished_at": null, - "failure": null - }, - { - "id": "6", - "name": "Udskrivning godkendt", - "status": "PENDING", - "started_at": null, - "finished_at": null, - "failure": null - }, - { - "id": "7", - "name": "Journal og røntgen afleveret og journaliseret", - "status": "PENDING", - "started_at": null, - "finished_at": null, - "failure": null - }, - { - "id": "8", - "name": "Journal arkiveret", - "status": "PENDING", - "started_at": null, - "finished_at": null, - "failure": null - } - ], - "updated_at": "2025-09-15T10:10:00Z" - }, - { - "process_id": "2", - "person": { - "cpr_masked": "0202025678", - "name": "Peter Nielsen" - }, - "metadata": { - "cpr_masked": "0202025678", - "name": "Peter Nielsen" - }, - "steps": [ - { - "id": "1", - "name": "Digital Post udsendt", - "status": "SUCCESS", - "started_at": "2025-09-14T09:30:00Z", - "finished_at": "2025-09-14T09:35:00Z", - "failure": null - }, - { - "id": "2", - "name": "Formular indsendt", - "status": "SUCCESS", - "started_at": "2025-09-14T09:35:10Z", - "finished_at": "2025-09-14T09:40:00Z", - "failure": null - }, - { - "id": "3", - "name": "Tandklinik registreret i Solteq", - "status": "PENDING", - "started_at": null, - "finished_at": null, - "failure": null - }, - { - "id": "4", - "name": "Formular journaliseret", - "status": "PENDING", - "started_at": null, - "finished_at": null, - "failure": null - }, - { - "id": "5", - "name": "Borger fyldt 22 år", - "status": "PENDING", - "started_at": null, - "finished_at": null, - "failure": null - }, - { - "id": "6", - "name": "Udskrivning godkendt", - "status": "PENDING", - "started_at": null, - "finished_at": null, - "failure": null - }, - { - "id": "7", - "name": "Journal og røntgen afleveret og journaliseret", - "status": "PENDING", - "started_at": null, - "finished_at": null, - "failure": null - }, - { - "id": "8", - "name": "Journal arkiveret", - "status": "PENDING", - "started_at": null, - "finished_at": null, - "failure": null - } - ], - "updated_at": "2025-09-14T09:40:00Z" - } - ] - } -} diff --git a/mocks/process-overview-2.json b/mocks/process-overview-2.json deleted file mode 100644 index 1bce130..0000000 --- a/mocks/process-overview-2.json +++ /dev/null @@ -1,171 +0,0 @@ -{ - "request": { - "method": "GET", - "path": "/v1/process/overview/2" - }, - "response": { - "body": [ - { - "process_id": "1", - "person": { - "cpr": "0101011234", - "name": "Anna Hansen" - }, - "metadata": { - "cpr": "0101011234", - "name": "Anna Hansen" - }, - "steps": [ - { - "id": "1", - "name": "Digital Post udsendt", - "status": "SUCCESS", - "started_at": "2025-09-15T10:00:00Z", - "finished_at": "2025-09-15T10:05:00Z", - "failure": null - }, - { - "id": "2", - "name": "Formular indsendt", - "status": "FAILED", - "started_at": "2025-09-15T10:05:10Z", - "finished_at": "2025-09-15T10:10:00Z", - "failure": { - "code": "Fejlkode", - "message": "Fejlbesked", - "retryable": true, - "occurred_at": "2025-09-15T10:10:00Z" - } - }, - { - "id": "3", - "name": "Tandklinik registreret i Solteq", - "status": "PENDING", - "started_at": null, - "finished_at": null, - "failure": null - }, - { - "id": "4", - "name": "Formular journaliseret", - "status": "PENDING", - "started_at": null, - "finished_at": null, - "failure": null - }, - { - "id": "5", - "name": "Borger fyldt 22 år", - "status": "PENDING", - "started_at": null, - "finished_at": null, - "failure": null - }, - { - "id": "6", - "name": "Udskrivning godkendt", - "status": "PENDING", - "started_at": null, - "finished_at": null, - "failure": null - }, - { - "id": "7", - "name": "Journal og røntgen afleveret og journaliseret", - "status": "PENDING", - "started_at": null, - "finished_at": null, - "failure": null - }, - { - "id": "8", - "name": "Journal arkiveret", - "status": "PENDING", - "started_at": null, - "finished_at": null, - "failure": null - } - ], - "updated_at": "2025-09-15T10:10:00Z" - }, - { - "process_id": "2", - "person": { - "cpr_masked": "0202025678", - "name": "Peter Nielsen" - }, - "metadata": { - "cpr_masked": "0202025678", - "name": "Peter Nielsen" - }, - "steps": [ - { - "id": "1", - "name": "Digital Post udsendt", - "status": "SUCCESS", - "started_at": "2025-09-14T09:30:00Z", - "finished_at": "2025-09-14T09:35:00Z", - "failure": null - }, - { - "id": "2", - "name": "Formular indsendt", - "status": "SUCCESS", - "started_at": "2025-09-14T09:35:10Z", - "finished_at": "2025-09-14T09:40:00Z", - "failure": null - }, - { - "id": "3", - "name": "Tandklinik registreret i Solteq", - "status": "PENDING", - "started_at": null, - "finished_at": null, - "failure": null - }, - { - "id": "4", - "name": "Formular journaliseret", - "status": "PENDING", - "started_at": null, - "finished_at": null, - "failure": null - }, - { - "id": "5", - "name": "Borger fyldt 22 år", - "status": "PENDING", - "started_at": null, - "finished_at": null, - "failure": null - }, - { - "id": "6", - "name": "Udskrivning godkendt", - "status": "PENDING", - "started_at": null, - "finished_at": null, - "failure": null - }, - { - "id": "7", - "name": "Journal og røntgen afleveret og journaliseret", - "status": "PENDING", - "started_at": null, - "finished_at": null, - "failure": null - }, - { - "id": "8", - "name": "Journal arkiveret", - "status": "PENDING", - "started_at": null, - "finished_at": null, - "failure": null - } - ], - "updated_at": "2025-09-14T09:40:00Z" - } - ] - } -} diff --git a/mocks/process-overview-3.json b/mocks/process-overview-3.json deleted file mode 100644 index 537b993..0000000 --- a/mocks/process-overview-3.json +++ /dev/null @@ -1,171 +0,0 @@ -{ - "request": { - "method": "GET", - "path": "/v1/process/overview/3" - }, - "response": { - "body": [ - { - "process_id": "1", - "person": { - "cpr": "0101011234", - "name": "Anna Hansen" - }, - "metadata": { - "cpr": "0101011234", - "name": "Anna Hansen" - }, - "steps": [ - { - "id": "1", - "name": "Digital Post udsendt", - "status": "SUCCESS", - "started_at": "2025-09-15T10:00:00Z", - "finished_at": "2025-09-15T10:05:00Z", - "failure": null - }, - { - "id": "2", - "name": "Formular indsendt", - "status": "FAILED", - "started_at": "2025-09-15T10:05:10Z", - "finished_at": "2025-09-15T10:10:00Z", - "failure": { - "code": "Fejlkode", - "message": "Fejlbesked", - "retryable": true, - "occurred_at": "2025-09-15T10:10:00Z" - } - }, - { - "id": "3", - "name": "Tandklinik registreret i Solteq", - "status": "PENDING", - "started_at": null, - "finished_at": null, - "failure": null - }, - { - "id": "4", - "name": "Formular journaliseret", - "status": "PENDING", - "started_at": null, - "finished_at": null, - "failure": null - }, - { - "id": "5", - "name": "Borger fyldt 22 år", - "status": "PENDING", - "started_at": null, - "finished_at": null, - "failure": null - }, - { - "id": "6", - "name": "Udskrivning godkendt", - "status": "PENDING", - "started_at": null, - "finished_at": null, - "failure": null - }, - { - "id": "7", - "name": "Journal og røntgen afleveret og journaliseret", - "status": "PENDING", - "started_at": null, - "finished_at": null, - "failure": null - }, - { - "id": "8", - "name": "Journal arkiveret", - "status": "PENDING", - "started_at": null, - "finished_at": null, - "failure": null - } - ], - "updated_at": "2025-09-15T10:10:00Z" - }, - { - "process_id": "2", - "person": { - "cpr_masked": "0202025678", - "name": "Peter Nielsen" - }, - "metadata": { - "cpr_masked": "0202025678", - "name": "Peter Nielsen" - }, - "steps": [ - { - "id": "1", - "name": "Digital Post udsendt", - "status": "SUCCESS", - "started_at": "2025-09-14T09:30:00Z", - "finished_at": "2025-09-14T09:35:00Z", - "failure": null - }, - { - "id": "2", - "name": "Formular indsendt", - "status": "SUCCESS", - "started_at": "2025-09-14T09:35:10Z", - "finished_at": "2025-09-14T09:40:00Z", - "failure": null - }, - { - "id": "3", - "name": "Tandklinik registreret i Solteq", - "status": "PENDING", - "started_at": null, - "finished_at": null, - "failure": null - }, - { - "id": "4", - "name": "Formular journaliseret", - "status": "PENDING", - "started_at": null, - "finished_at": null, - "failure": null - }, - { - "id": "5", - "name": "Borger fyldt 22 år", - "status": "PENDING", - "started_at": null, - "finished_at": null, - "failure": null - }, - { - "id": "6", - "name": "Udskrivning godkendt", - "status": "PENDING", - "started_at": null, - "finished_at": null, - "failure": null - }, - { - "id": "7", - "name": "Journal og røntgen afleveret og journaliseret", - "status": "PENDING", - "started_at": null, - "finished_at": null, - "failure": null - }, - { - "id": "8", - "name": "Journal arkiveret", - "status": "PENDING", - "started_at": null, - "finished_at": null, - "failure": null - } - ], - "updated_at": "2025-09-14T09:40:00Z" - } - ] - } -} diff --git a/mocks/process-overview-4.json b/mocks/process-overview-4.json deleted file mode 100644 index f0aef44..0000000 --- a/mocks/process-overview-4.json +++ /dev/null @@ -1,171 +0,0 @@ -{ - "request": { - "method": "GET", - "path": "/v1/process/overview/4" - }, - "response": { - "body": [ - { - "process_id": "1", - "person": { - "cpr": "0101011234", - "name": "Anna Hansen" - }, - "metadata": { - "cpr": "0101011234", - "name": "Anna Hansen" - }, - "steps": [ - { - "id": "1", - "name": "Digital Post udsendt", - "status": "SUCCESS", - "started_at": "2025-09-15T10:00:00Z", - "finished_at": "2025-09-15T10:05:00Z", - "failure": null - }, - { - "id": "2", - "name": "Formular indsendt", - "status": "FAILED", - "started_at": "2025-09-15T10:05:10Z", - "finished_at": "2025-09-15T10:10:00Z", - "failure": { - "code": "Fejlkode", - "message": "Fejlbesked", - "retryable": true, - "occurred_at": "2025-09-15T10:10:00Z" - } - }, - { - "id": "3", - "name": "Tandklinik registreret i Solteq", - "status": "PENDING", - "started_at": null, - "finished_at": null, - "failure": null - }, - { - "id": "4", - "name": "Formular journaliseret", - "status": "PENDING", - "started_at": null, - "finished_at": null, - "failure": null - }, - { - "id": "5", - "name": "Borger fyldt 22 år", - "status": "PENDING", - "started_at": null, - "finished_at": null, - "failure": null - }, - { - "id": "6", - "name": "Udskrivning godkendt", - "status": "PENDING", - "started_at": null, - "finished_at": null, - "failure": null - }, - { - "id": "7", - "name": "Journal og røntgen afleveret og journaliseret", - "status": "PENDING", - "started_at": null, - "finished_at": null, - "failure": null - }, - { - "id": "8", - "name": "Journal arkiveret", - "status": "PENDING", - "started_at": null, - "finished_at": null, - "failure": null - } - ], - "updated_at": "2025-09-15T10:10:00Z" - }, - { - "process_id": "2", - "person": { - "cpr_masked": "0202025678", - "name": "Peter Nielsen" - }, - "metadata": { - "cpr_masked": "0202025678", - "name": "Peter Nielsen" - }, - "steps": [ - { - "id": "1", - "name": "Digital Post udsendt", - "status": "SUCCESS", - "started_at": "2025-09-14T09:30:00Z", - "finished_at": "2025-09-14T09:35:00Z", - "failure": null - }, - { - "id": "2", - "name": "Formular indsendt", - "status": "SUCCESS", - "started_at": "2025-09-14T09:35:10Z", - "finished_at": "2025-09-14T09:40:00Z", - "failure": null - }, - { - "id": "3", - "name": "Tandklinik registreret i Solteq", - "status": "PENDING", - "started_at": null, - "finished_at": null, - "failure": null - }, - { - "id": "4", - "name": "Formular journaliseret", - "status": "PENDING", - "started_at": null, - "finished_at": null, - "failure": null - }, - { - "id": "5", - "name": "Borger fyldt 22 år", - "status": "PENDING", - "started_at": null, - "finished_at": null, - "failure": null - }, - { - "id": "6", - "name": "Udskrivning godkendt", - "status": "PENDING", - "started_at": null, - "finished_at": null, - "failure": null - }, - { - "id": "7", - "name": "Journal og røntgen afleveret og journaliseret", - "status": "PENDING", - "started_at": null, - "finished_at": null, - "failure": null - }, - { - "id": "8", - "name": "Journal arkiveret", - "status": "PENDING", - "started_at": null, - "finished_at": null, - "failure": null - } - ], - "updated_at": "2025-09-14T09:40:00Z" - } - ] - } -} diff --git a/mocks/process.json b/mocks/process.json deleted file mode 100644 index 22154f4..0000000 --- a/mocks/process.json +++ /dev/null @@ -1,84 +0,0 @@ -{ - "request": { - "method": "GET", - "path": "/v1/process" - }, - "response": { - "body": { - "data": [ - { - "id": "1", - "name": "Udskrivning 22 år", - "metadata": { - "cpr": "string", - "name": "string" - }, - "steps": [ - { - "id": "1", - "name": "Digital Post udsendt" - }, - { - "id": "2", - "name": "Formular indsendt" - }, - { - "id": "3", - "name": "Tandklinik registreret i Solteq" - }, - { - "id": "4", - "name": "Formular journaliseret" - }, - { - "id": "5", - "name": "Borger fyldt 22 år" - }, - { - "id": "6", - "name": "Udskrivning godkendt" - }, - { - "id": "7", - "name": "Journal og røntgen afleveret og journaliseret" - }, - { - "id": "8", - "name": "Journal arkiveret" - } - ], - "links": { - "overview": "/v1/process/overview/1" - } - }, - { - "id": "2", - "name": "Frit valg 0-15 år", - "steps": [], - "links": { - "overview": "/v1/process/overview/2" - } - }, - { - "id": "3", - "name": "Frit valg +16 år", - "steps": [], - "links": { - "overview": "/v1/process/overview/3" - } - }, - { - "id": "4", - "name": "Tilflytter til Aarhus Kommune", - "steps": [], - "links": { - "overview": "/v1/process/overview/4" - } - } - ], - "meta": { - "updated_at": "2025-09-14T09:40:00Z" - } - } - } -} diff --git a/public/widgets/ProcessOverview.min.js b/public/widgets/ProcessOverview.min.js new file mode 100644 index 0000000..0c8d3f4 --- /dev/null +++ b/public/widgets/ProcessOverview.min.js @@ -0,0 +1 @@ +(function(pn){typeof define=="function"&&define.amd?define(pn):pn()})(function(){"use strict";var gn=Array.isArray,Le=Array.prototype.indexOf,wn=Array.from,mn=Object.defineProperty,yt=Object.getOwnPropertyDescriptor,Ie=Object.prototype,Fe=Array.prototype,Re=Object.getPrototypeOf,Kn=Object.isExtensible;const Be=()=>{},Dt=16,Qt=32,Vn=64,G=256,yn=512,A=1024,nt=2048,st=4096,H=8192,xt=16384,xn=32768,$t=65536,Ue=1<<17,Hn=1<<19,bn=1<<21,Te=1<<22,ft=1<<23,Xt=Symbol("$state"),We=Symbol("legacy props"),_n=new class extends Error{name="StaleReactionError";message="The reaction that called `getAbortSignal()` was re-run or destroyed"},M=Symbol();function Jn(t){return t===this.v}function Qn(t){return n=t,e=this.v,!(n!=n?e==e:n!==e||n!==null&&typeof n=="object"||typeof n=="function");var n,e}let qt=!1,j=null;function Yt(t){j=t}function tn(t,n=!1,e){j={p:j,c:null,e:null,s:t,x:null,l:qt&&!n?{s:null,u:null,$:[]}:null}}function nn(t){var n=j,e=n.e;if(e!==null)for(var r of(n.e=null,e))ve(r);return j=n.p,{}}function Lt(){return!qt||j!==null&&j.l===null}const Ze=new WeakMap;function kn(t,n){for(;n!==null;){if(128&n.f)try{return void n.b.error(t)}catch(e){t=e}n=n.parent}throw t instanceof Error&&Xn(t),t}function Xn(t){const n=Ze.get(t);n&&(mn(t,"message",{value:n.message}),mn(t,"stack",{value:n.stack}))}let bt=[];function Yn(t){if(bt.length===0){var n=bt;queueMicrotask(()=>{n===bt&&(function(){var e=bt;bt=[],(function(r){for(var l=0;l{try{var u=t();i&&Promise.resolve(u).catch(()=>{})}catch(f){u=Promise.reject(f)}var a=()=>u;l=i?.then(a,a)??Promise.resolve(u),i=l;var s=D,d=r.is_pending();c&&(r.update_pending_count(1),d||s.increment());const v=(f,p=void 0)=>{i=null,d||s.activate(),p?p!==_n&&(o.f|=ft,Rt(o,p)):((o.f&ft)!==0&&(o.f^=ft),Rt(o,f)),c&&(r.update_pending_count(-1),d||s.decrement()),re()};if(l.then(v,f=>v(null,f||"unknown")),s)return()=>{queueMicrotask(()=>s.neuter())}}),new Promise(u=>{(function a(s){function d(){s===l?u(o):a(l)}s.then(d,d)})(l)})}function te(t){const n=Cn(t);return xe(n),n}function Sn(t){const n=Cn(t);return n.equals=Qn,n}function ne(t){var n=t.effects;if(n!==null){t.effects=null;for(var e=0;eGe(v))).then(v=>{u?.activate(),s();try{e([...t.map(r),...v])}catch(f){(a.f&xt)===0&&kn(f,a)}u?.deactivate(),re()}).catch(v=>{d.error(v)})}else e(t.map(r))}function re(){ot(null),Q(null),Yt(null)}const Nn=new Set;let D=null,le=new Set,It=[],Pn=null,Mn=!1;class _t{current=new Map;#a=new Map;#l=new Set;#t=0;#s=null;#f=!1;#e=[];#o=[];#r=[];#n=[];#u=[];#c=[];#v=[];skipped_effects=new Set;process(n){It=[];for(const l of n)this.#h(l);if(this.#e.length===0&&this.#t===0){this.#d();var e=this.#r,r=this.#n;this.#r=[],this.#n=[],this.#u=[],D=null,oe(e),oe(r),D===null?D=this:Nn.delete(this),this.#s?.resolve()}else this.#i(this.#r),this.#i(this.#n),this.#i(this.#u);for(const l of this.#e)Pt(l);for(const l of this.#o)Pt(l);this.#e=[],this.#o=[]}#h(n){n.f^=A;for(var e=n.first;e!==null;){var r=e.f,l=!!(96&r);if(!(l&&(r&A)!==0||(r&H)!==0||this.skipped_effects.has(e))&&e.fn!==null){l?e.f^=A:4&r?this.#n.push(e):(r&A)===0&&((r&Te)!==0?(e.b?.is_pending()?this.#o:this.#e).push(e):un(e)&&((e.f&Dt)!==0&&this.#u.push(e),Pt(e)));var o=e.first;if(o!==null){e=o;continue}}var i=e.parent;for(e=e.next;e===null&&i!==null;)e=i.next,i=i.parent}}#i(n){for(const e of n)((e.f&nt)!==0?this.#c:this.#v).push(e),R(e,A);n.length=0}capture(n,e){this.#a.has(n)||this.#a.set(n,e),this.current.set(n,n.v)}activate(){D=this}deactivate(){D=null;for(const n of le)if(le.delete(n),n(),D!==null)break}neuter(){this.#f=!0}flush(){It.length>0?(function(){var n=Nt;Mn=!0;try{var e=0;for(me(!0);It.length>0;){var r=_t.ensure();e++>1e3&&Ve(),r.process(It),lt.clear()}}finally{Mn=!1,me(n),Pn=null}})():this.#d(),D===this&&(this.#t===0&&Nn.delete(this),this.deactivate())}#d(){if(!this.#f)for(const n of this.#l)n();this.#l.clear()}increment(){this.#t+=1}decrement(){if(this.#t-=1,this.#t===0){for(const n of this.#c)R(n,nt),kt(n);for(const n of this.#v)R(n,st),kt(n);this.#r=[],this.#n=[],this.flush()}else this.deactivate()}add_callback(n){this.#l.add(n)}settled(){return(this.#s??={promise:new Promise((r,l)=>{n=r,e=l}),resolve:n,reject:e}).promise;var n,e}static ensure(){if(D===null){const n=D=new _t;Nn.add(D),_t.enqueue(()=>{D===n&&n.flush()})}return D}static enqueue(n){Yn(n)}}function Ve(){try{(function(){throw new Error("https://svelte.dev/e/effect_update_depth_exceeded")})()}catch(t){kn(t,Pn)}}let ct=null;function oe(t){var n=t.length;if(n!==0){for(var e=0;e0)){lt.clear();for(const l of ct)Pt(l);ct=[]}}ct=null}}function kt(t){for(var n=Pn=t;n.parent!==null;){var e=(n=n.parent).f;if(Mn&&n===y&&(e&Dt)!==0)return;if(96&e){if((e&A)===0)return;n.f^=A}}It.push(n)}const lt=new Map;function Ft(t,n){return{f:0,v:t,reactions:null,equals:Jn,rv:0,wv:0}}function U(t,n){const e=Ft(t);return xe(e),e}function I(t,n,e=!1){return x!==null&&(!ht||(x.f&Ue)!==0)&&Lt()&&4325394&x.f&&!et?.includes(t)&&(function(){throw new Error("https://svelte.dev/e/state_unsafe_mutation")})(),Rt(t,e?Ct(n):n)}function Rt(t,n){if(!t.equals(n)){var e=t.v;dt?lt.set(t,n):lt.set(t,e),t.v=n,_t.ensure().capture(t,e),2&t.f&&((t.f&nt)!==0&&En(t),R(t,(t.f&G)===0?A:st)),t.wv=ke(),ue(t,nt),!Lt()||y===null||(y.f&A)===0||96&y.f||(W===null?(function(r){W=r})([t]):W.push(t))}return n}function On(t){I(t,t.v+1)}function ue(t,n){var e=t.reactions;if(e!==null)for(var r=Lt(),l=e.length,o=0;o{if(pt===o)return c();var u=x,a=pt;Q(null),_e(o);var s=c();return Q(u),_e(a),s};return r&&e.set("length",U(t.length)),new Proxy(t,{defineProperty(c,u,a){"value"in a&&a.configurable!==!1&&a.enumerable!==!1&&a.writable!==!1||(function(){throw new Error("https://svelte.dev/e/state_descriptors_fixed")})();var s=e.get(u);return s===void 0?s=i(()=>{var d=U(a.value);return e.set(u,d),d}):I(s,a.value,!0),!0},deleteProperty(c,u){var a=e.get(u);if(a===void 0){if(u in c){const s=i(()=>U(M));e.set(u,s),On(l)}}else I(a,M),On(l);return!0},get(c,u,a){if(u===Xt)return t;var s=e.get(u),d=u in c;if(s!==void 0||d&&!yt(c,u)?.writable||(s=i(()=>U(Ct(d?c[u]:M))),e.set(u,s)),s!==void 0){var v=b(s);return v===M?void 0:v}return Reflect.get(c,u,a)},getOwnPropertyDescriptor(c,u){var a=Reflect.getOwnPropertyDescriptor(c,u);if(a&&"value"in a){var s=e.get(u);s&&(a.value=b(s))}else if(a===void 0){var d=e.get(u),v=d?.v;if(d!==void 0&&v!==M)return{enumerable:!0,configurable:!0,value:v,writable:!0}}return a},has(c,u){if(u===Xt)return!0;var a=e.get(u),s=a!==void 0&&a.v!==M||Reflect.has(c,u);return(a!==void 0||y!==null&&(!s||yt(c,u)?.writable))&&(a===void 0&&(a=i(()=>U(s?Ct(c[u]):M)),e.set(u,a)),b(a)===M)?!1:s},set(c,u,a,s){var d=e.get(u),v=u in c;if(r&&u==="length")for(var f=a;fU(M)),e.set(f+"",p))}d===void 0?v&&!yt(c,u)?.writable||(I(d=i(()=>U(void 0)),Ct(a)),e.set(u,d)):(v=d.v!==M,I(d,i(()=>Ct(a))));var h=Reflect.getOwnPropertyDescriptor(c,u);if(h?.set&&h.set.call(s,a),!v){if(r&&typeof u=="string"){var g=e.get("length"),m=Number(u);Number.isInteger(m)&&m>=g.v&&I(g,m+1)}On(l)}return!0},ownKeys(c){b(l);var u=Reflect.ownKeys(c).filter(d=>{var v=e.get(d);return v===void 0||v.v!==M});for(var[a,s]of e)s.v===M||a in c||u.push(a);return u},setPrototypeOf(){(function(){throw new Error("https://svelte.dev/e/state_prototype_fixed")})()}})}var ie,ae,se,fe;function en(t=""){return document.createTextNode(t)}function Bt(t){return se.call(t)}function rn(t){return fe.call(t)}function P(t,n){return Bt(t)}function Ut(t,n){var e=Bt(t);return e instanceof Comment&&e.data===""?rn(e):e}function St(t,n=1,e=!1){let r=t;for(;n--;)r=rn(r);return r}function ce(t){var n=x,e=y;Q(null),ot(null);try{return t()}finally{Q(n),ot(e)}}function He(t){y===null&&x===null&&(function(){throw new Error("https://svelte.dev/e/effect_orphan")})(),x!==null&&(x.f&G)!==0&&y===null&&(function(){throw new Error("https://svelte.dev/e/effect_in_unowned_derived")})(),dt&&(function(){throw new Error("https://svelte.dev/e/effect_in_teardown")})()}function Et(t,n,e,r=!0){var l=y;l!==null&&(l.f&H)!==0&&(t|=H);var o={ctx:j,deps:null,nodes_start:null,nodes_end:null,f:t|nt,first:null,fn:n,last:null,next:null,parent:l,b:l&&l.b,prev:null,teardown:null,transitions:null,wv:0,ac:null};if(e)try{Pt(o),o.f|=xn}catch(u){throw J(o),u}else n!==null&&kt(o);if(r){var i=o;if(e&&i.deps===null&&i.teardown===null&&i.nodes_start===null&&i.first===i.last&&(i.f&Hn)===0&&(i=i.first),i!==null&&(i.parent=l,l!==null&&(function(u,a){var s=a.last;s===null?a.last=a.first=u:(s.next=u,u.prev=s,a.last=u)})(i,l),x!==null&&2&x.f&&(t&Vn)===0)){var c=x;(c.effects??=[]).push(i)}}return o}function ve(t){return Et(1048580,t,!1)}function T(t,n=[],e=[]){Ke(n,e,r=>{Et(8,()=>t(...r.map(b)),!0)})}function ln(t,n=0){return Et(Dt|n,t,!0)}function vt(t,n=!0){return Et(524320,t,!0,n)}function de(t){var n=t.teardown;if(n!==null){const e=dt,r=x;ye(!0),Q(null);try{n.call(null)}finally{ye(e),Q(r)}}}function he(t,n=!1){var e=t.first;for(t.first=t.last=null;e!==null;){const l=e.ac;l!==null&&ce(()=>{l.abort(_n)});var r=e.next;(e.f&Vn)!==0?e.parent=null:J(e,n),e=r}}function J(t,n=!0){var e=!1;(n||262144&t.f)&&t.nodes_start!==null&&t.nodes_end!==null&&((function(o,i){for(;o!==null;){var c=o===i?null:rn(o);o.remove(),o=c}})(t.nodes_start,t.nodes_end),e=!0),he(t,n&&!e),an(t,0),R(t,xt);var r=t.transitions;if(r!==null)for(const o of r)o.stop();de(t);var l=t.parent;l!==null&&l.first!==null&&pe(t),t.next=t.prev=t.teardown=t.ctx=t.deps=t.fn=t.nodes_start=t.nodes_end=t.ac=null}function pe(t){var n=t.parent,e=t.prev,r=t.next;e!==null&&(e.next=r),r!==null&&(r.prev=e),n!==null&&(n.first===t&&(n.first=r),n.last===t&&(n.last=e))}function on(t,n){var e=[];jn(t,e,!0),ge(e,()=>{J(t),n&&n()})}function ge(t,n){var e=t.length;if(e>0){var r=()=>--e||n();for(var l of t)l.out(r)}else n()}function jn(t,n,e){if((t.f&H)===0){if(t.f^=H,t.transitions!==null)for(const o of t.transitions)(o.is_global||e)&&n.push(o);for(var r=t.first;r!==null;){var l=r.next;jn(r,n,((r.f&$t)!==0||(r.f&Qt)!==0)&&e),r=l}}}function An(t){we(t,!0)}function we(t,n){if((t.f&H)!==0){t.f^=H,(t.f&A)===0&&(R(t,nt),kt(t));for(var e=t.first;e!==null;){var r=e.next;we(e,((e.f&$t)!==0||(e.f&Qt)!==0)&&n),e=r}if(t.transitions!==null)for(const l of t.transitions)(l.is_global||n)&&l.in()}}let Nt=!1;function me(t){Nt=t}let dt=!1;function ye(t){dt=t}let x=null,ht=!1;function Q(t){x=t}let y=null;function ot(t){y=t}let et=null;function xe(t){x!==null&&(et===null?et=[t]:et.push(t))}let $=null,F=0,W=null,be=1,Tt=0,pt=Tt;function _e(t){pt=t}let ut=!1;function ke(){return++be}function un(t){var n=t.f;if((n&nt)!==0)return!0;if((n&st)!==0){var e=t.deps,r=(n&G)!==0;if(e!==null){var l,o,i=(n&yn)!==0,c=r&&y!==null&&!ut,u=e.length;if((i||c)&&(y===null||(y.f&xt)===0)){var a=t,s=a.parent;for(l=0;lt.wv)return!0}r&&(y===null||ut)||R(t,A)}return!1}function Ce(t,n,e=!0){var r=t.reactions;if(r!==null&&!et?.includes(t))for(var l=0;l{t.ac.abort(_n)}),t.ac=null);try{t.f|=bn;var d=(0,t.fn)(),v=t.deps;if($!==null){var f;if(an(t,F),v!==null&&F>0)for(v.length=F+$.length,f=0;f<$.length;f++)v[F+f]=$[f];else t.deps=v=$;if(!ut||2&s&&t.reactions!==null)for(f=F;fo||e});var s=x,d=y;Q(null),ot(null);try{for(var v,f=[];o!==null;){var p=o.assignedSlot||o.parentNode||o.host||null;try{var h=o["__"+r];if(h!=null&&(!o.disabled||t.target===o))if(gn(h)){var[g,...m]=h;g.apply(o,[t,...m])}else h.call(o,t)}catch(w){v?f.push(w):v=w}if(t.cancelBubble||p===n||p===null)break;o=p}if(v){for(let w of f)queueMicrotask(()=>{throw w});throw v}}finally{t.__root=n,delete t.currentTarget,Q(s),ot(d)}}}function Me(t){var n=document.createElement("template");return n.innerHTML=t.replaceAll("",""),n.content}function Dn(t,n){var e=y;e.nodes_start===null&&(e.nodes_start=t,e.nodes_end=n)}function B(t,n){var e,r=!!(2&n),l=!t.startsWith("");return()=>{e===void 0&&(e=Bt(e=Me(l?t:""+t)));var o=r||ae?document.importNode(e,!0):e.cloneNode(!0);return Dn(o,o),o}}function fn(t,n){return(function(e,r,l="svg"){var o,i=`<${l}>${e.startsWith("")?""+e:e}`;return()=>{if(!o){var c=Bt(Me(i));o=Bt(c)}var u=o.cloneNode(!0);return Dn(u,u),u}})(t,0,"svg")}function Wt(){var t=document.createDocumentFragment(),n=document.createComment(""),e=en();return t.append(n,e),Dn(n,e),t}function E(t,n){t!==null&&t.before(n)}function Mt(t,n){var e=n==null?"":typeof n=="object"?n+"":n;e!==(t.__t??=t.nodeValue)&&(t.__t=e,t.nodeValue=e+"")}function nr(t,n){return(function(e,{target:r,anchor:l,props:o={},events:i,context:c,intro:u=!0}){(function(){if(ie===void 0){ie=window,ae=/Firefox/.test(navigator.userAgent);var f=Element.prototype,p=Node.prototype,h=Text.prototype;se=yt(p,"firstChild").get,fe=yt(p,"nextSibling").get,Kn(f)&&(f.__click=void 0,f.__className=void 0,f.__attributes=null,f.__style=void 0,f.__e=void 0),Kn(h)&&(h.__t=void 0)}})();var a=new Set,s=f=>{for(var p=0;pnew Promise(g=>{h.outro?on(p,()=>{J(p),g(void 0)}):(J(p),g(void 0))})})(()=>{var f=l??r.appendChild(en());return vt(()=>{c&&(tn({}),j.c=c),i&&(o.$$events=i),d=e(f,o)||{},c&&nn()}),()=>{for(var p of a){r.removeEventListener(p,sn);var h=Ot.get(p);--h===0?(document.removeEventListener(p,sn),Ot.delete(p)):Ot.set(p,h)}Ne.delete(s),f!==l&&f.parentNode?.removeChild(f)}});return $n.set(d,v),d})(t,n)}const Ot=new Map;let $n=new WeakMap;function gt(t,n,e=!1){var r=t,l=null,o=null,i=M,c=!1;const u=(v,f=!0)=>{c=!0,d(f,v)};var a=null;function s(){a!==null&&(a.lastChild.remove(),r.before(a),a=null);var v=i?l:o,f=i?o:l;v&&An(v),f&&on(f,()=>{i?o=null:l=null})}const d=(v,f)=>{if(i!==(i=v)){var p=!1,h=r;i?l??=f&&vt(()=>f(h)):o??=f&&vt(()=>f(h)),s()}};ln(()=>{c=!1,n(u),c||d(null,null)},e?$t:0)}function qn(t,n){return n}function Ln(t,n,e,r,l,o=null){var i=t,c={flags:n,items:new Map,first:null};!(4&n)||(i=t.appendChild(en()));var u,a,s=null,d=!1,v=new Map,f=Sn(()=>{var h=e();return gn(h)?h:h==null?[]:wn(h)});function p(){(function(h,g,m,w,N,Z,q,K,Gt){var O,z,it,V,_,S,at=!!(8&q),Kt=!!(3&q),Vt=g.length,rt=m.items,jt=m.first,k=jt,C=null,L=[],Y=[];if(at)for(S=0;S0){var Er=4&q&&Vt===0?N:null;if(at){for(S=0;S0&&Wn.length===0&&Tn!==null;if(zn){var qe=Tn.parentNode;qe.textContent="",qe.append(Tn),$e.clear(),X(Un,Jt[0].prev,Jt[dn-1].next)}ge(Wn,()=>{for(var Gn=0;Gn{if(z!==void 0)for(_ of z)_.a?.apply()});for(var Nr of(h.first=m.first&&m.first.e,h.last=C&&C.e,w.values()))J(Nr.e);w.clear()})(a,u,c,v,i,l,n,r,e),o!==null&&(u.length===0?s?An(s):s=vt(()=>o(i)):s!==null&&on(s,()=>{s=null}))}ln(()=>{a??=y;var h=(u=b(f)).length;d&&h===0||(d=h===0,p(),b(f))})}function er(t,n,e,r){1&r&&Rt(t.v,n),2&r?Rt(t.i,e):t.i=e}function rr(t,n,e,r,l,o,i,c,u,a,s){var d=1&u?16&u?Ft(l):(function(p,h=!1,g=!0){const m=Ft(p);return h||(m.equals=Qn),qt&&g&&j!==null&&j.l!==null&&(j.l.s??=[]).push(m),m})(l,!1,!1):l,v=2&u?Ft(i):i,f={i:v,v:d,k:o,a:null,e:null,prev:e,next:r};try{return t===null&&document.createDocumentFragment().append(t=en()),f.e=vt(()=>c(t,d,v,a),!1),f.e.prev=e&&e.e,f.e.next=r&&r.e,e===null?s||(n.first=f):(e.next=f,e.e.next=f.e),r!==null&&(r.prev=f,r.e.prev=f.e),f}finally{}}function In(t,n,e){for(var r=t.next?t.next.e.nodes_start:e,l=n?n.e.nodes_start:e,o=t.e.nodes_start;o!==null&&o!==r;){var i=rn(o);l.before(o),o=i}}function X(t,n,e){n===null?t.first=e:(n.next=e,n.e.next=e&&e.e),e!==null&&(e.prev=n,e.e.prev=n&&n.e)}function Oe(t){var n,e,r="";if(typeof t=="string"||typeof t=="number")r+=t;else if(typeof t=="object")if(Array.isArray(t)){var l=t.length;for(n=0;n(a&&(a=!1,u=r),u),d=Xt in t||We in t;l=yt(t,n)?.set??(d&&n in t?w=>t[n]=w:void 0);var v,f=!1;if([o,f]=(function(w){var N=cn;try{return cn=!1,[w(),cn]}finally{cn=N}})(()=>t[n]),o===void 0&&r!==void 0&&(o=s(),l&&(i&&(function(){throw new Error("https://svelte.dev/e/props_invalid_value")})(),l(o))),v=i?()=>{var w=t[n];return w===void 0?s():(a=!0,w)}:()=>{var w=t[n];return w!==void 0&&(u=void 0),w===void 0?u:w},i&&!(4&e))return v;if(l){var p=t.$$legacy;return function(w,N){return arguments.length>0?(i&&N&&!p&&!f||l(N?v():w),w):v()}}var h=!1,g=Sn(()=>(h=!1,v()));b(g);var m=y;return function(w,N){if(arguments.length>0){const Z=N?b(g):i&&c?Ct(w):w;return I(g,Z),h=!0,u!==void 0&&(u=Z),w}return dt&&h||(m.f&xt)!==0?g.v:b(g)}}typeof window<"u"&&((window.__svelte??={}).v??=new Set).add("5"),qt=!0;var lr=B('
'),or=fn(''),ur=fn('');function ir(t,n){let e=zt(n,"className",8,"h-4 w-4");var r=ur();T(()=>wt(r,0,Zt(e()))),E(t,r)}var ar=fn('');function sr(t,n){let e=zt(n,"className",8,"h-4 w-4 dark:opacity-50");var r=ar();T(()=>wt(r,0,Zt(e()))),E(t,r)}var fr=fn('');function cr(t,n){let e=zt(n,"className",8,"h-4 w-4");var r=fr();T(()=>wt(r,0,Zt(e()))),E(t,r)}const mt={FAILED:"FAILED",PENDING:"PENDING",SUCCESS:"SUCCESS"};Object.freeze(mt);var vr=B("
"),dr=B('
');function hr(t,n){tn(n,!0);var e=dr(),r=P(e);(function(i,c,...u){var a,s=i,d=Be;ln(()=>{d!==(d=c())&&(a&&(J(a),a=null),a=vt(()=>d(s,...u)))},$t)})(P(r),()=>n.children);var l=St(r,2),o=i=>{var c=vr();T(u=>wt(c,1,`${u??""} absolute top-1/2 left-[calc(50%+16px)] h-0.5 w-[calc(100%-32px)] -translate-y-1/2 z-0`),[()=>(function(u,a){if(a===null)return!1;const s=u.findIndex(({step_index:d})=>d===a)+1;return u[s]?.status!==mt.SUCCESS})(n.row,n.cell.step_index)?"bg-neutral-400 dark:bg-neutral-800":"bg-green-700"]),E(i,c)};gt(l,i=>{(function(c,u){return cwt(r,1,`${n.className??""} w-8 h-8 rounded-full flex items-center justify-center mx-auto text-white`)),E(t,e),nn()}var pr=B(' '),gr=B(''),wr=B(' '),mr=B(''),yr=B(""),xr=B('
');function br(t,n){tn(n,!0);const e={[mt.PENDING]:sr,[mt.SUCCESS]:ir,[mt.FAILED]:cr};var r=xr(),l=P(r),o=u=>{var a=gr();Ln(P(a),21,()=>n.columns,qn,(s,d)=>{var v=pr(),f=P(v),p=P(f);T(()=>Mt(p,b(d).label)),E(s,v)}),E(u,a)};gt(l,u=>{n.columns&&u(o)});var i=St(l),c=u=>{var a=yr(),s=P(a),d=v=>{var f=Wt();Ln(Ut(f),17,()=>n.rows,qn,(p,h)=>{var g=mr();Ln(g,21,()=>b(h),qn,(m,w,N)=>{var Z=Wt(),q=Ut(Z),K=O=>{{let z=te(()=>b(w).status===mt.SUCCESS?"bg-green-700":b(w).status===mt.FAILED?"bg-rose-700":"bg-neutral-400 dark:bg-neutral-800");hr(O,{get cell(){return b(w)},get row(){return b(h)},i:N,get className(){return b(z)},children:(it,V)=>{const _=te(()=>e[b(w).status]);var S=Wt();(function(at,Kt,Vt){var rt,jt,k=at,C=null,L=null;function Y(){jt&&(on(jt),jt=null),C&&(C.lastChild.remove(),k.before(C),C=null),jt=L,L=null}ln(()=>{if(rt!==(rt=Kt())){if(rt){var tt=k;L=vt(()=>Vt(tt,rt))}Y()}},$t)})(Ut(S),()=>b(_),(at,Kt)=>{Kt(at,{})}),E(it,S)},$$slots:{default:!0}})}},Gt=O=>{var z=wr(),it=P(z);T(()=>Mt(it,b(w).value??b(w).status??"👻")),E(O,z)};gt(q,O=>{b(w).status?O(K):O(Gt,!1)}),E(m,Z)}),E(p,g)}),E(v,f)};gt(s,v=>{n.rows.length>0&&v(d)}),E(u,a)};gt(i,u=>{n.columns&&n.rows!=null&&u(c)}),E(t,r),nn()}const je=(()=>{const t=document.querySelector("[data-rpa-process-overview-config]");if(t!==null)try{const n=t.dataset.rpaProcessOverviewConfig;if(n)return JSON.parse(n)}catch{}return{}})(),Fn=t=>je.messages?.[t]??t+" (missing translation)";var _r=B('

'),kr=B('
'),Cr=B('

'),Sr=B('

');(function(t,n){const e=new URLSearchParams(window.location.search).get("target")??n,r=nr(t,{target:document.getElementById(e)??document.body});window[e]={stop:()=>(function(l,o){const i=$n.get(l);return i?($n.delete(l),i(o)):Promise.resolve()})(r)}})(function(t,n){tn(n,!0);let e=U(!1),r=U(null),l=U(null),o=U(!0);(function(s){He();var d=y.f;if(x||(d&Qt)===0||(d&xn)!==0)return ve(s);var v=j;(v.e??=[]).push(s)})(()=>{I(o,!0);const s=new URL(je.data_url,document.location.href);fetch(s.toString()).then(d=>d.json()).then(({data:d,meta:v})=>{d&&(I(l,v?.total??null,!0),I(r,d,!0)),I(o,!1)}).catch(()=>{I(e,!0)})});var i=Wt(),c=Ut(i),u=s=>{var d=kr();(function(v,f){let p=zt(f,"className",8,"w-8 h-8 animate-spin dark:text-gray-800 fill-blue-900");var h=lr(),g=P(h);(function(m,w,N,Z){var q=w.$$slots?.[N],K=!1;q===!0&&(q=w.children,K=!0),q===void 0||q(m,K?()=>Z:Z)})(St(g,2),f,"default",{}),T(()=>wt(g,0,Zt(p()))),E(v,h)})(P(d),{children:(v,f)=>{var p=_r(),h=P(p);T(g=>Mt(h,g),[()=>Fn("Loading data...")]),E(v,p)},$$slots:{default:!0}}),E(s,d)},a=s=>{var d=Wt(),v=Ut(d),f=h=>{var g=Cr(),m=P(g),w=P(m);T(N=>Mt(w,N),[()=>Fn("Missing data")]),E(h,g)},p=h=>{var g=Sr(),m=P(g),w=P(m);(function(K,Gt){let O=zt(Gt,"className",8,"w-5 h-5 mr-2 text-rose-700");var z=or();T(()=>wt(z,0,Zt(O()))),E(K,z)})(w,{});var N=St(w,2),Z=P(N),q=P(St(N,2));br(P(St(m,2)),{get columns(){return b(r).columns},get rows(){return b(r).rows}}),T(K=>{Mt(Z,K),Mt(q,b(l)??"?")},[()=>Fn("Failed processes")]),E(h,g)};gt(v,h=>{b(r)===null?h(f):h(p,!1)},!0),E(s,d)};gt(c,s=>{b(o)?s(u):s(a,!1)}),E(t,i),nn()},"ProcessOverview")}); diff --git a/public/widgets/ProcessSearch.min.js b/public/widgets/ProcessSearch.min.js new file mode 100644 index 0000000..dbf9b69 --- /dev/null +++ b/public/widgets/ProcessSearch.min.js @@ -0,0 +1 @@ +(function(jn){typeof define=="function"&&define.amd?define(jn):jn()})(function(){"use strict";var tt=Array.isArray,Ut=Array.prototype.indexOf,Vt=Array.from,Ln=Object.defineProperty,cn=Object.getOwnPropertyDescriptor,Ft=Object.prototype,Kt=Array.prototype,Ht=Object.getPrototypeOf,et=Object.isExtensible;function rt(n){for(var t=0;t0||En.length>0}function Yt(){var n;W.length>0&&ft(),En.length>0&&(n=En,En=[],rt(n))}function Zt(){const n=w.b;return n===null&&(function(){throw new Error("https://svelte.dev/e/await_outside_boundary")})(),n}function ne(n){var t=2050,e=g!==null&&2&g.f?g:null;return w===null||e!==null&&(e.f&M)!==0?t|=M:w.f|=ut,{ctx:j,deps:null,effects:null,equals:it,f:t,fn:n,reactions:null,rv:0,v:x,wv:0,parent:e??w,ac:null}}function te(n,t){let e=w;e===null&&(function(){throw new Error("https://svelte.dev/e/async_derived_orphan")})();var r=e.b,l=void 0,u=wt(x),o=null,s=!g;return(function(i){Q(4718592,i,!0)})(()=>{try{var i=n();o&&Promise.resolve(i).catch(()=>{})}catch(f){i=Promise.reject(f)}var a=()=>i;l=o?.then(a,a)??Promise.resolve(i),o=l;var c=y,d=r.is_pending();s&&(r.update_pending_count(1),d||c.increment());const v=(f,p=void 0)=>{o=null,d||c.activate(),p?p!==Wn&&(u.f|=K,Un(u,p)):((u.f&K)!==0&&(u.f^=K),Un(u,f)),s&&(r.update_pending_count(-1),d||c.decrement()),dt()};if(l.then(v,f=>v(null,f||"unknown")),c)return()=>{queueMicrotask(()=>c.neuter())}}),new Promise(i=>{(function a(c){function d(){c===l?i(u):a(l)}c.then(d,d)})(l)})}function ct(n){var t=n.effects;if(t!==null){n.effects=null;for(var e=0;ete(i))).then(i=>{l?.activate(),o();try{e([...n.map(r),...i])}catch(a){(u.f&dn)===0&&$n(a,u)}l?.deactivate(),dt()}).catch(i=>{s.error(i)})}else e(n.map(r))}function dt(){B(null),C(null),xn(null)}const In=new Set;let y=null,Pn=null,ht=new Set,H=[],Sn=null,Jn=!1,hn=!1;class nn{current=new Map;#a=new Map;#l=new Set;#n=0;#s=null;#f=!1;#e=[];#u=[];#r=[];#t=[];#i=[];#c=[];#v=[];skipped_effects=new Set;process(t){H=[],Pn=null;for(const l of t)this.#h(l);if(this.#e.length===0&&this.#n===0){this.#d();var e=this.#r,r=this.#t;this.#r=[],this.#t=[],this.#i=[],Pn=y,y=null,gt(e),gt(r),y===null?y=this:In.delete(this),this.#s?.resolve()}else this.#o(this.#r),this.#o(this.#t),this.#o(this.#i);for(const l of this.#e)ln(l);for(const l of this.#u)ln(l);this.#e=[],this.#u=[]}#h(t){t.f^=E;for(var e=t.first;e!==null;){var r=e.f,l=!!(96&r);if(!(l&&(r&E)!==0||(r&F)!==0||this.skipped_effects.has(e))&&e.fn!==null){l?e.f^=E:4&r?this.#t.push(e):(r&E)===0&&((r&Gt)!==0?(e.b?.is_pending()?this.#u:this.#e).push(e):kn(e)&&((e.f&vn)!==0&&this.#i.push(e),ln(e)));var u=e.first;if(u!==null){e=u;continue}}var o=e.parent;for(e=e.next;e===null&&o!==null;)e=o.next,o=o.parent}}#o(t){for(const e of t)((e.f&D)!==0?this.#c:this.#v).push(e),k(e,E);t.length=0}capture(t,e){this.#a.has(t)||this.#a.set(t,e),this.current.set(t,t.v)}activate(){y=this}deactivate(){y=null,Pn=null;for(const t of ht)if(ht.delete(t),t(),y!==null)break}neuter(){this.#f=!0}flush(){H.length>0?pt():this.#d(),y===this&&(this.#n===0&&In.delete(this),this.deactivate())}#d(){if(!this.#f)for(const t of this.#l)t();this.#l.clear()}increment(){this.#n+=1}decrement(){if(this.#n-=1,this.#n===0){for(const t of this.#c)k(t,D),tn(t);for(const t of this.#v)k(t,V),tn(t);this.#r=[],this.#t=[],this.flush()}else this.deactivate()}add_callback(t){this.#l.add(t)}settled(){return(this.#s??={promise:new Promise((r,l)=>{t=r,e=l}),resolve:t,reject:e}).promise;var t,e}static ensure(){if(y===null){const t=y=new nn;In.add(y),hn||nn.enqueue(()=>{y===t&&t.flush()})}return y}static enqueue(t){(function(e){if(W.length===0&&!hn){var r=W;queueMicrotask(()=>{r===W&&ft()})}W.push(e)})(t)}}function pt(){var n=en;Jn=!0;try{var t=0;for(Lt(!0);H.length>0;){var e=nn.ensure();t++>1e3&&re(),e.process(H),$.clear()}}finally{Jn=!1,Lt(n),Sn=null}}function re(){try{(function(){throw new Error("https://svelte.dev/e/effect_update_depth_exceeded")})()}catch(n){$n(n,Sn)}}let z=null;function gt(n){var t=n.length;if(t!==0){for(var e=0;e0)){$.clear();for(const l of z)ln(l);z=[]}}z=null}}function tn(n){for(var t=Sn=n;t.parent!==null;){var e=(t=t.parent).f;if(Jn&&t===w&&(e&vn)!==0)return;if(96&e){if((e&E)===0)return;t.f^=E}}H.push(t)}const $=new Map;function wt(n,t){return{f:0,v:n,reactions:null,equals:it,rv:0,wv:0}}function L(n,t){const e=wt(n);var r;return r=e,g!==null&&(T===null?T=[r]:T.push(r)),e}function R(n,t,e=!1){return g===null||A&&(g.f&zt)===0||!(4325394&g.f)||T?.includes(n)||(function(){throw new Error("https://svelte.dev/e/state_unsafe_mutation")})(),Un(n,e?pn(t):t)}function Un(n,t){if(!n.equals(t)){var e=n.v;rn?$.set(n,t):$.set(n,e),n.v=t,nn.ensure().capture(n,e),2&n.f&&((n.f&D)!==0&&Bn(n),k(n,(n.f&M)===0?E:V)),n.wv=Dt(),mt(n,D),w===null||(w.f&E)===0||96&w.f||(N===null?(function(r){N=r})([n]):N.push(n))}return t}function Vn(n){R(n,n.v+1)}function mt(n,t){var e=n.reactions;if(e!==null)for(var r=e.length,l=0;l{if(Y===u)return s();var i=g,a=Y;C(null),Ct(u);var c=s();return C(i),Ct(a),c};return r&&e.set("length",L(n.length)),new Proxy(n,{defineProperty(s,i,a){"value"in a&&a.configurable!==!1&&a.enumerable!==!1&&a.writable!==!1||(function(){throw new Error("https://svelte.dev/e/state_descriptors_fixed")})();var c=e.get(i);return c===void 0?c=o(()=>{var d=L(a.value);return e.set(i,d),d}):R(c,a.value,!0),!0},deleteProperty(s,i){var a=e.get(i);if(a===void 0){if(i in s){const c=o(()=>L(x));e.set(i,c),Vn(l)}}else R(a,x),Vn(l);return!0},get(s,i,a){if(i===Tn)return n;var c=e.get(i),d=i in s;if(c!==void 0||d&&!cn(s,i)?.writable||(c=o(()=>L(pn(d?s[i]:x))),e.set(i,c)),c!==void 0){var v=_(c);return v===x?void 0:v}return Reflect.get(s,i,a)},getOwnPropertyDescriptor(s,i){var a=Reflect.getOwnPropertyDescriptor(s,i);if(a&&"value"in a){var c=e.get(i);c&&(a.value=_(c))}else if(a===void 0){var d=e.get(i),v=d?.v;if(d!==void 0&&v!==x)return{enumerable:!0,configurable:!0,value:v,writable:!0}}return a},has(s,i){if(i===Tn)return!0;var a=e.get(i),c=a!==void 0&&a.v!==x||Reflect.has(s,i);return(a!==void 0||w!==null&&(!c||cn(s,i)?.writable))&&(a===void 0&&(a=o(()=>L(c?pn(s[i]):x)),e.set(i,a)),_(a)===x)?!1:c},set(s,i,a,c){var d=e.get(i),v=i in s;if(r&&i==="length")for(var f=a;fL(x)),e.set(f+"",p))}d===void 0?v&&!cn(s,i)?.writable||(R(d=o(()=>L(void 0)),pn(a)),e.set(i,d)):(v=d.v!==x,R(d,o(()=>pn(a))));var h=Reflect.getOwnPropertyDescriptor(s,i);if(h?.set&&h.set.call(c,a),!v){if(r&&typeof i=="string"){var b=e.get("length"),m=Number(i);Number.isInteger(m)&&m>=b.v&&R(b,m+1)}Vn(l)}return!0},ownKeys(s){_(l);var i=Reflect.ownKeys(s).filter(d=>{var v=e.get(d);return v===void 0||v.v!==x});for(var[a,c]of e)c.v===x||a in s||i.push(a);return i},setPrototypeOf(){(function(){throw new Error("https://svelte.dev/e/state_prototype_fixed")})()}})}var yt,_t,bt,xt;function Et(n=""){return document.createTextNode(n)}function On(n){return bt.call(n)}function Fn(n){return xt.call(n)}function S(n,t){return On(n)}function Kn(n,t){var e=On(n);return e instanceof Comment&&e.data===""?Fn(e):e}function G(n,t=1,e=!1){let r=n;for(;t--;)r=Fn(r);return r}let Pt=!1;function Hn(n){var t=g,e=w;C(null),B(null);try{return n()}finally{C(t),B(e)}}function le(n,t,e,r=e){n.addEventListener(t,()=>Hn(e));const l=n.__on_r;n.__on_r=l?()=>{l(),r(!0)}:()=>r(!0),Pt||(Pt=!0,document.addEventListener("reset",u=>{Promise.resolve().then(()=>{if(!u.defaultPrevented)for(const o of u.target.elements)o.__on_r?.()})},{capture:!0}))}function ue(n){w===null&&g===null&&(function(){throw new Error("https://svelte.dev/e/effect_orphan")})(),g!==null&&(g.f&M)!==0&&w===null&&(function(){throw new Error("https://svelte.dev/e/effect_in_unowned_derived")})(),rn&&(function(){throw new Error("https://svelte.dev/e/effect_in_teardown")})()}function Q(n,t,e,r=!0){var l=w;l!==null&&(l.f&F)!==0&&(n|=F);var u={ctx:j,deps:null,nodes_start:null,nodes_end:null,f:n|D,first:null,fn:t,last:null,next:null,parent:l,b:l&&l.b,prev:null,teardown:null,transitions:null,wv:0,ac:null};if(e)try{ln(u),u.f|=An}catch(i){throw X(u),i}else t!==null&&tn(u);if(r){var o=u;if(e&&o.deps===null&&o.teardown===null&&o.nodes_start===null&&o.first===o.last&&(o.f&ut)===0&&(o=o.first),o!==null&&(o.parent=l,l!==null&&(function(i,a){var c=a.last;c===null?a.last=a.first=i:(c.next=i,i.prev=c,a.last=i)})(o,l),g!==null&&2&g.f&&(n<)===0)){var s=g;(s.effects??=[]).push(o)}}return u}function St(n){return Q(1048580,n,!1)}function gn(n,t=[],e=[]){ee(t,e,r=>{Q(8,()=>n(...r.map(_)),!0)})}function zn(n,t=!0){return Q(524320,n,!0,t)}function Ot(n){var t=n.teardown;if(t!==null){const e=rn,r=g;Rt(!0),C(null);try{t.call(null)}finally{Rt(e),C(r)}}}function kt(n,t=!1){var e=n.first;for(n.first=n.last=null;e!==null;){const l=e.ac;l!==null&&Hn(()=>{l.abort(Wn)});var r=e.next;(e.f<)!==0?e.parent=null:X(e,t),e=r}}function X(n,t=!0){var e=!1;(t||262144&n.f)&&n.nodes_start!==null&&n.nodes_end!==null&&((function(u,o){for(;u!==null;){var s=u===o?null:Fn(u);u.remove(),u=s}})(n.nodes_start,n.nodes_end),e=!0),kt(n,t&&!e),qn(n,0),k(n,dn);var r=n.transitions;if(r!==null)for(const u of r)u.stop();Ot(n);var l=n.parent;l!==null&&l.first!==null&&qt(n),n.next=n.prev=n.teardown=n.ctx=n.deps=n.fn=n.nodes_start=n.nodes_end=n.ac=null}function qt(n){var t=n.parent,e=n.prev,r=n.next;e!==null&&(e.next=r),r!==null&&(r.prev=e),t!==null&&(t.first===n&&(t.first=r),t.last===n&&(t.last=e))}function Nt(n,t){var e=[];Mt(n,e,!0),(function(r,l){var u=r.length;if(u>0){var o=()=>--u||l();for(var s of r)s.out(o)}else l()})(e,()=>{X(n),t&&t()})}function Mt(n,t,e){if((n.f&F)===0){if(n.f^=F,n.transitions!==null)for(const u of n.transitions)(u.is_global||e)&&t.push(u);for(var r=n.first;r!==null;){var l=r.next;Mt(r,t,((r.f&Cn)!==0||(r.f&bn)!==0)&&e),r=l}}}function ie(n){jt(n,!0)}function jt(n,t){if((n.f&F)!==0){n.f^=F,(n.f&E)===0&&(k(n,D),tn(n));for(var e=n.first;e!==null;){var r=e.next;jt(e,((e.f&Cn)!==0||(e.f&bn)!==0)&&t),e=r}if(n.transitions!==null)for(const l of n.transitions)(l.is_global||t)&&l.in()}}let en=!1;function Lt(n){en=n}let rn=!1;function Rt(n){rn=n}let g=null,A=!1;function C(n){g=n}let w=null;function B(n){w=n}let T=null,P=null,O=0,N=null,At=1,wn=0,Y=wn;function Ct(n){Y=n}let I=!1;function Dt(){return++At}function kn(n){var t=n.f;if((t&D)!==0)return!0;if((t&V)!==0){var e=n.deps,r=(t&M)!==0;if(e!==null){var l,u,o=(t&Rn)!==0,s=r&&w!==null&&!I,i=e.length;if((o||s)&&(w===null||(w.f&dn)===0)){var a=n,c=a.parent;for(l=0;ln.wv)return!0}r&&(w===null||I)||k(n,E)}return!1}function Tt(n,t,e=!0){var r=n.reactions;if(r!==null&&!T?.includes(n))for(var l=0;l{n.ac.abort(Wn)}),n.ac=null);try{n.f|=Dn;var d=(0,n.fn)(),v=n.deps;if(P!==null){var f;if(qn(n,O),v!==null&&O>0)for(v.length=O+P.length,f=0;fu||e});var c=g,d=w;C(null),B(null);try{for(var v,f=[];u!==null;){var p=u.assignedSlot||u.parentNode||u.host||null;try{var h=u["__"+r];if(h!=null&&(!u.disabled||n.target===u))if(tt(h)){var[b,...m]=h;b.apply(u,[n,...m])}else h.call(u,n)}catch(q){v?f.push(q):v=q}if(n.cancelBubble||p===t||p===null)break;u=p}if(v){for(let q of f)queueMicrotask(()=>{throw q});throw v}}finally{n.__root=t,delete n.currentTarget,C(c),B(d)}}}function Gn(n,t){var e=w;e.nodes_start===null&&(e.nodes_start=n,e.nodes_end=t)}function un(n,t){var e,r=!!(1&t),l=!!(2&t),u=!n.startsWith("");return()=>{var o,s;e===void 0&&(o=u?n:""+n,(s=document.createElement("template")).innerHTML=o.replaceAll("",""),e=s.content,r||(e=On(e)));var i=l||_t?document.importNode(e,!0):e.cloneNode(!0);return r?Gn(On(i),i.lastChild):Gn(i,i),i}}function Z(n,t){n!==null&&n.before(t)}function on(n,t){var e=t==null?"":typeof t=="object"?t+"":t;e!==(n.__t??=n.nodeValue)&&(n.__t=e,n.nodeValue=e+"")}function de(n,t){return(function(e,{target:r,anchor:l,props:u={},events:o,context:s,intro:i=!0}){(function(){if(yt===void 0){yt=window,_t=/Firefox/.test(navigator.userAgent);var f=Element.prototype,p=Node.prototype,h=Text.prototype;bt=cn(p,"firstChild").get,xt=cn(p,"nextSibling").get,et(f)&&(f.__click=void 0,f.__className=void 0,f.__attributes=null,f.__style=void 0,f.__e=void 0),et(h)&&(h.__t=void 0)}})();var a=new Set,c=f=>{for(var p=0;pnew Promise(b=>{h.outro?Nt(p,()=>{X(p),b(void 0)}):(X(p),b(void 0))})})(()=>{var f=l??r.appendChild(Et());return zn(()=>{s&&(ot({}),j.c=s),o&&(u.$$events=o),d=e(f,u)||{},s&&at()}),()=>{for(var p of a){r.removeEventListener(p,Nn);var h=an.get(p);--h===0?(document.removeEventListener(p,Nn),an.delete(p)):an.set(p,h)}Bt.delete(c),f!==l&&f.parentNode?.removeChild(f)}});return Qn.set(d,v),d})(n,t)}const an=new Map;let Qn=new WeakMap;function Xn(n,t,e=!1){var r=n,l=null,u=null,o=x,s=!1;const i=(v,f=!0)=>{s=!0,d(f,v)};var a=null;function c(){a!==null&&(a.lastChild.remove(),r.before(a),a=null);var v=o?l:u,f=o?u:l;v&&ie(v),f&&Nt(f,()=>{o?u=null:l=null})}const d=(v,f)=>{if(o!==(o=v)){var p=!1,h=r;o?l??=f&&zn(()=>f(h)):u??=f&&zn(()=>f(h)),c()}};(function(v,f=0){Q(vn|f,v,!0)})(()=>{s=!1,t(i),s||d(null,null)},e?Cn:0)}function he(n,t,e=t){var r=new WeakSet;le(n,"input",async l=>{var u=l?n.defaultValue:n.value;if(u=Yn(n)?Zn(u):u,e(u),y!==null&&r.add(y),await ae(),u!==(u=t())){var o=n.selectionStart,s=n.selectionEnd;n.value=u??"",s!==null&&(n.selectionStart=o,n.selectionEnd=Math.min(s,n.value.length))}}),(function(l){var u=A;try{return A=!0,l()}finally{A=u}})(t)==null&&n.value&&(e(Yn(n)?Zn(n.value):n.value),y!==null&&r.add(y)),(function(l,u=0){Q(8|u,l,!0)})(()=>{var l=t();if(n===document.activeElement){var u=Pn??y;if(r.has(u))return}Yn(n)&&l===Zn(n.value)||(n.type!=="date"||l||n.value)&&l!==n.value&&(n.value=l??"")})}function Yn(n){var t=n.type;return t==="number"||t==="range"}function Zn(n){return n===""?null:+n}typeof window<"u"&&((window.__svelte??={}).v??=new Set).add("5");var pe=un('

No results matching

'),ge=un('

One result matching

'),we=un('

'),me=un("
 
",1),ye=un("

Enter a non-empty query.

"),_e=un('
Config and data
 
',1);(function(n,t){const e=new URLSearchParams(window.location.search).get("target")??t,r=de(n,{target:document.getElementById(e)??document.body});window[e]={stop:()=>(function(l,u){const o=Qn.get(l);return o?(Qn.delete(l),o(u)):Promise.resolve()})(r)}})(function(n,t){ot(t,!0);const e=(()=>{try{return JSON.parse(document.getElementById("ProcessSearch")?.dataset.config||"{}")}catch{return{}}})();let r=L(""),l=L(""),u=L(null);(function(m){ue();var q=w.f;if(g||(q&bn)===0||(q&An)!==0)return St(m);var J=j;(J.e??=[]).push(m)})(()=>{if(R(l,_(r).trim(),!0),_(l)){const m=new URL(document.location.href);m.searchParams.set("q",_(l)),history.replaceState({},"",m);const q=new URL(e.search_url,document.location.href);q.searchParams.set("q",_(l)),R(u,null),fetch(q.toString()).then(J=>J.json()).then(J=>R(u,J,!0))}});var o=_e(),s=Kn(o),i=S(s),a=S(i),c=G(s,2),d=m=>{var q=me(),J=Kn(q),be=U=>{var sn=pe(),mn=G(S(sn)),yn=S(mn);gn(()=>on(yn,_(l))),Z(U,sn)},xe=U=>{var sn,mn,yn,Jt=(sn=document.createDocumentFragment(),mn=document.createComment(""),yn=Et(),sn.append(mn,yn),Gn(mn,yn),sn),Se=Kn(Jt),Oe=fn=>{var _n=ge(),Mn=G(S(_n)),nt=S(Mn);gn(()=>on(nt,_(l))),Z(fn,_n)},ke=fn=>{var _n=we(),Mn=S(_n),nt=S(G(Mn));gn(()=>{on(Mn,`${_(u)?.rows?.length??""} results matching `),on(nt,_(l))}),Z(fn,_n)};Xn(Se,fn=>{_(u)?.rows?.length===1?fn(Oe):fn(ke,!1)},!0),Z(U,Jt)};Xn(J,U=>{(_(u)?.rows??[]).length===0?U(be):U(xe,!1)});var Ee=S(G(J,2)),Pe=S(Ee);gn(U=>on(Pe,U),[()=>JSON.stringify(_(u)?.rows)]),Z(m,q)},v=m=>{Z(m,ye())};Xn(c,m=>{_(l)?m(d):m(v,!1)});var f=G(c,2),p=G(S(f),2),h=S(p),b=S(h);gn(m=>on(b,m),[()=>JSON.stringify({config:e,data:_(u)},null,2)]),he(a,()=>_(r),m=>R(r,m)),Z(n,o),at()},"ProcessSearch")}); diff --git a/src/Admin/Field/FormField.php b/src/Admin/Field/FormField.php new file mode 100644 index 0000000..00d82de --- /dev/null +++ b/src/Admin/Field/FormField.php @@ -0,0 +1,47 @@ +setFieldFqcn(__CLASS__) + ->hideOnIndex() + ->setProperty('admin_form_template_view') + ->setLabel(false) + ->setFormType(FormTemplateViewType::class) + ->addCssClass('field-form_template_view') + ->setFormTypeOptions(['mapped' => false, 'required' => false]) + ->setValue(true) + ->setTemplatePath($templatePath) + ->setTemplateContext($context); + } + + public function setTemplateContext(array $context): self + { + $this->setCustomOption(self::OPTION_TEMPLATE_CONTEXT, $context); + + return $this; + } +} diff --git a/src/Admin/Form/FormTemplateViewType.php b/src/Admin/Form/FormTemplateViewType.php new file mode 100644 index 0000000..cbe6116 --- /dev/null +++ b/src/Admin/Form/FormTemplateViewType.php @@ -0,0 +1,13 @@ +setTitle(t('RPA Process Overview')); + ->setTitle($this->getParameter('site_title')); + } + + public function configureUserMenu(UserInterface $user): UserMenu + { + return parent::configureUserMenu($user) + ->setMenuItems([ + // Remove the logout link. + ]); } public function configureMenuItems(): iterable { yield MenuItem::linkToCrud(t('Group'), null, ProcessOverviewGroup::class); yield MenuItem::linkToCrud(t('Process overview'), null, ProcessOverview::class); + yield MenuItem::linkToCrud(t('Data source'), null, DataSource::class); + yield MenuItem::section(); yield MenuItem::linkToRoute(t('Home'), null, 'app_default'); } diff --git a/src/Controller/Admin/DataSourceCrudController.php b/src/Controller/Admin/DataSourceCrudController.php new file mode 100644 index 0000000..801cfcc --- /dev/null +++ b/src/Controller/Admin/DataSourceCrudController.php @@ -0,0 +1,43 @@ +setEntityLabelInSingular(t('Data source')) + ->setEntityLabelInPlural(t('Data sources')); + } + + public function configureFields(string $pageName): iterable + { + yield TextField::new('label', t('Label')); + yield TextField::new('url', t('URL')) + ->setFormType(UrlType::class); + + yield TextField::new('createdBy', t('Created by')) + ->hideOnForm(); + yield DateTimeField::new('createdAt', t('Created at')) + ->hideOnForm(); + + yield CodeEditorField::new('options', t('Options')) + ->hideOnIndex() + ->setLanguage('yaml'); + } +} diff --git a/src/Controller/Admin/ProcessOverviewCrudController.php b/src/Controller/Admin/ProcessOverviewCrudController.php index 5440379..06f86df 100644 --- a/src/Controller/Admin/ProcessOverviewCrudController.php +++ b/src/Controller/Admin/ProcessOverviewCrudController.php @@ -2,19 +2,30 @@ namespace App\Controller\Admin; +use App\Admin\Field\FormField; +use App\DataSourceHelper; use App\Entity\ProcessOverview; use EasyCorp\Bundle\EasyAdminBundle\Config\Action; use EasyCorp\Bundle\EasyAdminBundle\Config\Actions; use EasyCorp\Bundle\EasyAdminBundle\Config\Crud; use EasyCorp\Bundle\EasyAdminBundle\Field\AssociationField; +use EasyCorp\Bundle\EasyAdminBundle\Field\ChoiceField; use EasyCorp\Bundle\EasyAdminBundle\Field\CodeEditorField; +use EasyCorp\Bundle\EasyAdminBundle\Field\DateTimeField; +use EasyCorp\Bundle\EasyAdminBundle\Field\FormField as EaFormField; use EasyCorp\Bundle\EasyAdminBundle\Field\IdField; use EasyCorp\Bundle\EasyAdminBundle\Field\TextField; +use Symfony\Component\Form\ChoiceList\Loader\CallbackChoiceLoader; use function Symfony\Component\Translation\t; class ProcessOverviewCrudController extends AbstractCrudController { + public function __construct( + private readonly DataSourceHelper $dataSourceHelper, + ) { + } + public static function getEntityFqcn(): string { return ProcessOverview::class; @@ -23,20 +34,32 @@ public static function getEntityFqcn(): string public function configureCrud(Crud $crud): Crud { return parent::configureCrud($crud) + ->setEntityLabelInSingular(t('Process overview')) ->setEntityLabelInPlural(t('Process overviews')) - ->setEntityLabelInSingular(t('Process overview')); + ->addFormTheme('admin/process_overview_form.html.twig'); } public function configureActions(Actions $actions): Actions { + // Cheat a little to set display condition on built in action. + $saveAndReturn = $actions->getAsDto(Crud::PAGE_EDIT)->getAction(Crud::PAGE_EDIT, Action::SAVE_AND_RETURN) + ->getAsConfigObject() + // Hide for incomplete overview. + ->displayIf(static fn (ProcessOverview $overview) => $overview->isReady()); + return parent::configureActions($actions) - ->add(Crud::PAGE_INDEX, Action::DETAIL) ->add(Crud::PAGE_INDEX, Action::new('show', t('Show process overview')) - ->displayIf(fn (ProcessOverview $overview) => $overview->getGroup()) + ->displayIf(fn (ProcessOverview $overview) => $overview->isReady()) ->linkToUrl(fn (ProcessOverview $overview) => $this->generateUrl('process_overview_show', [ 'group' => $overview->getGroup()->getId(), 'overview' => $overview->getId(), - ]))); + ]))) + ->remove(Crud::PAGE_NEW, Action::SAVE_AND_ADD_ANOTHER) + ->add(Crud::PAGE_NEW, Action::SAVE_AND_CONTINUE) + ->remove(Crud::PAGE_NEW, Action::SAVE_AND_RETURN) + // Hide “Save and return” for incomplete overview. + ->remove(Crud::PAGE_EDIT, Action::SAVE_AND_RETURN) + ->add(Crud::PAGE_EDIT, $saveAndReturn); } public function configureFields(string $pageName): iterable @@ -45,8 +68,73 @@ public function configureFields(string $pageName): iterable ->onlyOnDetail(); yield TextField::new('label', t('Label')); yield AssociationField::new('group', t('Group')); - yield CodeEditorField::new('options', t('Options')) - ->hideOnIndex() - ->setLanguage('yaml'); + + yield TextField::new('createdBy', t('Created by')) + ->hideOnForm(); + yield DateTimeField::new('createdAt', t('Created at')) + ->hideOnForm(); + + /** @var ProcessOverview $entity */ + $entity = $this->getContext()->getEntity()->getInstance(); + $dataSource = $entity?->getDataSource(); + $process = null; + if ($dataSource) { + if ($processId = $entity->getProcessId()) { + try { + $process = $this->dataSourceHelper->getProcess($dataSource, $processId); + } catch (\Exception) { + } + } + } + + // https://symfony.com/bundles/EasyAdminBundle/current/fields.html#form-fieldsets + yield EaFormField::addFieldset( + $dataSource ? t('Data source ({label})', ['label' => $dataSource->getLabel()]) : t('Data source'), + propertySuffix: 'data_source' + ); + + yield AssociationField::new('dataSource', t('Data source')) + ->hideOnIndex(); + + yield EaFormField::addFieldset( + isset($process['name']) ? t('Process ({label})', ['label' => $process['name']]) : t('Process'), + propertySuffix: 'process' + ); + + if ($dataSource) { + yield ChoiceField::new('processId', t('Process')) + ->setFormTypeOptions([ + // @todo Add search for process + 'choice_loader' => new CallbackChoiceLoader(function () use ($dataSource): array { + $processes = $this->dataSourceHelper->getProcesses($dataSource); + $options = array_combine( + array_column($processes['items'] ?? [], 'name'), + array_column($processes['items'] ?? [], 'id'), + ); + + return $options; + }), + ]) + // ->setRequired(true) + ->onlyOnForms(); + } + + yield EaFormField::addFieldset(t('Process options'), propertySuffix: 'process_options'); + + if ($process) { + yield CodeEditorField::new('options', t('Options')) + ->setLanguage('yaml') + ->setColumns(6) + ->setFormTypeOptions([ + 'empty_data' => '', + ]) + ; + yield FormField::addTemplateView('admin/crud/process_overview/options_details.html.twig', [ + 'process' => $process, + ]) + ->onlyOnForms() + ->setColumns(6) + ; + } } } diff --git a/src/Controller/Admin/ProcessOverviewGroupCrudController.php b/src/Controller/Admin/ProcessOverviewGroupCrudController.php index 8665aab..4ab444a 100644 --- a/src/Controller/Admin/ProcessOverviewGroupCrudController.php +++ b/src/Controller/Admin/ProcessOverviewGroupCrudController.php @@ -6,6 +6,7 @@ use EasyCorp\Bundle\EasyAdminBundle\Config\Action; use EasyCorp\Bundle\EasyAdminBundle\Config\Actions; use EasyCorp\Bundle\EasyAdminBundle\Config\Crud; +use EasyCorp\Bundle\EasyAdminBundle\Field\DateTimeField; use EasyCorp\Bundle\EasyAdminBundle\Field\IdField; use EasyCorp\Bundle\EasyAdminBundle\Field\TextField; @@ -21,8 +22,8 @@ public static function getEntityFqcn(): string public function configureCrud(Crud $crud): Crud { return parent::configureCrud($crud) - ->setEntityLabelInPlural(t('Groups')) - ->setEntityLabelInSingular(t('Group')); + ->setEntityLabelInSingular(t('Group')) + ->setEntityLabelInPlural(t('Groups')); } public function configureActions(Actions $actions): Actions @@ -39,5 +40,10 @@ public function configureFields(string $pageName): iterable yield IdField::new('id', t('ID')) ->onlyOnDetail(); yield TextField::new('label', t('Label')); + + yield TextField::new('createdBy', t('Created by')) + ->hideOnForm(); + yield DateTimeField::new('createdAt', t('Created at')) + ->hideOnForm(); } } diff --git a/src/Controller/ProcessOverviewController.php b/src/Controller/ProcessOverviewController.php index 1518411..34841bd 100644 --- a/src/Controller/ProcessOverviewController.php +++ b/src/Controller/ProcessOverviewController.php @@ -7,6 +7,7 @@ use App\ProcessOverviewHelper; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\JsonResponse; +use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; use Symfony\Component\Routing\Attribute\Route; @@ -27,13 +28,25 @@ public function show(ProcessOverviewGroup $group, ProcessOverview $overview): Re 'group' => $group->getId(), 'overview' => $overview->getId(), ]), + 'search_url' => $this->generateUrl('process_overview_search', [ + 'group' => $group->getId(), + 'overview' => $overview->getId(), + ]), ]); } #[Route('/{overview}/data', name: 'data')] - public function data(ProcessOverview $overview, ProcessOverviewHelper $helper): Response + public function data(Request $request, ProcessOverview $overview, ProcessOverviewHelper $helper): Response + { + $data = $helper->getData($request, $overview); + + return new JsonResponse($data); + } + + #[Route('/{overview}/search', name: 'search')] + public function search(Request $request, ProcessOverview $overview, ProcessOverviewHelper $helper): Response { - $data = $helper->getData($overview); + $data = $helper->getData($request, $overview); return new JsonResponse($data); } diff --git a/src/Controller/SecurityController.php b/src/Controller/SecurityController.php new file mode 100644 index 0000000..38a1c71 --- /dev/null +++ b/src/Controller/SecurityController.php @@ -0,0 +1,16 @@ +redirectToRoute('itkdev_openid_connect_login', ['providerKey' => 'admin']); + } +} diff --git a/src/DataSourceHelper.php b/src/DataSourceHelper.php new file mode 100644 index 0000000..b7ab05b --- /dev/null +++ b/src/DataSourceHelper.php @@ -0,0 +1,98 @@ +get($dataSource, 'process'); + } + + public function getProcess(DataSource $dataSource, string $processId): array + { + return $this->get($dataSource, 'process/'.$processId); + } + + public function getProcessRun(DataSource $dataSource, string $processId, array $query): array + { + return $this->get($dataSource, 'process/'.$processId.'/run', $query); + } + + private function get(DataSource $dataSource, string $path, array $query = []): array + { + $url = $this->buildUrl($dataSource, $path, $query); + $options = $this->buildOptions($dataSource); + $response = $this->httpClient->request(Request::METHOD_GET, $url, $options); + + return $response->toArray(); + } + + private function buildUrl(DataSource $dataSource, string $path, array $query): string + { + $url = $dataSource->getUrl(); + + $path = ltrim($path, '/'); + if (!str_starts_with($path, 'api/')) { + $path = self::DEFAULT_API_BASE_PATH.$path; + } + + $url = rtrim($url, '/').'/'.$path; + + if (!empty($query)) { + $url .= (str_contains($url, '?') ? '&' : '?').$this->buildQueryString($query); + } + + return $url; + } + + private function buildOptions(DataSource $dataSource): array + { + $options = []; + + $dataSourceOptions = $this->getOptions($dataSource); + if ($header = ($dataSourceOptions['auth']['header'] ?? null)) { + foreach ($header as $name => $value) { + $options['headers'][$name] = $value; + } + } + + return $options; + } + + private function getOptions(DataSource $dataSource): array + { + try { + $data = Yaml::parse($dataSource->getOptions() ?? ''); + if (is_array($data)) { + return $data; + } + } catch (\Exception) { + } + + return []; + } + + /** + * Build query string with proper handling of list values. + */ + private function buildQueryString(array $params): string + { + // @see https://stackoverflow.com/a/8171667 + $query = http_build_query($params); + + return preg_replace('/%5B\d+%5D(?==)/', '', $query); + } +} diff --git a/src/Entity/DataSource.php b/src/Entity/DataSource.php new file mode 100644 index 0000000..2ae1472 --- /dev/null +++ b/src/Entity/DataSource.php @@ -0,0 +1,119 @@ + + */ + #[ORM\OneToMany(targetEntity: ProcessOverview::class, mappedBy: 'dataSource')] + private Collection $processOverviews; + + #[ORM\Column(length: 255)] + private ?string $url = null; + + public function __construct() + { + $this->processOverviews = new ArrayCollection(); + } + + public function __toString(): string + { + return $this->getLabel(); + } + + public function getId(): ?int + { + return $this->id; + } + + public function getLabel(): ?string + { + return $this->label; + } + + public function setLabel(string $label): static + { + $this->label = $label; + + return $this; + } + + public function getOptions(): ?string + { + return $this->options; + } + + public function setOptions(string $options): static + { + $this->options = $options; + + return $this; + } + + /** + * @return Collection + */ + public function getProcessOverviews(): Collection + { + return $this->processOverviews; + } + + public function addProcessOverview(ProcessOverview $processOverview): static + { + if (!$this->processOverviews->contains($processOverview)) { + $this->processOverviews->add($processOverview); + $processOverview->setDataSource($this); + } + + return $this; + } + + public function removeProcessOverview(ProcessOverview $processOverview): static + { + if ($this->processOverviews->removeElement($processOverview)) { + // set the owning side to null (unless already changed) + if ($processOverview->getDataSource() === $this) { + $processOverview->setDataSource(null); + } + } + + return $this; + } + + public function getUrl(): ?string + { + return $this->url; + } + + public function setUrl(string $url): static + { + $this->url = $url; + + return $this; + } +} diff --git a/src/Entity/ProcessOverview.php b/src/Entity/ProcessOverview.php index 12d6934..7157709 100644 --- a/src/Entity/ProcessOverview.php +++ b/src/Entity/ProcessOverview.php @@ -6,12 +6,19 @@ use Doctrine\Common\Collections\ArrayCollection; use Doctrine\Common\Collections\Collection; use Doctrine\DBAL\Types\Types; +use Doctrine\ORM\Event\PreUpdateEventArgs; use Doctrine\ORM\Mapping as ORM; +use Gedmo\Blameable\Traits\BlameableEntity; +use Gedmo\Timestampable\Traits\TimestampableEntity; #[ORM\Entity(repositoryClass: ProcessOverviewRepository::class)] #[ORM\Table(name: 'rpa_process_overview_process_overview')] +#[ORM\HasLifecycleCallbacks] class ProcessOverview { + use BlameableEntity; + use TimestampableEntity; + #[ORM\Id] #[ORM\GeneratedValue] #[ORM\Column] @@ -30,9 +37,16 @@ class ProcessOverview #[ORM\OneToMany(targetEntity: Process::class, mappedBy: 'process', orphanRemoval: true)] private Collection $steps; - #[ORM\Column(type: Types::TEXT)] + #[ORM\Column(type: Types::TEXT, nullable: true)] private ?string $options = null; + #[ORM\ManyToOne(inversedBy: 'processOverviews')] + #[ORM\JoinColumn(nullable: false)] + private ?DataSource $dataSource = null; + + #[ORM\Column(length: 255, nullable: true)] + private ?string $processId = null; + public function __construct() { $this->steps = new ArrayCollection(); @@ -97,7 +111,7 @@ public function removeStep(Process $step): static return $this; } - public function getOptions(): string + public function getOptions(): ?string { return $this->options; } @@ -108,4 +122,46 @@ public function setOptions(string $options): static return $this; } + + public function getDataSource(): ?DataSource + { + return $this->dataSource; + } + + public function setDataSource(?DataSource $dataSource): static + { + $this->dataSource = $dataSource; + + return $this; + } + + public function getProcessId(): ?string + { + return $this->processId; + } + + public function setProcessId(?string $processId): static + { + $this->processId = $processId; + + return $this; + } + + /** + * Decide if overview is ready for display. + */ + public function isReady(): bool + { + return null !== $this->getProcessId(); + } + + #[ORM\PreUpdate] + public function onPreUpdate(PreUpdateEventArgs $args): void + { + if ($args->hasChangedField('dataSource')) { + /** @var self $object */ + $object = $args->getObject(); + $object->setProcessId(null); + } + } } diff --git a/src/Entity/ProcessOverviewGroup.php b/src/Entity/ProcessOverviewGroup.php index 9269b17..9792716 100644 --- a/src/Entity/ProcessOverviewGroup.php +++ b/src/Entity/ProcessOverviewGroup.php @@ -6,11 +6,16 @@ use Doctrine\Common\Collections\ArrayCollection; use Doctrine\Common\Collections\Collection; use Doctrine\ORM\Mapping as ORM; +use Gedmo\Blameable\Traits\BlameableEntity; +use Gedmo\Timestampable\Traits\TimestampableEntity; #[ORM\Entity(repositoryClass: ProcessOverviewGroupRepository::class)] #[ORM\Table(name: 'rpa_process_overview_process_overview_group')] class ProcessOverviewGroup { + use BlameableEntity; + use TimestampableEntity; + #[ORM\Id] #[ORM\GeneratedValue] #[ORM\Column] diff --git a/src/Entity/User.php b/src/Entity/User.php new file mode 100644 index 0000000..991eef8 --- /dev/null +++ b/src/Entity/User.php @@ -0,0 +1,91 @@ + The user roles + */ + #[ORM\Column] + private array $roles = []; + + public function getId(): ?int + { + return $this->id; + } + + public function getEmail(): ?string + { + return $this->email; + } + + public function setEmail(string $email): static + { + $this->email = $email; + + return $this; + } + + /** + * A visual identifier that represents this user. + * + * @see UserInterface + */ + public function getUserIdentifier(): string + { + return (string) $this->email; + } + + /** + * @see UserInterface + */ + public function getRoles(): array + { + $roles = $this->roles; + // guarantee every user at least has ROLE_USER + $roles[] = 'ROLE_USER'; + + return array_unique($roles); + } + + /** + * @param list $roles + */ + public function setRoles(array $roles): static + { + $this->roles = $roles; + + return $this; + } + + public function __serialize(): array + { + return (array) $this; + } + + #[\Deprecated] + public function eraseCredentials(): void + { + // @deprecated, to be removed when upgrading to Symfony 8 + } +} diff --git a/src/Entity/UserRole.php b/src/Entity/UserRole.php new file mode 100644 index 0000000..fa9a1f7 --- /dev/null +++ b/src/Entity/UserRole.php @@ -0,0 +1,10 @@ +getOptions()); - - $url = $this->getArrayValue($options, 'data_source.url'); - - $response = $this->httpClient->request(Request::METHOD_GET, $url); + $datasource = $overview->getDataSource(); + $processId = $overview->getProcessId(); + if (empty($datasource) || empty($processId)) { + return []; + } - $data = $response->toArray(); + $options = $this->getOptions($overview); - $metadataColumnsOptions = $this->getArrayValue($options, 'metadata_columns') ?? []; + $process = $this->dataSourceHelper->getProcess($datasource, $processId); + $query = $options['data']['default_query'] ?? null; + if (!is_array($query)) { + $query = []; + } + $query += $request->query->all(); + $data = $this->dataSourceHelper->getProcessRun($datasource, $processId, $query); $metadataColumns = []; - $stepColumns = []; + $metadataColumnsOptions = $this->getArrayValue($options, 'metadata_columns') ?? []; foreach ($metadataColumnsOptions as $column) { $metadataColumns[] = $column + [ 'type' => $column['type'] ?? 'text', ]; } + // Add step columns + $stepColumns = []; + foreach ($process['steps'] as $step) { + $stepColumns[] = [ + 'label' => $step['name'], + 'type' => 'step', + ]; + } + $rows = []; - foreach ($data as $index => $item) { + $items = $data['items'] ?? []; + foreach ($items as $item) { $steps = $item['steps'] ?? null; if (!$steps) { break; } - if (0 === $index) { - foreach ($steps as $stepIndex => $step) { - $stepColumns[] = [ - 'label' => $step['label'] ?? $step['name'] ?? $stepIndex, - 'type' => 'step', - ]; - } - } $rows[] = array_merge( array_map(fn (array $col) => [ 'type' => 'text', @@ -61,10 +69,29 @@ public function getData(ProcessOverview $overview): array ); } + $modifier = Modifier::from($request->getUri()); + $page = $data['page'] ?? 1; + $links = [ + 'self' => $modifier->getUriString(), + ]; + if ($page > 1) { + $links['prev'] = $modifier->mergeQueryParameters(['page' => $page - 1])->getUriString(); + } + if ($page < ($data['pages'] ?? 0)) { + $links['next'] = $modifier->mergeQueryParameters(['page' => $page + 1])->getUriString(); + } + $meta = array_filter([ + 'total' => $data['total'] ?? null, + ]); + return [ - 'rows' => $rows, - 'columns' => array_merge($metadataColumns, $stepColumns), - 'data' => $data, + 'data' => [ + 'columns' => array_merge($metadataColumns, $stepColumns), + 'rows' => $rows, + 'data' => $data, + ], + 'links' => $links, + 'meta' => $meta, ]; } catch (\Exception $exception) { // @todo Log the exception @@ -78,4 +105,17 @@ private function getArrayValue(array $array, string $key): mixed return $this->propertyAccessor->getValue($array, $propertyPath); } + + private function getOptions(ProcessOverview $overview): array + { + try { + $data = Yaml::parse($overview->getOptions() ?? ''); + if (is_array($data)) { + return $data; + } + } catch (\Exception) { + } + + return []; + } } diff --git a/src/Repository/DataSourceRepository.php b/src/Repository/DataSourceRepository.php new file mode 100644 index 0000000..6d62ce1 --- /dev/null +++ b/src/Repository/DataSourceRepository.php @@ -0,0 +1,18 @@ + + */ +class DataSourceRepository extends ServiceEntityRepository +{ + public function __construct(ManagerRegistry $registry) + { + parent::__construct($registry, DataSource::class); + } +} diff --git a/src/Repository/UserRepository.php b/src/Repository/UserRepository.php new file mode 100644 index 0000000..70f985b --- /dev/null +++ b/src/Repository/UserRepository.php @@ -0,0 +1,26 @@ + + */ +class UserRepository extends ServiceEntityRepository +{ + public function __construct(ManagerRegistry $registry) + { + parent::__construct($registry, User::class); + } + + public function save(User $user, bool $flush = false): void + { + $this->getEntityManager()->persist($user); + if ($flush) { + $this->getEntityManager()->flush(); + } + } +} diff --git a/src/Security/OidcAuthenticator.php b/src/Security/OidcAuthenticator.php new file mode 100644 index 0000000..1f3dd6f --- /dev/null +++ b/src/Security/OidcAuthenticator.php @@ -0,0 +1,75 @@ +validateClaims($request); + + // Extract properties from claims + $email = $claims['email'] ?? $claims['upn'] ?? null; + $roles = $claims['roles'] ?? $claims['groups'] ?? []; + + // Check if user already exists already or create a new one. + $user = $this->userRepository->findOneBy(['email' => $email]) ?? new User(); + + if (is_array($roles)) { + $map = (array) ($this->options['role_map'] ?? null); + $userRoles = array_map(static fn (string $role) => (array) ($map[$role] ?? null), $roles); + // Flatten and filter out invalid roles. + $userRoles = array_filter(array_merge(...$userRoles), static fn (string $role) => null !== UserRole::tryFrom($role)); + $user->setRoles($userRoles); + } + + $user->setEmail($email); + $this->userRepository->save($user, flush: true); + + return new SelfValidatingPassport(new UserBadge($user->getUserIdentifier())); + } catch (ItkOpenIdConnectException|InvalidProviderException $exception) { + throw new CustomUserMessageAuthenticationException($exception->getMessage()); + } + } + + public function onAuthenticationSuccess(Request $request, TokenInterface $token, string $firewallName): ?Response + { + return new RedirectResponse($this->router->generate('app_default')); + } + + public function start(Request $request, ?AuthenticationException $authException = null): Response + { + return new RedirectResponse($this->router->generate('itkdev_openid_connect_login', [ + 'providerKey' => 'admin', + ])); + } +} diff --git a/src/Twig/Extension/AppExtension.php b/src/Twig/Extension/AppExtension.php new file mode 100644 index 0000000..23ac032 --- /dev/null +++ b/src/Twig/Extension/AppExtension.php @@ -0,0 +1,17 @@ +__/__/' translations/*.da.*xlf diff --git a/templates/_partials/group_header.html.twig b/templates/_partials/group_header.html.twig index 109d188..3d04006 100644 --- a/templates/_partials/group_header.html.twig +++ b/templates/_partials/group_header.html.twig @@ -1,13 +1,15 @@
-

{{ group.label }}

+

{{ group.label }}

{# https://getbootstrap.com/docs/5.3/components/navs-tabs/#tabs #} -
diff --git a/templates/_partials/main_header.html.twig b/templates/_partials/main_header.html.twig index 8e7a660..2db4f21 100644 --- a/templates/_partials/main_header.html.twig +++ b/templates/_partials/main_header.html.twig @@ -1,6 +1,6 @@