feat: collect infrastructure dependency inventories
Module Package Release / publish-packages (push) Successful in 13s
Module Package Release / publish-packages (push) Successful in 13s
This commit is contained in:
@@ -200,6 +200,19 @@ Feature providers remain responsible for their own semantics:
|
||||
Ops projects the same Core-validated receipt. It must not maintain a second
|
||||
parser with different validation or secret-handling rules.
|
||||
|
||||
Core also defines the inverse, read-only dependency-inventory contract used
|
||||
before the installer changes one of those infrastructure capabilities. An
|
||||
enabled module registers
|
||||
`infrastructure.dependency_inventory.<module_id>` and returns bounded, stable
|
||||
references to its persisted configuration or data, a lifecycle state, scope,
|
||||
numeric metrics, and a required operator action. Providers must not return
|
||||
secrets or use this read to migrate state. The Core collector validates provider
|
||||
identity and capability coverage, orders records deterministically, and marks
|
||||
the complete inventory failed when any provider raises or violates the
|
||||
contract. Ops is the authorized projection boundary; the installer remains the
|
||||
consumer and must match installation id, freshness, completion and impacted
|
||||
capability coverage before apply.
|
||||
|
||||
The admin wizard backend starts with these routes:
|
||||
|
||||
- `GET /api/v1/admin/configuration-packages/catalog`
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "govoplan-core"
|
||||
version = "0.1.41"
|
||||
version = "0.1.42"
|
||||
description = "Reusable GovOPlaN platform core, access, tenancy, and RBAC components."
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.12"
|
||||
|
||||
@@ -2,14 +2,18 @@ from __future__ import annotations
|
||||
|
||||
from collections.abc import Mapping
|
||||
from dataclasses import dataclass
|
||||
from datetime import UTC, datetime
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
import re
|
||||
from typing import Any
|
||||
from typing import Any, Protocol, runtime_checkable
|
||||
|
||||
|
||||
DEPLOYMENT_CAPABILITIES_ENV = "GOVOPLAN_DEPLOYMENT_CAPABILITIES_PATH"
|
||||
INFRASTRUCTURE_DEPENDENCY_PROVIDER_CAPABILITY_PREFIX = (
|
||||
"infrastructure.dependency_inventory."
|
||||
)
|
||||
MAX_CAPABILITY_DOCUMENT_BYTES = 256 * 1024
|
||||
CAPABILITY_STATES = frozenset(
|
||||
{
|
||||
@@ -20,12 +24,125 @@ CAPABILITY_STATES = frozenset(
|
||||
}
|
||||
)
|
||||
_ENV_REFERENCE_RE = re.compile(r"^env:[A-Za-z_][A-Za-z0-9_]*$")
|
||||
_DEPENDENCY_STATES = frozenset(
|
||||
{"active", "inactive", "data_present", "pending_work", "runtime_binding"}
|
||||
)
|
||||
|
||||
|
||||
class InfrastructureCapabilityReceiptError(ValueError):
|
||||
pass
|
||||
|
||||
|
||||
@dataclass(frozen=True, slots=True)
|
||||
class InfrastructureDependency:
|
||||
"""A non-secret module-owned dependency on deployment infrastructure."""
|
||||
|
||||
capability_id: str
|
||||
module_id: str
|
||||
dependency_type: str
|
||||
dependency_ref: str
|
||||
state: str
|
||||
scope: str
|
||||
summary: str
|
||||
metrics: Mapping[str, int]
|
||||
required_action: str
|
||||
|
||||
def __post_init__(self) -> None:
|
||||
for field_name, value, maximum in (
|
||||
("capability_id", self.capability_id, 120),
|
||||
("module_id", self.module_id, 120),
|
||||
("dependency_type", self.dependency_type, 120),
|
||||
("dependency_ref", self.dependency_ref, 240),
|
||||
("scope", self.scope, 120),
|
||||
("summary", self.summary, 1000),
|
||||
("required_action", self.required_action, 1000),
|
||||
):
|
||||
if (
|
||||
not value.strip()
|
||||
or len(value) > maximum
|
||||
or any(ord(char) < 32 for char in value)
|
||||
):
|
||||
raise ValueError(
|
||||
f"Infrastructure dependency {field_name} is invalid."
|
||||
)
|
||||
if self.state not in _DEPENDENCY_STATES:
|
||||
raise ValueError(
|
||||
f"Infrastructure dependency state is unsupported: {self.state!r}."
|
||||
)
|
||||
if len(self.metrics) > 20 or any(
|
||||
not isinstance(key, str)
|
||||
or not key.strip()
|
||||
or len(key) > 80
|
||||
or any(ord(char) < 32 for char in key)
|
||||
or type(value) is not int
|
||||
or value < 0
|
||||
for key, value in self.metrics.items()
|
||||
):
|
||||
raise ValueError("Infrastructure dependency metrics are invalid.")
|
||||
|
||||
def to_dict(self) -> dict[str, object]:
|
||||
return {
|
||||
"capability_id": self.capability_id,
|
||||
"module_id": self.module_id,
|
||||
"dependency_type": self.dependency_type,
|
||||
"dependency_ref": self.dependency_ref,
|
||||
"state": self.state,
|
||||
"scope": self.scope,
|
||||
"summary": self.summary,
|
||||
"metrics": dict(sorted(self.metrics.items())),
|
||||
"required_action": self.required_action,
|
||||
}
|
||||
|
||||
|
||||
@runtime_checkable
|
||||
class InfrastructureDependencyProvider(Protocol):
|
||||
module_id: str
|
||||
capability_ids: tuple[str, ...]
|
||||
|
||||
def infrastructure_dependencies(self) -> tuple[InfrastructureDependency, ...]:
|
||||
...
|
||||
|
||||
|
||||
@dataclass(frozen=True, slots=True)
|
||||
class InfrastructureDependencyProviderReport:
|
||||
module_id: str
|
||||
capability_ids: tuple[str, ...]
|
||||
state: str
|
||||
dependency_count: int
|
||||
error: str | None = None
|
||||
|
||||
def to_dict(self) -> dict[str, object]:
|
||||
return {
|
||||
"module_id": self.module_id,
|
||||
"capability_ids": list(self.capability_ids),
|
||||
"state": self.state,
|
||||
"dependency_count": self.dependency_count,
|
||||
"error": self.error,
|
||||
}
|
||||
|
||||
|
||||
@dataclass(frozen=True, slots=True)
|
||||
class InfrastructureDependencyInventory:
|
||||
installation_id: str
|
||||
generated_at: str
|
||||
complete: bool
|
||||
inspected_capability_ids: tuple[str, ...]
|
||||
providers: tuple[InfrastructureDependencyProviderReport, ...]
|
||||
dependencies: tuple[InfrastructureDependency, ...]
|
||||
schema_version: int = 1
|
||||
|
||||
def to_dict(self) -> dict[str, object]:
|
||||
return {
|
||||
"schema_version": self.schema_version,
|
||||
"installation_id": self.installation_id,
|
||||
"generated_at": self.generated_at,
|
||||
"complete": self.complete,
|
||||
"inspected_capability_ids": list(self.inspected_capability_ids),
|
||||
"providers": [item.to_dict() for item in self.providers],
|
||||
"dependencies": [item.to_dict() for item in self.dependencies],
|
||||
}
|
||||
|
||||
|
||||
@dataclass(frozen=True, slots=True)
|
||||
class InfrastructureCapability:
|
||||
id: str
|
||||
@@ -214,6 +331,134 @@ def deployment_capability_status(
|
||||
}
|
||||
|
||||
|
||||
def collect_infrastructure_dependency_inventory(
|
||||
registry: object,
|
||||
*,
|
||||
installation_id: str,
|
||||
observed_at: datetime | None = None,
|
||||
) -> InfrastructureDependencyInventory:
|
||||
"""Collect actual module-owned dependencies without importing module internals."""
|
||||
|
||||
normalized_installation_id = installation_id.strip()
|
||||
if not normalized_installation_id or len(normalized_installation_id) > 100:
|
||||
raise ValueError("Infrastructure dependency installation id is invalid.")
|
||||
capability_names = getattr(registry, "capability_names", None)
|
||||
capability = getattr(registry, "capability", None)
|
||||
if not callable(capability_names) or not callable(capability):
|
||||
raise ValueError("Infrastructure dependency inventory requires a module registry.")
|
||||
|
||||
provider_names = tuple(
|
||||
name
|
||||
for name in capability_names()
|
||||
if isinstance(name, str)
|
||||
and name.startswith(INFRASTRUCTURE_DEPENDENCY_PROVIDER_CAPABILITY_PREFIX)
|
||||
)
|
||||
reports: list[InfrastructureDependencyProviderReport] = []
|
||||
dependencies: list[InfrastructureDependency] = []
|
||||
inspected_capability_ids: set[str] = set()
|
||||
complete = True
|
||||
|
||||
for provider_name in sorted(provider_names):
|
||||
expected_module_id = provider_name.removeprefix(
|
||||
INFRASTRUCTURE_DEPENDENCY_PROVIDER_CAPABILITY_PREFIX
|
||||
)
|
||||
module_id = expected_module_id or "unknown"
|
||||
declared_ids: tuple[str, ...] = ()
|
||||
try:
|
||||
provider = capability(provider_name)
|
||||
if not isinstance(provider, InfrastructureDependencyProvider):
|
||||
raise TypeError("provider does not implement the inventory contract")
|
||||
module_id = provider.module_id.strip()
|
||||
declared_ids = tuple(
|
||||
sorted(
|
||||
{
|
||||
item.strip()
|
||||
for item in provider.capability_ids
|
||||
if isinstance(item, str) and item.strip()
|
||||
}
|
||||
)
|
||||
)
|
||||
if (
|
||||
module_id != expected_module_id
|
||||
or len(module_id) > 120
|
||||
or any(ord(char) < 32 for char in module_id)
|
||||
or not declared_ids
|
||||
or len(declared_ids) > 30
|
||||
or any(
|
||||
len(item) > 120 or any(ord(char) < 32 for char in item)
|
||||
for item in declared_ids
|
||||
)
|
||||
):
|
||||
raise ValueError("provider identity or capability declaration is invalid")
|
||||
provider_dependencies = tuple(provider.infrastructure_dependencies())
|
||||
if len(provider_dependencies) > 10_000:
|
||||
raise ValueError("provider dependency inventory is too large")
|
||||
seen_refs: set[tuple[str, str, str]] = set()
|
||||
for item in provider_dependencies:
|
||||
if not isinstance(item, InfrastructureDependency):
|
||||
raise TypeError("provider returned an invalid dependency")
|
||||
if item.module_id != module_id or item.capability_id not in declared_ids:
|
||||
raise ValueError("provider returned a dependency outside its declaration")
|
||||
identity = (
|
||||
item.capability_id,
|
||||
item.dependency_type,
|
||||
item.dependency_ref,
|
||||
)
|
||||
if identity in seen_refs:
|
||||
raise ValueError("provider returned a duplicate dependency")
|
||||
seen_refs.add(identity)
|
||||
if len(dependencies) + len(provider_dependencies) > 10_000:
|
||||
raise ValueError("combined dependency inventory is too large")
|
||||
dependencies.extend(provider_dependencies)
|
||||
inspected_capability_ids.update(declared_ids)
|
||||
reports.append(
|
||||
InfrastructureDependencyProviderReport(
|
||||
module_id=module_id,
|
||||
capability_ids=declared_ids,
|
||||
state="complete",
|
||||
dependency_count=len(provider_dependencies),
|
||||
)
|
||||
)
|
||||
except Exception as exc:
|
||||
complete = False
|
||||
inspected_capability_ids.update(declared_ids)
|
||||
reports.append(
|
||||
InfrastructureDependencyProviderReport(
|
||||
module_id=module_id,
|
||||
capability_ids=declared_ids,
|
||||
state="error",
|
||||
dependency_count=0,
|
||||
error=(
|
||||
f"{type(exc).__name__}: provider inventory could not be completed"
|
||||
),
|
||||
)
|
||||
)
|
||||
|
||||
timestamp = observed_at or datetime.now(UTC)
|
||||
if timestamp.tzinfo is None:
|
||||
timestamp = timestamp.replace(tzinfo=UTC)
|
||||
return InfrastructureDependencyInventory(
|
||||
installation_id=normalized_installation_id,
|
||||
generated_at=timestamp.astimezone(UTC).isoformat(),
|
||||
complete=complete,
|
||||
inspected_capability_ids=tuple(sorted(inspected_capability_ids)),
|
||||
providers=tuple(
|
||||
sorted(reports, key=lambda item: (item.module_id, item.capability_ids))
|
||||
),
|
||||
dependencies=tuple(
|
||||
sorted(
|
||||
dependencies,
|
||||
key=lambda item: (
|
||||
item.capability_id,
|
||||
item.module_id,
|
||||
item.dependency_type,
|
||||
item.dependency_ref,
|
||||
),
|
||||
)
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
def _capability(value: object) -> InfrastructureCapability:
|
||||
if not isinstance(value, Mapping):
|
||||
raise InfrastructureCapabilityReceiptError(
|
||||
@@ -352,10 +597,16 @@ def _unavailable_status(*, configured: bool, error: str | None) -> dict[str, obj
|
||||
__all__ = [
|
||||
"CAPABILITY_STATES",
|
||||
"DEPLOYMENT_CAPABILITIES_ENV",
|
||||
"INFRASTRUCTURE_DEPENDENCY_PROVIDER_CAPABILITY_PREFIX",
|
||||
"InfrastructureCapability",
|
||||
"InfrastructureCapabilityReceipt",
|
||||
"InfrastructureCapabilityReceiptError",
|
||||
"InfrastructureDependency",
|
||||
"InfrastructureDependencyInventory",
|
||||
"InfrastructureDependencyProvider",
|
||||
"InfrastructureDependencyProviderReport",
|
||||
"InfrastructurePostInstallTask",
|
||||
"collect_infrastructure_dependency_inventory",
|
||||
"deployment_capability_status",
|
||||
"infrastructure_capability_receipt_from_mapping",
|
||||
"load_infrastructure_capability_receipt",
|
||||
|
||||
@@ -2,6 +2,7 @@ from __future__ import annotations
|
||||
|
||||
import json
|
||||
import os
|
||||
from datetime import UTC, datetime
|
||||
from pathlib import Path
|
||||
import tempfile
|
||||
import unittest
|
||||
@@ -9,12 +10,53 @@ from unittest.mock import patch
|
||||
|
||||
from govoplan_core.core.infrastructure_capabilities import (
|
||||
InfrastructureCapabilityReceiptError,
|
||||
InfrastructureDependency,
|
||||
collect_infrastructure_dependency_inventory,
|
||||
deployment_capability_status,
|
||||
infrastructure_capability_receipt_from_mapping,
|
||||
load_infrastructure_capability_receipt,
|
||||
)
|
||||
|
||||
|
||||
class _InventoryProvider:
|
||||
module_id = "mail"
|
||||
capability_ids = ("mail.smtp",)
|
||||
|
||||
def infrastructure_dependencies(self) -> tuple[InfrastructureDependency, ...]:
|
||||
return (
|
||||
InfrastructureDependency(
|
||||
capability_id="mail.smtp",
|
||||
module_id="mail",
|
||||
dependency_type="smtp_endpoint",
|
||||
dependency_ref="mail-server:server-1",
|
||||
state="active",
|
||||
scope="system",
|
||||
summary="One active SMTP endpoint uses the deployment relay.",
|
||||
metrics={"credential_binding_count": 1},
|
||||
required_action="Rebind or retire the endpoint before removal.",
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
class _FailingInventoryProvider:
|
||||
module_id = "files"
|
||||
capability_ids = ("files.storage",)
|
||||
|
||||
def infrastructure_dependencies(self) -> tuple[InfrastructureDependency, ...]:
|
||||
raise RuntimeError("database URL must not escape")
|
||||
|
||||
|
||||
class _Registry:
|
||||
def __init__(self, providers: dict[str, object]) -> None:
|
||||
self.providers = providers
|
||||
|
||||
def capability_names(self) -> tuple[str, ...]:
|
||||
return tuple(self.providers)
|
||||
|
||||
def capability(self, name: str) -> object | None:
|
||||
return self.providers.get(name)
|
||||
|
||||
|
||||
def _receipt_payload() -> dict[str, object]:
|
||||
return {
|
||||
"schema_version": 1,
|
||||
@@ -48,6 +90,40 @@ def _receipt_payload() -> dict[str, object]:
|
||||
|
||||
|
||||
class InfrastructureCapabilityReceiptTests(unittest.TestCase):
|
||||
def test_collects_non_secret_provider_dependency_inventory(self) -> None:
|
||||
inventory = collect_infrastructure_dependency_inventory(
|
||||
_Registry(
|
||||
{
|
||||
"infrastructure.dependency_inventory.mail": _InventoryProvider(),
|
||||
"unrelated.capability": object(),
|
||||
}
|
||||
),
|
||||
installation_id="govoplan-test",
|
||||
observed_at=datetime(2026, 8, 24, 12, 0, tzinfo=UTC),
|
||||
)
|
||||
|
||||
self.assertTrue(inventory.complete)
|
||||
self.assertEqual(("mail.smtp",), inventory.inspected_capability_ids)
|
||||
self.assertEqual("mail-server:server-1", inventory.dependencies[0].dependency_ref)
|
||||
self.assertEqual("2026-08-24T12:00:00+00:00", inventory.generated_at)
|
||||
self.assertNotIn("database URL", json.dumps(inventory.to_dict()))
|
||||
|
||||
def test_provider_failure_makes_inventory_incomplete_without_leaking_error(self) -> None:
|
||||
inventory = collect_infrastructure_dependency_inventory(
|
||||
_Registry(
|
||||
{
|
||||
"infrastructure.dependency_inventory.files": (
|
||||
_FailingInventoryProvider()
|
||||
)
|
||||
}
|
||||
),
|
||||
installation_id="govoplan-test",
|
||||
)
|
||||
|
||||
self.assertFalse(inventory.complete)
|
||||
self.assertEqual("error", inventory.providers[0].state)
|
||||
self.assertNotIn("database URL", str(inventory.providers[0].error))
|
||||
|
||||
def test_parses_typed_capability_and_task_lookup(self) -> None:
|
||||
receipt = infrastructure_capability_receipt_from_mapping(_receipt_payload())
|
||||
|
||||
|
||||
Generated
+2
-2
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@govoplan/core-webui",
|
||||
"version": "0.1.41",
|
||||
"version": "0.1.42",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@govoplan/core-webui",
|
||||
"version": "0.1.41",
|
||||
"version": "0.1.42",
|
||||
"dependencies": {
|
||||
"@govoplan/access-webui": "file:../../govoplan-access/webui",
|
||||
"@govoplan/addresses-webui": "file:../../govoplan-addresses/webui",
|
||||
|
||||
@@ -1,28 +1,28 @@
|
||||
{
|
||||
"name": "@govoplan/core-webui",
|
||||
"version": "0.1.41",
|
||||
"version": "0.1.42",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@govoplan/core-webui",
|
||||
"version": "0.1.41",
|
||||
"version": "0.1.42",
|
||||
"dependencies": {
|
||||
"@govoplan/access-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-access.git#v0.1.23",
|
||||
"@govoplan/admin-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-admin.git#v0.1.22",
|
||||
"@govoplan/audit-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-audit.git#v0.1.18",
|
||||
"@govoplan/audit-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-audit.git#v0.1.20",
|
||||
"@govoplan/calendar-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-calendar.git#v0.1.22",
|
||||
"@govoplan/campaign-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-campaign.git#v0.1.27",
|
||||
"@govoplan/cases-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-cases.git#v0.1.20",
|
||||
"@govoplan/dashboard-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-dashboard.git#v0.1.18",
|
||||
"@govoplan/dashboard-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-dashboard.git#v0.1.20",
|
||||
"@govoplan/docs-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-docs.git#v0.1.22",
|
||||
"@govoplan/files-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-files.git#v0.1.23",
|
||||
"@govoplan/files-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-files.git#v0.1.24",
|
||||
"@govoplan/helpdesk-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-helpdesk.git#v0.1.20",
|
||||
"@govoplan/idm-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-idm.git#v0.1.24",
|
||||
"@govoplan/mail-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-mail.git#v0.1.25",
|
||||
"@govoplan/ops-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-ops.git#v0.1.18",
|
||||
"@govoplan/mail-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-mail.git#v0.1.26",
|
||||
"@govoplan/ops-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-ops.git#v0.1.21",
|
||||
"@govoplan/organizations-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-organizations.git#v0.1.20",
|
||||
"@govoplan/policy-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-policy.git#v0.1.18",
|
||||
"@govoplan/policy-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-policy.git#v0.1.22",
|
||||
"@govoplan/tickets-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-tickets.git#v0.1.22",
|
||||
"@govoplan/wiki-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-wiki.git#v0.1.22",
|
||||
"@tiptap/core": "^3.29.2",
|
||||
@@ -789,8 +789,8 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@govoplan/audit-webui": {
|
||||
"version": "0.1.18",
|
||||
"resolved": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-audit.git#6e63f6920ad5fb71f5d40e785eaed263905d658c",
|
||||
"version": "0.1.20",
|
||||
"resolved": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-audit.git#f2d7b9b29a497607d20f57886448247a8561df8d",
|
||||
"peerDependencies": {
|
||||
"@govoplan/core-webui": "^0.1.18",
|
||||
"lucide-react": "^1.23.0",
|
||||
@@ -859,8 +859,8 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@govoplan/dashboard-webui": {
|
||||
"version": "0.1.18",
|
||||
"resolved": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-dashboard.git#ede16c5439f71ed36b50994d5ad7f656212a43c4",
|
||||
"version": "0.1.20",
|
||||
"resolved": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-dashboard.git#ec240ed2199637a68751e348887908a82d4cfe65",
|
||||
"peerDependencies": {
|
||||
"@govoplan/core-webui": "^0.1.18",
|
||||
"lucide-react": "^1.23.0",
|
||||
@@ -894,8 +894,8 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@govoplan/files-webui": {
|
||||
"version": "0.1.23",
|
||||
"resolved": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-files.git#11b9b7c4c6d919b9f6649034445edfea0c77d042",
|
||||
"version": "0.1.24",
|
||||
"resolved": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-files.git#6176e9f40e070387f03c15d10b3b3d2f132508b5",
|
||||
"peerDependencies": {
|
||||
"@govoplan/core-webui": "^0.1.18",
|
||||
"@vitejs/plugin-react": "^5.2.0",
|
||||
@@ -948,8 +948,8 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@govoplan/mail-webui": {
|
||||
"version": "0.1.25",
|
||||
"resolved": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-mail.git#ecc1283de76f154919c786cb84354afb8f2299c1",
|
||||
"version": "0.1.26",
|
||||
"resolved": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-mail.git#c62c7783d6f522b7ec13063024885bb88e257012",
|
||||
"peerDependencies": {
|
||||
"@govoplan/core-webui": "^0.1.18",
|
||||
"lucide-react": "^1.23.0",
|
||||
@@ -964,8 +964,8 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@govoplan/ops-webui": {
|
||||
"version": "0.1.18",
|
||||
"resolved": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-ops.git#71666db45c8e602ea439495017e5849cad44ec4b",
|
||||
"version": "0.1.21",
|
||||
"resolved": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-ops.git#ac6b211827571c69b058fc857bbddd15125519c6",
|
||||
"peerDependencies": {
|
||||
"@govoplan/core-webui": "^0.1.18",
|
||||
"@vitejs/plugin-react": "^5.2.0",
|
||||
@@ -1002,8 +1002,8 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@govoplan/policy-webui": {
|
||||
"version": "0.1.18",
|
||||
"resolved": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-policy.git#65159dec5fb6594fddfc2c5f8ab199c1e23994a2",
|
||||
"version": "0.1.22",
|
||||
"resolved": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-policy.git#29a9aea3b186b45fcf1b7bed9ebbd1712390f5c6",
|
||||
"peerDependencies": {
|
||||
"@govoplan/core-webui": "^0.1.18",
|
||||
"lucide-react": "^1.23.0",
|
||||
@@ -2127,9 +2127,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/electron-to-chromium": {
|
||||
"version": "1.5.412",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.412.tgz",
|
||||
"integrity": "sha512-z4rMe3esBzlzovKHj4gxJnsCGZRK5l4baUvm+gCGJBPE+gsyUMKsuU9tnEUtI1dOebXz1ytAPGjvXhmQ7rIPwA==",
|
||||
"version": "1.5.413",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.413.tgz",
|
||||
"integrity": "sha512-F1XPKvt7HVfly5WND90ec16nFsdr4g5x/cVUP3EqjeyXynupabGDqpMa84wwvuYGDnldXLBz6DLXyZXWO9TPvw==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/esbuild": {
|
||||
@@ -2289,9 +2289,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/lucide-react": {
|
||||
"version": "1.33.0",
|
||||
"resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-1.33.0.tgz",
|
||||
"integrity": "sha512-MTRwMy0ZlL8Ur/vOAiJ9XGHE+kFPC7brq6MxAm0GiGXEBj0qy0jA/pG4N675oSzciO/UCdX8T+5yUQdmDeTLxg==",
|
||||
"version": "1.34.0",
|
||||
"resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-1.34.0.tgz",
|
||||
"integrity": "sha512-vnjGJNI7Htk5+oWW8gXGuaLgwgAb0T6/iZbBrp9JCfRFwdNWZ0YTm3eyxjOLgwN6r8iyAf3UA70zNmBRBNv7yg==",
|
||||
"license": "ISC",
|
||||
"peerDependencies": {
|
||||
"react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@govoplan/core-webui",
|
||||
"version": "0.1.41",
|
||||
"version": "0.1.42",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@govoplan/core-webui",
|
||||
"version": "0.1.41",
|
||||
"version": "0.1.42",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
@@ -28,19 +28,19 @@
|
||||
"dependencies": {
|
||||
"@govoplan/access-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-access.git#v0.1.23",
|
||||
"@govoplan/admin-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-admin.git#v0.1.22",
|
||||
"@govoplan/audit-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-audit.git#v0.1.18",
|
||||
"@govoplan/audit-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-audit.git#v0.1.20",
|
||||
"@govoplan/calendar-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-calendar.git#v0.1.22",
|
||||
"@govoplan/cases-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-cases.git#v0.1.20",
|
||||
"@govoplan/dashboard-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-dashboard.git#v0.1.18",
|
||||
"@govoplan/dashboard-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-dashboard.git#v0.1.20",
|
||||
"@govoplan/docs-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-docs.git#v0.1.22",
|
||||
"@govoplan/files-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-files.git#v0.1.23",
|
||||
"@govoplan/files-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-files.git#v0.1.24",
|
||||
"@govoplan/helpdesk-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-helpdesk.git#v0.1.20",
|
||||
"@govoplan/idm-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-idm.git#v0.1.24",
|
||||
"@govoplan/mail-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-mail.git#v0.1.25",
|
||||
"@govoplan/mail-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-mail.git#v0.1.26",
|
||||
"@govoplan/campaign-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-campaign.git#v0.1.27",
|
||||
"@govoplan/organizations-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-organizations.git#v0.1.20",
|
||||
"@govoplan/ops-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-ops.git#v0.1.18",
|
||||
"@govoplan/policy-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-policy.git#v0.1.18",
|
||||
"@govoplan/ops-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-ops.git#v0.1.21",
|
||||
"@govoplan/policy-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-policy.git#v0.1.22",
|
||||
"@govoplan/tickets-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-tickets.git#v0.1.22",
|
||||
"@govoplan/wiki-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-wiki.git#v0.1.22",
|
||||
"@tiptap/core": "^3.29.2",
|
||||
|
||||
Reference in New Issue
Block a user