Release v0.1.16
This commit is contained in:
@@ -0,0 +1,23 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from importlib.util import module_from_spec, spec_from_file_location
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
_path = (
|
||||
Path(__file__).resolve().parents[1]
|
||||
/ "versions"
|
||||
/ "a36d8e4f9b12_german_reference_locale.py"
|
||||
)
|
||||
_spec = spec_from_file_location("govoplan_german_reference_locale_migration", _path)
|
||||
if _spec is None or _spec.loader is None:
|
||||
raise RuntimeError(f"Unable to load migration implementation from {_path}")
|
||||
_module = module_from_spec(_spec)
|
||||
_spec.loader.exec_module(_module)
|
||||
|
||||
revision = _module.revision
|
||||
down_revision = _module.down_revision
|
||||
branch_labels = _module.branch_labels
|
||||
depends_on = _module.depends_on
|
||||
upgrade = _module.upgrade
|
||||
downgrade = _module.downgrade
|
||||
@@ -0,0 +1,44 @@
|
||||
"""adopt German as the untouched system reference locale
|
||||
|
||||
Revision ID: a36d8e4f9b12
|
||||
Revises: f25c9d3e7a01
|
||||
Create Date: 2026-08-05 00:00:00.000000
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
revision = "a36d8e4f9b12"
|
||||
down_revision = "f25c9d3e7a01"
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
bind = op.get_bind()
|
||||
if "core_system_settings" not in set(sa.inspect(bind).get_table_names()):
|
||||
return
|
||||
|
||||
settings = sa.table(
|
||||
"core_system_settings",
|
||||
sa.column("id", sa.String),
|
||||
sa.column("default_locale", sa.String),
|
||||
sa.column("created_at", sa.DateTime(timezone=True)),
|
||||
sa.column("updated_at", sa.DateTime(timezone=True)),
|
||||
)
|
||||
bind.execute(
|
||||
settings.update()
|
||||
.where(settings.c.id == "global")
|
||||
.where(settings.c.default_locale == "en")
|
||||
.where(settings.c.created_at == settings.c.updated_at)
|
||||
.values(default_locale="de", updated_at=sa.func.now())
|
||||
)
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
# Locale selection is user-visible state. A downgrade must not overwrite a
|
||||
# German value that may have been selected explicitly after this migration.
|
||||
pass
|
||||
@@ -28,6 +28,12 @@ manifest, allowing the shell to link directly to an exact topic when possible.
|
||||
Docs still performs the authoritative audience, permission, configured-state,
|
||||
and documentation-type filtering.
|
||||
|
||||
Core also maps explicit route, navigation, settings, and View surface IDs to
|
||||
the module's static user or administrator documentation baseline. This makes a
|
||||
page association complete by default and gives every derived field/action
|
||||
context a useful fallback. Exact `metadata.help_contexts` remain the preferred
|
||||
authoring mechanism for consequential or unfamiliar controls.
|
||||
|
||||
When there is no exact topic, Docs resolves the page fallback and then the first
|
||||
visible topic owned by the module. If Docs is unavailable, the shell opens the
|
||||
hosted documentation with the same context parameters.
|
||||
|
||||
@@ -18,7 +18,9 @@ operator, and roadmap pages.
|
||||
| External references and integration maturity | `EXTERNAL_REFERENCES_AND_INTEGRATION_MATURITY.md` | Stable external identity and cumulative connector maturity; configured source authority is defined by the meta target architecture. |
|
||||
| Institutional context and governed references | `INSTITUTIONAL_CONTEXT_CONTRACT.md` | Shared temporal, actor/representation, institution, mandate, service, party, decision, evidence, legal-basis, information-governance, presentation, and geo DTO/provider contracts. |
|
||||
| Temporal data read context | `TEMPORAL_DATA_CONTEXT.md` | Valid-time and recorded-time titlebar selection, HTTP/cache contract, security boundary, and module-adoption rule. |
|
||||
| Cross-module information governance adoption | `INFORMATION_GOVERNANCE_ADOPTION.md` | Manifest evidence and enforcement rules for temporal browsing, purpose-aware access, retention, and institutional context. |
|
||||
| Context-sensitive F1 help | `CONTEXTUAL_HELP_CONTRACT.md` | Focus, route, module-manifest documentation contexts, Docs projection, and hosted fallback. |
|
||||
| German localization and help quality gate | `LOCALIZATION_AND_HELP_QUALITY.md` | German reference locale, new-installation default, catalog completeness, automatic page associations, and explicit-help review priorities. |
|
||||
| Postbox E2EE target architecture | `POSTBOX_E2EE_ARCHITECTURE.md` | Strategic encrypted postbox/mailbox model, key ownership, role mailbox semantics, and retraction limits. |
|
||||
| Shared state, runtime coordination, and recovery | `STATE_AND_RECOVERY_CONTRACT.md` | State profiles, object storage, node registration/drain, fenced leases, migration ordering, and recovery evidence. |
|
||||
| Module lifecycle recovery | `MODULE_LIFECYCLE_RECOVERY.md` | Installer/live-graph recovery modes, deployment fence, evidence, retry blocking, and operator reconciliation. |
|
||||
@@ -39,6 +41,8 @@ operator, and roadmap pages.
|
||||
| Topic | Canonical document | Notes |
|
||||
| --- | --- | --- |
|
||||
| Product roadmap and module routing | `GOVOPLAN_MASTER_ROADMAP.md` | Product-level sequencing, implementation gates, issue routing, and missing-module decisions. |
|
||||
| Stable platform ideas | `govoplan/docs/PLATFORM_CORE_IDEAS.md` | Cross-product thesis, canonical distinctions, product experience rule, maturity rule, and decision test. |
|
||||
| Current cross-product reconciliation | `govoplan/docs/STRATEGY_STATUS.md` | The only current prose status source; generated evidence and Gitea remain authoritative inputs. |
|
||||
| Institutional governance target | `govoplan/docs/INSTITUTIONAL_GOVERNANCE_TARGET_ARCHITECTURE.md` | Cross-product semantic layers, source-authority modes, candidate Mandates/Services/Parties/Decisions boundaries, and migration sequence. |
|
||||
| UI/UX decisions | `UI_UX_DECISION_LEDGER.md` | Binding guided-UI decisions, open decisions, impact index, and review checklist. |
|
||||
| Core interface migration | `INTERFACE_PATTERN_MIGRATION.md` | Core-owned settings, credential, retention, lifecycle, and shared-component evidence for the product pattern language. |
|
||||
|
||||
@@ -0,0 +1,131 @@
|
||||
# Information Governance Adoption
|
||||
|
||||
## Platform Rule
|
||||
|
||||
Temporal browsing, purpose-aware access, retention, and institutional context
|
||||
are platform-wide information-governance dimensions. Every module receives the
|
||||
same contract by default. A module may claim `partial` or `enforced` only with
|
||||
repository-owned object scope, evidence, and limitations; it may claim
|
||||
`not_applicable` only when the dimension genuinely does not apply.
|
||||
|
||||
Historical business data is always authorized under the current security
|
||||
state. No module may use a historical permission, membership, role, function
|
||||
assignment, or policy projection to weaken present-day access.
|
||||
|
||||
Platform-wide adoption is tracked in
|
||||
[GovOPlaN #40](https://git.add-ideas.de/GovOPlaN/govoplan/issues/40), with
|
||||
temporal reads detailed in
|
||||
[GovOPlaN #39](https://git.add-ideas.de/GovOPlaN/govoplan/issues/39).
|
||||
|
||||
## Manifest Declaration
|
||||
|
||||
`ModuleManifest.information_governance` publishes four dimensions:
|
||||
|
||||
- `temporal_browsing`;
|
||||
- `purpose_aware_access`;
|
||||
- `retention`;
|
||||
- `institutional_context`.
|
||||
|
||||
Each dimension declares:
|
||||
|
||||
- adoption: `not_applicable`, `contract_only`, `partial`, or `enforced`;
|
||||
- object types covered;
|
||||
- repository-local test/documentation evidence;
|
||||
- the remaining limitation for `contract_only` or `partial`.
|
||||
|
||||
The default is intentionally `contract_only`. It applies the platform rule
|
||||
without pretending that existing domain queries and effects already enforce
|
||||
it. `reference_ready`, `supported`, and `lts` modules cannot retain an
|
||||
applicable dimension below `enforced`.
|
||||
|
||||
## Read Contract
|
||||
|
||||
For every persistent domain object, the owner classifies the read:
|
||||
|
||||
1. **Current-only:** historical semantics do not exist and the API says so.
|
||||
2. **Valid-time:** select facts effective now or at the requested instant.
|
||||
3. **Bitemporal:** additionally select only revisions known by `recorded_at`.
|
||||
4. **All-validity:** return effective revisions in a bounded history view.
|
||||
|
||||
The Core temporal middleware supplies the request context. Owners apply it in
|
||||
repositories or query helpers, include it in cache keys, return evaluated
|
||||
context, and test current/at/all plus recorded-time boundaries. Search,
|
||||
reporting, exports, selectors, counts, and drill-through must use the same
|
||||
projection as the owning list/detail API.
|
||||
|
||||
## Purpose-Aware Access Contract
|
||||
|
||||
Permission establishes a technical action ceiling. Purpose-aware access asks
|
||||
whether this actor, represented capacity, case/work item, legal basis, and
|
||||
declared use may access this object now.
|
||||
|
||||
- A client-supplied purpose is an assertion, never authority by itself.
|
||||
- The owner or Policy capability validates the purpose and returns explainable
|
||||
provenance.
|
||||
- Sensitive access can require case assignment, mandate, reason capture,
|
||||
approval, or break-glass evidence.
|
||||
- Search, selectors, reporting, exports, background jobs, and connectors apply
|
||||
the same decision.
|
||||
- Audit records the validated purpose identifier and decision reference, not
|
||||
unnecessary content.
|
||||
|
||||
## Retention Contract
|
||||
|
||||
Every persistent object declares an owner, retention class or policy reference,
|
||||
trigger, start instant, hold behavior, review/disposition action, and evidence.
|
||||
Retention is not a generic timestamp deletion job.
|
||||
|
||||
- Domain owners enumerate and execute their own effects through a typed
|
||||
retention provider.
|
||||
- Policy resolves inherited ceilings and simulation.
|
||||
- Records owns record disposition; Files owns byte/object effects; Audit owns
|
||||
audit-detail behavior; external providers declare their own effect and
|
||||
recovery semantics.
|
||||
- Dry-run, legal hold, exact revision, idempotency, outcome unknown,
|
||||
reconciliation, correction, and destruction evidence are mandatory for
|
||||
consequential removal.
|
||||
|
||||
## Institutional Context Contract
|
||||
|
||||
Consequential objects and effects carry the relevant tenant, institution,
|
||||
organization unit, function, mandate/jurisdiction, service/case/work item,
|
||||
party/representation, decision, and record references. Context is minimized to
|
||||
what the operation needs. Organizational membership is not itself permission
|
||||
or mandate.
|
||||
|
||||
Events, automation intents, audit evidence, records, and external effects retain
|
||||
the same governed context envelope or an exact reference to it. Consumers must
|
||||
not reconstruct authority later from mutable current structures.
|
||||
|
||||
## Adoption Order
|
||||
|
||||
1. Inventory every domain list/detail/search/export/effect and classify all
|
||||
four dimensions.
|
||||
2. Migrate institutional owners first: Access, IDM, Organizations, Mandates,
|
||||
Services, Parties, Cases, Approvals, Committee, Decisions, Voting, and
|
||||
Records.
|
||||
3. Migrate communication and content: Addresses, Distribution Lists, Campaign,
|
||||
Postbox, Mail, Calendar, Files, Templates, and Forms Runtime.
|
||||
4. Migrate data projections: Connectors, Datasources, Dataflow, Reporting,
|
||||
Search, Risk Compliance, and Dashboard.
|
||||
5. Migrate workflow/task/background/provider operations and prove that no
|
||||
asynchronous path drops context.
|
||||
6. Advance manifest claims only after owner tests and browser/reference-journey
|
||||
evidence pass.
|
||||
|
||||
The generated platform inventory reports adoption counts and module details.
|
||||
Gitea tracks individual migrations; the declaration is evidence and a maturity
|
||||
gate, not a substitute for implementation.
|
||||
|
||||
## Definition Of Enforced
|
||||
|
||||
A dimension is `enforced` only when:
|
||||
|
||||
- all declared object types and public reads/effects use it;
|
||||
- list/detail/count/search/export/worker behavior is consistent;
|
||||
- cache and pagination semantics cannot cross contexts;
|
||||
- absence, invalid values, and inaccessible referenced context fail safely;
|
||||
- tests cover current, historical, unauthorized, replay, and module-absence
|
||||
combinations appropriate to the dimension;
|
||||
- user/admin documentation explains behavior and limitations;
|
||||
- the manifest cites those tests and docs.
|
||||
@@ -0,0 +1,60 @@
|
||||
# Localization And Contextual Help Quality
|
||||
|
||||
## Reference Language
|
||||
|
||||
German (`de`) is GovOPlaN's first-class reference target. Every translation key
|
||||
used by a shipped WebUI must exist in German and English. German completeness is
|
||||
a release gate; English remains the source-code fallback language so existing
|
||||
literal labels and external developer APIs do not change semantics.
|
||||
|
||||
New installations and tenants default to German. Existing system, tenant, and
|
||||
user preferences are preserved. The available-language and policy model can
|
||||
still select another default or disable a package at the relevant scope.
|
||||
|
||||
Explicit high-risk help content and browser acceptance are tracked in
|
||||
[Core #284](https://git.add-ideas.de/GovOPlaN/govoplan-core/issues/284).
|
||||
|
||||
The platform inventory recognizes both inline locale objects and generated
|
||||
catalogs declared as `const de` / `const en`. Its strict mode requires both
|
||||
locales and reports `de` explicitly as the reference locale.
|
||||
|
||||
## Help Resolution
|
||||
|
||||
Every focusable field and action receives a stable derived F1 identity from the
|
||||
shared shell, even when the component has no dedicated help text. Resolution
|
||||
falls back from field/action to dialog or page and then to the module's visible
|
||||
documentation baseline.
|
||||
|
||||
Backend manifests publish explicit topic associations first. Core additionally
|
||||
associates declared route, navigation, settings, and View surface IDs with the
|
||||
module's static user or administrator documentation baseline. Feature modules
|
||||
should still add exact `metadata.help_contexts` entries for consequential,
|
||||
unfamiliar, policy-controlled, destructive, security-sensitive, or legally
|
||||
meaningful fields and actions.
|
||||
|
||||
The generated `help_review_candidates` list is therefore a content-depth queue,
|
||||
not a list of controls on which F1 cannot work. It should prioritize:
|
||||
|
||||
1. effect, deletion, delivery, retention, disclosure, encryption, and recovery;
|
||||
2. identity, representation, mandate, institutional context, and purpose;
|
||||
3. valid-time versus recorded-time selection;
|
||||
4. provider authority, synchronization, conflict, and outcome unknown;
|
||||
5. fields whose consequences are not evident from their label.
|
||||
|
||||
## Verification
|
||||
|
||||
```bash
|
||||
cd /mnt/DATA/git/govoplan
|
||||
/mnt/DATA/git/govoplan/.venv/bin/python \
|
||||
tools/inventory/platform-interface-inventory.py \
|
||||
--strict --strict-declarations --strict-endpoints
|
||||
```
|
||||
|
||||
The check must report:
|
||||
|
||||
- reference locale `de` present and complete;
|
||||
- no used key missing from `de` or `en`;
|
||||
- every field has a resolvable F1 context;
|
||||
- no duplicate stable IDs;
|
||||
- no undeclared public WebUI surface;
|
||||
- no stale runtime route or endpoint declaration.
|
||||
@@ -93,10 +93,17 @@ Alembic, and post-migration tasks. The lock is session-scoped and therefore
|
||||
released if the migration process dies.
|
||||
|
||||
Runtime roles run `govoplan_core.commands.wait_for_database`. It waits until
|
||||
the database has exactly the configured Core/module Alembic heads and never
|
||||
upgrades schema. This permits a migration Job and runtime Deployments to be
|
||||
the database has exactly the configured, dependency-resolved Core/module
|
||||
Alembic heads and never upgrades schema. Cross-module `depends_on` revisions
|
||||
therefore do not leave runtime roles waiting for a branch marker Alembic has
|
||||
correctly consumed. This permits a migration Job and runtime Deployments to be
|
||||
submitted together while keeping startup fail-closed.
|
||||
|
||||
Runtime coordination records the installed `govoplan-core` distribution
|
||||
version for API, worker and scheduler roles. FastAPI/OpenAPI metadata versions
|
||||
are presentation metadata and must not be used as deployable software identity;
|
||||
mixing the two would create a false version-skew readiness failure.
|
||||
|
||||
## Recovery Ledger
|
||||
|
||||
`govoplan_core.core.recovery` provides a durable operation and evidence
|
||||
|
||||
@@ -1065,6 +1065,408 @@
|
||||
"release": "0.1.15",
|
||||
"squash_policy": "reviewed-manual",
|
||||
"track": "release"
|
||||
},
|
||||
{
|
||||
"heads": [
|
||||
{
|
||||
"owner": "govoplan-notifications",
|
||||
"revision": "6e2f91ab4c70"
|
||||
},
|
||||
{
|
||||
"owner": "govoplan-poll",
|
||||
"revision": "6e7f8a9b0c1d"
|
||||
},
|
||||
{
|
||||
"owner": "govoplan-dashboard",
|
||||
"revision": "7b9d2f4a6c8e"
|
||||
},
|
||||
{
|
||||
"owner": "govoplan-voting",
|
||||
"revision": "8b9c0d1e2f3a"
|
||||
},
|
||||
{
|
||||
"owner": "govoplan-mail",
|
||||
"revision": "93b4c5d6e7f8"
|
||||
},
|
||||
{
|
||||
"owner": "govoplan-core",
|
||||
"revision": "a36d8e4f9b12"
|
||||
},
|
||||
{
|
||||
"owner": "govoplan-forms-runtime",
|
||||
"revision": "a3d5f7b9c1e2"
|
||||
},
|
||||
{
|
||||
"owner": "govoplan-templates",
|
||||
"revision": "a3f7c9d2e1b4"
|
||||
},
|
||||
{
|
||||
"owner": "govoplan-organizations",
|
||||
"revision": "a61e4d9c72b8"
|
||||
},
|
||||
{
|
||||
"owner": "govoplan-mandates",
|
||||
"revision": "a8b1c2d3e4f5"
|
||||
},
|
||||
{
|
||||
"owner": "govoplan-audit",
|
||||
"revision": "a8d1e4f7b2c5"
|
||||
},
|
||||
{
|
||||
"owner": "govoplan-approvals",
|
||||
"revision": "a91c4e72b5d8"
|
||||
},
|
||||
{
|
||||
"owner": "govoplan-policy",
|
||||
"revision": "a9c4e7b2d5f8"
|
||||
},
|
||||
{
|
||||
"owner": "govoplan-idm",
|
||||
"revision": "b1c2d3e4f5a6"
|
||||
},
|
||||
{
|
||||
"owner": "govoplan-search",
|
||||
"revision": "b2c3d4e5f607"
|
||||
},
|
||||
{
|
||||
"owner": "govoplan-datasources",
|
||||
"revision": "b8d2f5a0c3e7"
|
||||
},
|
||||
{
|
||||
"owner": "govoplan-views",
|
||||
"revision": "b8e4c1f7a2d9"
|
||||
},
|
||||
{
|
||||
"owner": "govoplan-risk-compliance",
|
||||
"revision": "b9c0d1e2f3a4"
|
||||
},
|
||||
{
|
||||
"owner": "govoplan-services",
|
||||
"revision": "b9c2d3e4f5a6"
|
||||
},
|
||||
{
|
||||
"owner": "govoplan-parties",
|
||||
"revision": "c0d3e4f5a6b7"
|
||||
},
|
||||
{
|
||||
"owner": "govoplan-identity-trust",
|
||||
"revision": "c3f5a7b9d1e2"
|
||||
},
|
||||
{
|
||||
"owner": "govoplan-projects",
|
||||
"revision": "c4a1e8f2d6b9"
|
||||
},
|
||||
{
|
||||
"owner": "govoplan-addresses",
|
||||
"revision": "c5d7e8f9a0b1"
|
||||
},
|
||||
{
|
||||
"owner": "govoplan-access",
|
||||
"revision": "c7e0a3d6f9b2"
|
||||
},
|
||||
{
|
||||
"owner": "govoplan-reporting",
|
||||
"revision": "c8d5e2f6a9b3"
|
||||
},
|
||||
{
|
||||
"owner": "govoplan-scheduling",
|
||||
"revision": "c9d4e7f1a2b3"
|
||||
},
|
||||
{
|
||||
"owner": "govoplan-decisions",
|
||||
"revision": "d1e4f5a6b7c8"
|
||||
},
|
||||
{
|
||||
"owner": "govoplan-calendar",
|
||||
"revision": "d24e5f607182"
|
||||
},
|
||||
{
|
||||
"owner": "govoplan-committee",
|
||||
"revision": "d8b9f0a1c2e3"
|
||||
},
|
||||
{
|
||||
"owner": "govoplan-postbox",
|
||||
"revision": "d8e3f6a9b2c5"
|
||||
},
|
||||
{
|
||||
"owner": "govoplan-campaign",
|
||||
"revision": "e3c8f4a5b6d7"
|
||||
},
|
||||
{
|
||||
"owner": "govoplan-workflow-engine",
|
||||
"revision": "e4a1f8c2d7b6"
|
||||
},
|
||||
{
|
||||
"owner": "govoplan-encryption",
|
||||
"revision": "e5b7c9d1f3a4"
|
||||
},
|
||||
{
|
||||
"owner": "govoplan-dist-lists",
|
||||
"revision": "e7c3a9d1b5f2"
|
||||
},
|
||||
{
|
||||
"owner": "govoplan-files",
|
||||
"revision": "f1a2b3c4d5e7"
|
||||
},
|
||||
{
|
||||
"owner": "govoplan-dataflow",
|
||||
"revision": "f6c2a9d4e7b1"
|
||||
},
|
||||
{
|
||||
"owner": "govoplan-cases",
|
||||
"revision": "f6d3a8b1c4e7"
|
||||
},
|
||||
{
|
||||
"owner": "govoplan-connectors",
|
||||
"revision": "f7c8d9e0a1b2"
|
||||
}
|
||||
],
|
||||
"owner_heads": [
|
||||
{
|
||||
"owner": "govoplan-access",
|
||||
"revisions": [
|
||||
"c7e0a3d6f9b2"
|
||||
]
|
||||
},
|
||||
{
|
||||
"owner": "govoplan-addresses",
|
||||
"revisions": [
|
||||
"c5d7e8f9a0b1"
|
||||
]
|
||||
},
|
||||
{
|
||||
"owner": "govoplan-approvals",
|
||||
"revisions": [
|
||||
"a91c4e72b5d8"
|
||||
]
|
||||
},
|
||||
{
|
||||
"owner": "govoplan-audit",
|
||||
"revisions": [
|
||||
"a8d1e4f7b2c5"
|
||||
]
|
||||
},
|
||||
{
|
||||
"owner": "govoplan-calendar",
|
||||
"revisions": [
|
||||
"d24e5f607182"
|
||||
]
|
||||
},
|
||||
{
|
||||
"owner": "govoplan-campaign",
|
||||
"revisions": [
|
||||
"e3c8f4a5b6d7"
|
||||
]
|
||||
},
|
||||
{
|
||||
"owner": "govoplan-cases",
|
||||
"revisions": [
|
||||
"f6d3a8b1c4e7"
|
||||
]
|
||||
},
|
||||
{
|
||||
"owner": "govoplan-committee",
|
||||
"revisions": [
|
||||
"d8b9f0a1c2e3"
|
||||
]
|
||||
},
|
||||
{
|
||||
"owner": "govoplan-connectors",
|
||||
"revisions": [
|
||||
"f7c8d9e0a1b2"
|
||||
]
|
||||
},
|
||||
{
|
||||
"owner": "govoplan-core",
|
||||
"revisions": [
|
||||
"a36d8e4f9b12"
|
||||
]
|
||||
},
|
||||
{
|
||||
"owner": "govoplan-dashboard",
|
||||
"revisions": [
|
||||
"7b9d2f4a6c8e"
|
||||
]
|
||||
},
|
||||
{
|
||||
"owner": "govoplan-dataflow",
|
||||
"revisions": [
|
||||
"f6c2a9d4e7b1"
|
||||
]
|
||||
},
|
||||
{
|
||||
"owner": "govoplan-datasources",
|
||||
"revisions": [
|
||||
"b8d2f5a0c3e7"
|
||||
]
|
||||
},
|
||||
{
|
||||
"owner": "govoplan-decisions",
|
||||
"revisions": [
|
||||
"d1e4f5a6b7c8"
|
||||
]
|
||||
},
|
||||
{
|
||||
"owner": "govoplan-dist-lists",
|
||||
"revisions": [
|
||||
"e7c3a9d1b5f2"
|
||||
]
|
||||
},
|
||||
{
|
||||
"owner": "govoplan-encryption",
|
||||
"revisions": [
|
||||
"e5b7c9d1f3a4"
|
||||
]
|
||||
},
|
||||
{
|
||||
"owner": "govoplan-files",
|
||||
"revisions": [
|
||||
"f1a2b3c4d5e7"
|
||||
]
|
||||
},
|
||||
{
|
||||
"owner": "govoplan-forms",
|
||||
"revisions": [
|
||||
"e1f2a3b4c5d6"
|
||||
]
|
||||
},
|
||||
{
|
||||
"owner": "govoplan-forms-runtime",
|
||||
"revisions": [
|
||||
"a3d5f7b9c1e2"
|
||||
]
|
||||
},
|
||||
{
|
||||
"owner": "govoplan-identity",
|
||||
"revisions": [
|
||||
"5c6d7e8f9a10"
|
||||
]
|
||||
},
|
||||
{
|
||||
"owner": "govoplan-identity-trust",
|
||||
"revisions": [
|
||||
"c3f5a7b9d1e2"
|
||||
]
|
||||
},
|
||||
{
|
||||
"owner": "govoplan-idm",
|
||||
"revisions": [
|
||||
"b1c2d3e4f5a6"
|
||||
]
|
||||
},
|
||||
{
|
||||
"owner": "govoplan-mail",
|
||||
"revisions": [
|
||||
"93b4c5d6e7f8"
|
||||
]
|
||||
},
|
||||
{
|
||||
"owner": "govoplan-mandates",
|
||||
"revisions": [
|
||||
"a8b1c2d3e4f5"
|
||||
]
|
||||
},
|
||||
{
|
||||
"owner": "govoplan-notifications",
|
||||
"revisions": [
|
||||
"6e2f91ab4c70"
|
||||
]
|
||||
},
|
||||
{
|
||||
"owner": "govoplan-organizations",
|
||||
"revisions": [
|
||||
"a61e4d9c72b8"
|
||||
]
|
||||
},
|
||||
{
|
||||
"owner": "govoplan-parties",
|
||||
"revisions": [
|
||||
"c0d3e4f5a6b7"
|
||||
]
|
||||
},
|
||||
{
|
||||
"owner": "govoplan-policy",
|
||||
"revisions": [
|
||||
"a9c4e7b2d5f8"
|
||||
]
|
||||
},
|
||||
{
|
||||
"owner": "govoplan-poll",
|
||||
"revisions": [
|
||||
"6e7f8a9b0c1d"
|
||||
]
|
||||
},
|
||||
{
|
||||
"owner": "govoplan-postbox",
|
||||
"revisions": [
|
||||
"d8e3f6a9b2c5"
|
||||
]
|
||||
},
|
||||
{
|
||||
"owner": "govoplan-projects",
|
||||
"revisions": [
|
||||
"c4a1e8f2d6b9"
|
||||
]
|
||||
},
|
||||
{
|
||||
"owner": "govoplan-reporting",
|
||||
"revisions": [
|
||||
"c8d5e2f6a9b3"
|
||||
]
|
||||
},
|
||||
{
|
||||
"owner": "govoplan-risk-compliance",
|
||||
"revisions": [
|
||||
"b9c0d1e2f3a4"
|
||||
]
|
||||
},
|
||||
{
|
||||
"owner": "govoplan-scheduling",
|
||||
"revisions": [
|
||||
"c9d4e7f1a2b3"
|
||||
]
|
||||
},
|
||||
{
|
||||
"owner": "govoplan-search",
|
||||
"revisions": [
|
||||
"b2c3d4e5f607"
|
||||
]
|
||||
},
|
||||
{
|
||||
"owner": "govoplan-services",
|
||||
"revisions": [
|
||||
"b9c2d3e4f5a6"
|
||||
]
|
||||
},
|
||||
{
|
||||
"owner": "govoplan-templates",
|
||||
"revisions": [
|
||||
"a3f7c9d2e1b4"
|
||||
]
|
||||
},
|
||||
{
|
||||
"owner": "govoplan-views",
|
||||
"revisions": [
|
||||
"b8e4c1f7a2d9"
|
||||
]
|
||||
},
|
||||
{
|
||||
"owner": "govoplan-voting",
|
||||
"revisions": [
|
||||
"8b9c0d1e2f3a"
|
||||
]
|
||||
},
|
||||
{
|
||||
"owner": "govoplan-workflow-engine",
|
||||
"revisions": [
|
||||
"e4a1f8c2d7b6"
|
||||
]
|
||||
}
|
||||
],
|
||||
"recorded_at": "2026-08-05T17:51:25Z",
|
||||
"release": "0.1.16",
|
||||
"squash_policy": "reviewed-manual",
|
||||
"track": "release"
|
||||
}
|
||||
],
|
||||
"version": 1
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "govoplan-core"
|
||||
version = "0.1.15"
|
||||
version = "0.1.16"
|
||||
description = "Reusable GovOPlaN platform core, access, tenancy, and RBAC components."
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.12"
|
||||
|
||||
@@ -12,7 +12,7 @@ class SystemSettings(Base, TimestampMixin):
|
||||
__tablename__ = "core_system_settings"
|
||||
|
||||
id: Mapped[str] = mapped_column(String(36), primary_key=True, default="global")
|
||||
default_locale: Mapped[str] = mapped_column(String(20), default="en", nullable=False)
|
||||
default_locale: Mapped[str] = mapped_column(String(20), default="de", nullable=False)
|
||||
allow_tenant_custom_groups: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)
|
||||
allow_tenant_custom_roles: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)
|
||||
allow_tenant_api_keys: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)
|
||||
@@ -20,4 +20,3 @@ class SystemSettings(Base, TimestampMixin):
|
||||
|
||||
|
||||
__all__ = ["SystemSettings"]
|
||||
|
||||
|
||||
@@ -84,7 +84,7 @@ class TenantInfo(BaseModel):
|
||||
slug: str
|
||||
name: str
|
||||
is_active: bool = True
|
||||
default_locale: str = "en"
|
||||
default_locale: str = "de"
|
||||
enabled_language_codes: list[str] = Field(default_factory=list)
|
||||
|
||||
|
||||
@@ -210,7 +210,7 @@ class AuthProfileResponse(BaseModel):
|
||||
active_tenant: TenantInfo
|
||||
available_languages: list[LanguageInfo] = Field(default_factory=list)
|
||||
enabled_language_codes: list[str] = Field(default_factory=list)
|
||||
default_language: str = "en"
|
||||
default_language: str = "de"
|
||||
profile_loaded: bool = True
|
||||
|
||||
|
||||
@@ -239,7 +239,7 @@ class LoginResponse(BaseModel):
|
||||
principal: PrincipalContextInfo | None = None
|
||||
available_languages: list[LanguageInfo] = Field(default_factory=list)
|
||||
enabled_language_codes: list[str] = Field(default_factory=list)
|
||||
default_language: str = "en"
|
||||
default_language: str = "de"
|
||||
profile_loaded: bool = True
|
||||
roles_loaded: bool = True
|
||||
groups_loaded: bool = True
|
||||
@@ -257,7 +257,7 @@ class MeResponse(BaseModel):
|
||||
principal: PrincipalContextInfo | None = None
|
||||
available_languages: list[LanguageInfo] = Field(default_factory=list)
|
||||
enabled_language_codes: list[str] = Field(default_factory=list)
|
||||
default_language: str = "en"
|
||||
default_language: str = "de"
|
||||
profile_loaded: bool = True
|
||||
roles_loaded: bool = True
|
||||
groups_loaded: bool = True
|
||||
|
||||
@@ -356,7 +356,7 @@ def consume_first_admin_credential(
|
||||
tenant = Tenant(
|
||||
slug=clean_tenant_slug,
|
||||
name=clean_tenant_name,
|
||||
default_locale="en",
|
||||
default_locale="de",
|
||||
settings={},
|
||||
is_active=True,
|
||||
)
|
||||
|
||||
@@ -0,0 +1,236 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass, field
|
||||
from typing import Literal, Mapping, cast
|
||||
|
||||
|
||||
InformationGovernanceAdoption = Literal[
|
||||
"not_applicable",
|
||||
"contract_only",
|
||||
"partial",
|
||||
"enforced",
|
||||
]
|
||||
|
||||
INFORMATION_GOVERNANCE_ADOPTION_ORDER: tuple[InformationGovernanceAdoption, ...] = (
|
||||
"not_applicable",
|
||||
"contract_only",
|
||||
"partial",
|
||||
"enforced",
|
||||
)
|
||||
|
||||
|
||||
class InformationGovernanceDeclarationError(ValueError):
|
||||
pass
|
||||
|
||||
|
||||
@dataclass(frozen=True, slots=True)
|
||||
class InformationGovernanceDimension:
|
||||
"""Truthful module-level adoption claim for one cross-cutting dimension."""
|
||||
|
||||
adoption: InformationGovernanceAdoption = "contract_only"
|
||||
object_types: tuple[str, ...] = ()
|
||||
evidence: tuple[str, ...] = ()
|
||||
limitation: str | None = None
|
||||
|
||||
def __post_init__(self) -> None:
|
||||
if self.adoption not in INFORMATION_GOVERNANCE_ADOPTION_ORDER:
|
||||
raise InformationGovernanceDeclarationError(
|
||||
f"Unsupported information-governance adoption: {self.adoption!r}."
|
||||
)
|
||||
for field_name in ("object_types", "evidence"):
|
||||
values = getattr(self, field_name)
|
||||
if len(values) != len(set(values)) or any(not item.strip() for item in values):
|
||||
raise InformationGovernanceDeclarationError(
|
||||
f"Information-governance {field_name.replace('_', ' ')} must "
|
||||
"contain unique non-empty values."
|
||||
)
|
||||
if self.adoption == "enforced" and not self.evidence:
|
||||
raise InformationGovernanceDeclarationError(
|
||||
"An enforced information-governance dimension requires evidence."
|
||||
)
|
||||
if self.adoption in {"partial", "enforced"} and not self.object_types:
|
||||
raise InformationGovernanceDeclarationError(
|
||||
"Partial and enforced information-governance dimensions must "
|
||||
"name their covered object types."
|
||||
)
|
||||
if self.adoption == "not_applicable" and self.object_types:
|
||||
raise InformationGovernanceDeclarationError(
|
||||
"A non-applicable information-governance dimension cannot declare object types."
|
||||
)
|
||||
if self.adoption in {"contract_only", "partial"} and not str(
|
||||
self.limitation or ""
|
||||
).strip():
|
||||
raise InformationGovernanceDeclarationError(
|
||||
"Contract-only and partial adoption must state the current limitation."
|
||||
)
|
||||
|
||||
def to_dict(self) -> dict[str, object]:
|
||||
return {
|
||||
"adoption": self.adoption,
|
||||
"object_types": list(self.object_types),
|
||||
"evidence": list(self.evidence),
|
||||
"limitation": self.limitation,
|
||||
}
|
||||
|
||||
|
||||
def _contract_only_dimension() -> InformationGovernanceDimension:
|
||||
return InformationGovernanceDimension(
|
||||
adoption="contract_only",
|
||||
limitation=(
|
||||
"The platform contract applies, but module-specific adoption evidence "
|
||||
"has not been declared."
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
@dataclass(frozen=True, slots=True)
|
||||
class ModuleInformationGovernance:
|
||||
"""Cross-cutting data-use requirements and honest adoption evidence."""
|
||||
|
||||
temporal_browsing: InformationGovernanceDimension = field(
|
||||
default_factory=_contract_only_dimension
|
||||
)
|
||||
purpose_aware_access: InformationGovernanceDimension = field(
|
||||
default_factory=_contract_only_dimension
|
||||
)
|
||||
retention: InformationGovernanceDimension = field(
|
||||
default_factory=_contract_only_dimension
|
||||
)
|
||||
institutional_context: InformationGovernanceDimension = field(
|
||||
default_factory=_contract_only_dimension
|
||||
)
|
||||
current_authorization_for_historical_reads: bool = True
|
||||
contract_version: str = "1"
|
||||
|
||||
def __post_init__(self) -> None:
|
||||
if self.contract_version != "1":
|
||||
raise InformationGovernanceDeclarationError(
|
||||
"Unsupported module information-governance contract version."
|
||||
)
|
||||
if not self.current_authorization_for_historical_reads:
|
||||
raise InformationGovernanceDeclarationError(
|
||||
"Historical reads must always use current authorization."
|
||||
)
|
||||
for name, dimension in self.dimensions.items():
|
||||
if not isinstance(dimension, InformationGovernanceDimension):
|
||||
raise InformationGovernanceDeclarationError(
|
||||
f"Information-governance dimension {name!r} has an invalid value."
|
||||
)
|
||||
|
||||
@property
|
||||
def dimensions(self) -> Mapping[str, InformationGovernanceDimension]:
|
||||
return {
|
||||
"temporal_browsing": self.temporal_browsing,
|
||||
"purpose_aware_access": self.purpose_aware_access,
|
||||
"retention": self.retention,
|
||||
"institutional_context": self.institutional_context,
|
||||
}
|
||||
|
||||
def to_dict(self) -> dict[str, object]:
|
||||
return {
|
||||
"contract_version": self.contract_version,
|
||||
"current_authorization_for_historical_reads": (
|
||||
self.current_authorization_for_historical_reads
|
||||
),
|
||||
"dimensions": {
|
||||
name: dimension.to_dict()
|
||||
for name, dimension in self.dimensions.items()
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def information_governance_from_mapping(
|
||||
value: Mapping[str, object],
|
||||
) -> ModuleInformationGovernance:
|
||||
raw_dimensions = value.get("dimensions")
|
||||
if not isinstance(raw_dimensions, Mapping):
|
||||
raise InformationGovernanceDeclarationError(
|
||||
"Information-governance dimensions must be an object."
|
||||
)
|
||||
|
||||
def dimension(name: str) -> InformationGovernanceDimension:
|
||||
raw_dimension = raw_dimensions.get(name)
|
||||
if not isinstance(raw_dimension, Mapping):
|
||||
raise InformationGovernanceDeclarationError(
|
||||
f"Information-governance dimension {name!r} must be an object."
|
||||
)
|
||||
|
||||
def text_tuple(field_name: str) -> tuple[str, ...]:
|
||||
raw_values = raw_dimension.get(field_name, ())
|
||||
if not isinstance(raw_values, (list, tuple)):
|
||||
raise InformationGovernanceDeclarationError(
|
||||
f"Information-governance {name}.{field_name} must be a list."
|
||||
)
|
||||
if any(not isinstance(item, str) for item in raw_values):
|
||||
raise InformationGovernanceDeclarationError(
|
||||
f"Information-governance {name}.{field_name} must contain strings."
|
||||
)
|
||||
return tuple(raw_values)
|
||||
|
||||
raw_limitation = raw_dimension.get("limitation")
|
||||
raw_adoption = raw_dimension.get("adoption") or "contract_only"
|
||||
if not isinstance(raw_adoption, str):
|
||||
raise InformationGovernanceDeclarationError(
|
||||
f"Information-governance {name}.adoption must be a string."
|
||||
)
|
||||
if raw_limitation is not None and not isinstance(raw_limitation, str):
|
||||
raise InformationGovernanceDeclarationError(
|
||||
f"Information-governance {name}.limitation must be a string."
|
||||
)
|
||||
return InformationGovernanceDimension(
|
||||
adoption=cast(
|
||||
InformationGovernanceAdoption,
|
||||
raw_adoption,
|
||||
),
|
||||
object_types=text_tuple("object_types"),
|
||||
evidence=text_tuple("evidence"),
|
||||
limitation=raw_limitation,
|
||||
)
|
||||
|
||||
current_authorization = value.get(
|
||||
"current_authorization_for_historical_reads",
|
||||
True,
|
||||
)
|
||||
if not isinstance(current_authorization, bool):
|
||||
raise InformationGovernanceDeclarationError(
|
||||
"current_authorization_for_historical_reads must be boolean."
|
||||
)
|
||||
return ModuleInformationGovernance(
|
||||
contract_version=str(value.get("contract_version") or "1"),
|
||||
current_authorization_for_historical_reads=current_authorization,
|
||||
temporal_browsing=dimension("temporal_browsing"),
|
||||
purpose_aware_access=dimension("purpose_aware_access"),
|
||||
retention=dimension("retention"),
|
||||
institutional_context=dimension("institutional_context"),
|
||||
)
|
||||
|
||||
|
||||
def information_governance_maturity_issues(
|
||||
declaration: ModuleInformationGovernance,
|
||||
*,
|
||||
maturity: str | None,
|
||||
) -> tuple[str, ...]:
|
||||
if maturity not in {"reference_ready", "supported", "lts"}:
|
||||
return ()
|
||||
incomplete = [
|
||||
name
|
||||
for name, dimension in declaration.dimensions.items()
|
||||
if dimension.adoption not in {"not_applicable", "enforced"}
|
||||
]
|
||||
if not incomplete:
|
||||
return ()
|
||||
return (
|
||||
f"Maturity {maturity!r} requires enforced or explicitly non-applicable "
|
||||
"information governance for: " + ", ".join(incomplete),
|
||||
)
|
||||
|
||||
|
||||
__all__ = [
|
||||
"INFORMATION_GOVERNANCE_ADOPTION_ORDER",
|
||||
"InformationGovernanceAdoption",
|
||||
"InformationGovernanceDeclarationError",
|
||||
"InformationGovernanceDimension",
|
||||
"ModuleInformationGovernance",
|
||||
"information_governance_from_mapping",
|
||||
"information_governance_maturity_issues",
|
||||
]
|
||||
@@ -17,6 +17,10 @@ from cryptography.hazmat.primitives import serialization
|
||||
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey, Ed25519PublicKey
|
||||
|
||||
from govoplan_core.core.versioning import format_version_range, version_range_is_valid, version_satisfies_range
|
||||
from govoplan_core.core.information_governance import (
|
||||
information_governance_from_mapping,
|
||||
information_governance_maturity_issues,
|
||||
)
|
||||
from govoplan_core.core.provider_governance import (
|
||||
external_provider_from_mapping,
|
||||
module_architecture_from_mapping,
|
||||
@@ -630,6 +634,7 @@ def _normalize_catalog_item(value: Any) -> dict[str, object]:
|
||||
"tags": _string_list(value.get("tags")),
|
||||
}
|
||||
raw_architecture = value.get("architecture")
|
||||
architecture_maturity: str | None = None
|
||||
if raw_architecture is not None:
|
||||
if not isinstance(raw_architecture, Mapping):
|
||||
raise ValueError(
|
||||
@@ -647,6 +652,27 @@ def _normalize_catalog_item(value: Any) -> dict[str, object]:
|
||||
+ "; ".join(issues)
|
||||
)
|
||||
item["architecture"] = architecture.to_dict()
|
||||
architecture_maturity = architecture.maturity
|
||||
raw_information_governance = value.get("information_governance")
|
||||
if raw_information_governance is not None:
|
||||
if not isinstance(raw_information_governance, Mapping):
|
||||
raise ValueError(
|
||||
"Module package catalog information_governance for "
|
||||
f"{module_id!r} must be an object."
|
||||
)
|
||||
information_governance = information_governance_from_mapping(
|
||||
raw_information_governance
|
||||
)
|
||||
governance_issues = information_governance_maturity_issues(
|
||||
information_governance,
|
||||
maturity=architecture_maturity,
|
||||
)
|
||||
if governance_issues:
|
||||
raise ValueError(
|
||||
"Module package catalog information_governance for "
|
||||
f"{module_id!r} is invalid: " + "; ".join(governance_issues)
|
||||
)
|
||||
item["information_governance"] = information_governance.to_dict()
|
||||
raw_providers = value.get("external_providers")
|
||||
if raw_providers is not None:
|
||||
if not isinstance(raw_providers, list):
|
||||
|
||||
@@ -4,6 +4,7 @@ from collections.abc import Callable, Iterable, Mapping, Sequence
|
||||
from dataclasses import dataclass, field
|
||||
from typing import Any, Literal, Protocol, TYPE_CHECKING
|
||||
|
||||
from govoplan_core.core.information_governance import ModuleInformationGovernance
|
||||
from govoplan_core.core.ownership import OwnershipProviderRegistration
|
||||
from govoplan_core.core.provider_governance import (
|
||||
ExternalProviderDeclaration,
|
||||
@@ -446,6 +447,9 @@ class ModuleManifest:
|
||||
...,
|
||||
] = ()
|
||||
architecture: ModuleArchitectureDeclaration | None = None
|
||||
information_governance: ModuleInformationGovernance = field(
|
||||
default_factory=ModuleInformationGovernance
|
||||
)
|
||||
external_providers: tuple[ExternalProviderDeclaration, ...] = ()
|
||||
external_provider_state_providers: tuple[
|
||||
ExternalProviderStateProviderRegistration,
|
||||
|
||||
@@ -32,6 +32,9 @@ from govoplan_core.core.module_entitlements import (
|
||||
current_tenant_execution_context,
|
||||
tenant_execution_scope,
|
||||
)
|
||||
from govoplan_core.core.information_governance import (
|
||||
information_governance_maturity_issues,
|
||||
)
|
||||
from govoplan_core.core.ownership import (
|
||||
OwnershipProviderRegistration,
|
||||
ResourceOwnershipProvider,
|
||||
@@ -788,6 +791,13 @@ def _validate_architecture_declarations(manifest: ModuleManifest) -> None:
|
||||
raise RegistryError(
|
||||
f"Module {manifest.id!r} architecture declaration: {issue}"
|
||||
)
|
||||
for issue in information_governance_maturity_issues(
|
||||
manifest.information_governance,
|
||||
maturity=architecture.maturity if architecture is not None else None,
|
||||
):
|
||||
raise RegistryError(
|
||||
f"Module {manifest.id!r} information-governance declaration: {issue}"
|
||||
)
|
||||
|
||||
provider_ids: set[str] = set()
|
||||
declared_capabilities = {
|
||||
|
||||
@@ -73,7 +73,7 @@ def bootstrap_dev_data(
|
||||
) -> BootstrapResult:
|
||||
tenant = session.query(Tenant).filter(Tenant.slug == tenant_slug).one_or_none()
|
||||
if tenant is None:
|
||||
tenant = Tenant(slug=tenant_slug, name="Default Tenant", default_locale="en", settings={})
|
||||
tenant = Tenant(slug=tenant_slug, name="Default Tenant", default_locale="de", settings={})
|
||||
session.add(tenant)
|
||||
session.flush()
|
||||
|
||||
|
||||
@@ -685,7 +685,10 @@ def configured_migration_heads(
|
||||
manifest_factories=manifest_factories,
|
||||
migration_track=migration_track,
|
||||
)
|
||||
return tuple(sorted(ScriptDirectory.from_config(config).get_heads()))
|
||||
scripts = ScriptDirectory.from_config(config)
|
||||
return tuple(
|
||||
sorted(revision.revision for revision in scripts.get_revisions("heads"))
|
||||
)
|
||||
|
||||
|
||||
def database_is_at_configured_heads(
|
||||
|
||||
@@ -4,10 +4,12 @@ import re
|
||||
from typing import Any, Iterable
|
||||
|
||||
I18N_SETTINGS_KEY = "i18n"
|
||||
REFERENCE_LANGUAGE_CODE = "de"
|
||||
SOURCE_LANGUAGE_CODE = "en"
|
||||
|
||||
DEFAULT_LANGUAGE_PACKAGES: tuple[dict[str, str], ...] = (
|
||||
{"code": "en", "label": "English", "native_label": "English"},
|
||||
{"code": "de", "label": "German", "native_label": "Deutsch"},
|
||||
{"code": "en", "label": "English", "native_label": "English"},
|
||||
)
|
||||
|
||||
|
||||
@@ -66,7 +68,7 @@ def normalize_enabled_language_codes(
|
||||
available_languages: Iterable[dict[str, Any]],
|
||||
*,
|
||||
default_locale: object = None,
|
||||
fallback_codes: Iterable[str] = ("en", "de"),
|
||||
fallback_codes: Iterable[str] = (REFERENCE_LANGUAGE_CODE, SOURCE_LANGUAGE_CODE),
|
||||
) -> list[str]:
|
||||
available_codes = [normalize_language_code(item.get("code")) for item in available_languages if isinstance(item, dict)]
|
||||
available = {code for code in available_codes if code}
|
||||
@@ -88,8 +90,10 @@ def normalize_enabled_language_codes(
|
||||
|
||||
if enabled:
|
||||
return enabled
|
||||
if "en" in available:
|
||||
return ["en"]
|
||||
if REFERENCE_LANGUAGE_CODE in available:
|
||||
return [REFERENCE_LANGUAGE_CODE]
|
||||
if SOURCE_LANGUAGE_CODE in available:
|
||||
return [SOURCE_LANGUAGE_CODE]
|
||||
return available_codes[:1]
|
||||
|
||||
|
||||
@@ -126,7 +130,7 @@ def preferred_language_code(user_settings: dict[str, Any] | None, allowed_codes:
|
||||
default = resolve_language_code(default_locale, enabled)
|
||||
if default:
|
||||
return default
|
||||
return enabled[0] if enabled else "en"
|
||||
return enabled[0] if enabled else REFERENCE_LANGUAGE_CODE
|
||||
|
||||
|
||||
def update_i18n_settings(settings: dict[str, Any] | None, **values: Any) -> dict[str, Any]:
|
||||
@@ -141,10 +145,13 @@ def system_i18n_payload(settings_item: Any | None) -> dict[str, object]:
|
||||
settings = getattr(settings_item, "settings", None)
|
||||
packages = system_language_packages(settings)
|
||||
available_codes = [item["code"] for item in packages]
|
||||
default_language = resolve_language_code(getattr(settings_item, "default_locale", None), available_codes) or "en"
|
||||
default_language = (
|
||||
resolve_language_code(getattr(settings_item, "default_locale", None), available_codes)
|
||||
or REFERENCE_LANGUAGE_CODE
|
||||
)
|
||||
enabled = system_enabled_language_codes(settings, default_locale=default_language)
|
||||
if default_language not in enabled:
|
||||
default_language = enabled[0] if enabled else "en"
|
||||
default_language = enabled[0] if enabled else REFERENCE_LANGUAGE_CODE
|
||||
return {
|
||||
"available_languages": packages,
|
||||
"enabled_languages": enabled,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from contextlib import asynccontextmanager
|
||||
from importlib.metadata import PackageNotFoundError, version
|
||||
|
||||
from fastapi import Depends, FastAPI
|
||||
from fastapi import HTTPException, status
|
||||
@@ -20,6 +21,13 @@ from govoplan_core.core.runtime_coordination import (
|
||||
from govoplan_core.settings import Settings, settings
|
||||
|
||||
|
||||
def _core_distribution_version() -> str:
|
||||
try:
|
||||
return version("govoplan-core")
|
||||
except PackageNotFoundError:
|
||||
return "development"
|
||||
|
||||
|
||||
def _dev_bootstrap_needs_create_all(database_url: str) -> bool:
|
||||
try:
|
||||
return make_url(database_url).get_backend_name() == "sqlite"
|
||||
@@ -61,7 +69,7 @@ async def lifespan(app: FastAPI):
|
||||
)
|
||||
runtime_agent = RuntimeNodeAgent(
|
||||
settings=settings,
|
||||
software_version=app.version,
|
||||
software_version=_core_distribution_version(),
|
||||
module_ids=module_ids,
|
||||
metadata={"process": "api"},
|
||||
identity=configured_identity
|
||||
@@ -93,7 +101,7 @@ def register_health_details(
|
||||
):
|
||||
app.state.govoplan_runtime_identity = runtime_identity(
|
||||
active_settings,
|
||||
software_version=app.version,
|
||||
software_version=_core_distribution_version(),
|
||||
module_ids=module_ids,
|
||||
)
|
||||
bind_process_runtime_identity(app.state.govoplan_runtime_identity)
|
||||
|
||||
@@ -142,23 +142,58 @@ def _frontend_view_surfaces(manifest: ModuleManifest) -> list[dict[str, object]]
|
||||
def _documentation_help_contexts(manifest: ModuleManifest) -> list[dict[str, object]]:
|
||||
contexts: list[dict[str, object]] = []
|
||||
seen: set[str] = set()
|
||||
|
||||
def append_context(context_id: str, topic) -> None:
|
||||
if not context_id or context_id in seen:
|
||||
return
|
||||
seen.add(context_id)
|
||||
contexts.append(
|
||||
{
|
||||
"id": context_id,
|
||||
"topic_id": topic.id,
|
||||
"title": topic.title,
|
||||
"documentation_types": list(topic.documentation_types),
|
||||
}
|
||||
)
|
||||
|
||||
for topic in manifest.documentation:
|
||||
raw_contexts = topic.metadata.get("help_contexts", ())
|
||||
if isinstance(raw_contexts, str) or not isinstance(raw_contexts, (list, tuple, set)):
|
||||
continue
|
||||
for raw_context in raw_contexts:
|
||||
context_id = str(raw_context).strip()
|
||||
if not context_id or context_id in seen:
|
||||
continue
|
||||
seen.add(context_id)
|
||||
contexts.append(
|
||||
{
|
||||
"id": context_id,
|
||||
"topic_id": topic.id,
|
||||
"title": topic.title,
|
||||
"documentation_types": list(topic.documentation_types),
|
||||
}
|
||||
)
|
||||
append_context(context_id, topic)
|
||||
|
||||
frontend = manifest.frontend
|
||||
if frontend is None or not manifest.documentation:
|
||||
return contexts
|
||||
|
||||
ordered_topics = sorted(manifest.documentation, key=lambda item: (item.order, item.id))
|
||||
|
||||
def baseline_topic(documentation_type: str):
|
||||
return next(
|
||||
(
|
||||
topic
|
||||
for topic in ordered_topics
|
||||
if documentation_type in topic.documentation_types
|
||||
),
|
||||
ordered_topics[0],
|
||||
)
|
||||
|
||||
user_topic = baseline_topic("user")
|
||||
admin_topic = baseline_topic("admin")
|
||||
for route in frontend.routes:
|
||||
if route.surface_id:
|
||||
append_context(route.surface_id, user_topic)
|
||||
for route in frontend.settings_routes:
|
||||
if route.surface_id:
|
||||
append_context(route.surface_id, admin_topic)
|
||||
for item in frontend.nav_items:
|
||||
if item.surface_id:
|
||||
append_context(item.surface_id, user_topic)
|
||||
for surface in frontend.view_surfaces:
|
||||
topic = admin_topic if ".admin." in surface.id else user_topic
|
||||
append_context(surface.id, topic)
|
||||
return contexts
|
||||
|
||||
|
||||
@@ -253,6 +288,7 @@ def create_platform_router(settings: object | None = None) -> APIRouter:
|
||||
if manifest.architecture is not None
|
||||
else None
|
||||
),
|
||||
"information_governance": manifest.information_governance.to_dict(),
|
||||
"external_providers": [
|
||||
declaration.to_dict()
|
||||
for declaration in manifest.external_providers
|
||||
|
||||
@@ -33,7 +33,7 @@ class Tenant:
|
||||
slug: Mapped[str] = mapped_column(String(100), unique=True, nullable=False, index=True)
|
||||
name: Mapped[str] = mapped_column(String(255), nullable=False)
|
||||
description: Mapped[str | None] = mapped_column(Text)
|
||||
default_locale: Mapped[str] = mapped_column(String(20), default="en", nullable=False)
|
||||
default_locale: Mapped[str] = mapped_column(String(20), default="de", nullable=False)
|
||||
settings: Mapped[dict[str, Any]] = mapped_column(JSON, default=dict, nullable=False)
|
||||
allow_custom_groups: Mapped[bool | None] = mapped_column(Boolean, nullable=True)
|
||||
allow_custom_roles: Mapped[bool | None] = mapped_column(Boolean, nullable=True)
|
||||
|
||||
@@ -184,6 +184,12 @@ class DatabaseMigrationTests(unittest.TestCase):
|
||||
system_settings_count = connection.execute(
|
||||
text("SELECT COUNT(*) FROM core_system_settings WHERE id = 'global'"),
|
||||
).scalar_one()
|
||||
default_locale = connection.execute(
|
||||
text(
|
||||
"SELECT default_locale FROM core_system_settings "
|
||||
"WHERE id = 'global'"
|
||||
),
|
||||
).scalar_one()
|
||||
current = database_migration_heads(connection)
|
||||
|
||||
self.assertEqual(
|
||||
@@ -204,6 +210,7 @@ class DatabaseMigrationTests(unittest.TestCase):
|
||||
self.assertIn("file_assets", tables)
|
||||
self.assertIn("mail_server_profiles", tables)
|
||||
self.assertEqual(system_settings_count, 1)
|
||||
self.assertEqual(default_locale, "de")
|
||||
self.assertEqual(
|
||||
role_flags,
|
||||
{"system_owner": True, "system_admin": False, "system_auditor": False},
|
||||
@@ -211,6 +218,42 @@ class DatabaseMigrationTests(unittest.TestCase):
|
||||
finally:
|
||||
engine.dispose()
|
||||
|
||||
def test_german_reference_migration_preserves_explicit_english(self) -> None:
|
||||
with tempfile.TemporaryDirectory(prefix="govoplan-locale-migration-test-") as directory:
|
||||
database = Path(directory) / "locale.db"
|
||||
url = f"sqlite:///{database}"
|
||||
config = alembic_config(database_url=url, enabled_modules=())
|
||||
command.upgrade(config, "f25c9d3e7a01")
|
||||
|
||||
engine = create_engine(url)
|
||||
try:
|
||||
with engine.begin() as connection:
|
||||
connection.execute(
|
||||
text(
|
||||
"UPDATE core_system_settings "
|
||||
"SET default_locale = 'en', "
|
||||
"updated_at = '2040-01-01 00:00:00' "
|
||||
"WHERE id = 'global'"
|
||||
)
|
||||
)
|
||||
finally:
|
||||
engine.dispose()
|
||||
|
||||
command.upgrade(config, "heads")
|
||||
|
||||
engine = create_engine(url)
|
||||
try:
|
||||
with engine.connect() as connection:
|
||||
default_locale = connection.execute(
|
||||
text(
|
||||
"SELECT default_locale FROM core_system_settings "
|
||||
"WHERE id = 'global'"
|
||||
)
|
||||
).scalar_one()
|
||||
self.assertEqual(default_locale, "en")
|
||||
finally:
|
||||
engine.dispose()
|
||||
|
||||
def test_default_module_baselines_apply_to_fresh_database(self) -> None:
|
||||
with tempfile.TemporaryDirectory(prefix="govoplan-default-baseline-test-") as directory:
|
||||
database = Path(directory) / "default.db"
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import unittest
|
||||
|
||||
from govoplan_core.i18n import (
|
||||
REFERENCE_LANGUAGE_CODE,
|
||||
normalize_enabled_language_codes,
|
||||
normalize_language_packages,
|
||||
preferred_language_code,
|
||||
system_i18n_payload,
|
||||
)
|
||||
|
||||
|
||||
class I18nReferenceLanguageTests(unittest.TestCase):
|
||||
def test_german_is_the_new_installation_reference_default(self) -> None:
|
||||
packages = normalize_language_packages()
|
||||
|
||||
self.assertEqual("de", REFERENCE_LANGUAGE_CODE)
|
||||
self.assertEqual(["de", "en"], [item["code"] for item in packages])
|
||||
self.assertEqual(
|
||||
["de", "en"],
|
||||
normalize_enabled_language_codes(None, packages),
|
||||
)
|
||||
self.assertEqual("de", system_i18n_payload(None)["default_language"])
|
||||
self.assertEqual("de", preferred_language_code(None, ()))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
@@ -0,0 +1,68 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import unittest
|
||||
|
||||
from govoplan_core.core.information_governance import (
|
||||
InformationGovernanceDeclarationError,
|
||||
InformationGovernanceDimension,
|
||||
ModuleInformationGovernance,
|
||||
information_governance_from_mapping,
|
||||
information_governance_maturity_issues,
|
||||
)
|
||||
|
||||
|
||||
class InformationGovernanceTests(unittest.TestCase):
|
||||
def test_default_applies_contract_without_claiming_enforcement(self) -> None:
|
||||
declaration = ModuleInformationGovernance()
|
||||
|
||||
self.assertTrue(declaration.current_authorization_for_historical_reads)
|
||||
self.assertEqual(
|
||||
{"contract_only"},
|
||||
{item.adoption for item in declaration.dimensions.values()},
|
||||
)
|
||||
self.assertEqual("1", declaration.to_dict()["contract_version"])
|
||||
|
||||
def test_enforced_adoption_requires_evidence(self) -> None:
|
||||
with self.assertRaisesRegex(
|
||||
InformationGovernanceDeclarationError,
|
||||
"requires evidence",
|
||||
):
|
||||
InformationGovernanceDimension(adoption="enforced")
|
||||
|
||||
with self.assertRaisesRegex(
|
||||
InformationGovernanceDeclarationError,
|
||||
"covered object types",
|
||||
):
|
||||
InformationGovernanceDimension(
|
||||
adoption="enforced",
|
||||
evidence=("tests/test_information_governance.py",),
|
||||
)
|
||||
|
||||
def test_catalog_shape_round_trips(self) -> None:
|
||||
declaration = ModuleInformationGovernance()
|
||||
|
||||
self.assertEqual(
|
||||
declaration,
|
||||
information_governance_from_mapping(declaration.to_dict()),
|
||||
)
|
||||
|
||||
def test_reference_ready_rejects_unproved_dimensions(self) -> None:
|
||||
issues = information_governance_maturity_issues(
|
||||
ModuleInformationGovernance(),
|
||||
maturity="reference_ready",
|
||||
)
|
||||
|
||||
self.assertEqual(1, len(issues))
|
||||
self.assertIn("temporal_browsing", issues[0])
|
||||
self.assertIn("purpose_aware_access", issues[0])
|
||||
self.assertEqual(
|
||||
(),
|
||||
information_governance_maturity_issues(
|
||||
ModuleInformationGovernance(),
|
||||
maturity="vertical_slice",
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
@@ -106,6 +106,7 @@ from govoplan_core.core.module_package_catalog import (
|
||||
sign_module_package_catalog,
|
||||
validate_module_package_catalog,
|
||||
)
|
||||
from govoplan_core.core.information_governance import ModuleInformationGovernance
|
||||
from govoplan_core.core.modules import DocumentationTopic, FrontendModule, FrontendRoute, MigrationRetirementPlan, ModuleCompatibility, ModuleMigrationTask, ModuleMigrationTaskContext, ModuleMigrationTaskResult, ModuleUninstallGuardResult, PublicFrontendRoute
|
||||
from govoplan_core.core.module_guards import drop_table_retirement_provider
|
||||
from govoplan_core.core.modules import MigrationSpec, ModuleInterfaceProvider, ModuleInterfaceRequirement, ModuleManifest, PermissionDefinition, RoleTemplate
|
||||
@@ -119,7 +120,8 @@ from govoplan_core.security.module_permissions import scopes_grant_compatible
|
||||
from govoplan_core.security.permissions import scope_grants
|
||||
from govoplan_core.server.app import create_app
|
||||
from govoplan_core.server.config import GovoplanServerConfig
|
||||
from govoplan_core.server.platform import create_platform_router
|
||||
from govoplan_core.server.platform import _documentation_help_contexts, create_platform_router
|
||||
from govoplan_core.core.views import ViewSurface
|
||||
from govoplan_core.server.registry import available_module_manifests, build_platform_registry
|
||||
from govoplan_core.server.route_validation import RouteCollisionError
|
||||
from govoplan_core.tenancy.scope import Tenant, create_scope_tables
|
||||
@@ -597,6 +599,70 @@ class ModuleSystemTests(unittest.TestCase):
|
||||
[item["path"] for item in public_module["frontend"]["public_routes"]],
|
||||
)
|
||||
|
||||
def test_declared_surfaces_receive_static_documentation_fallbacks(self) -> None:
|
||||
manifest = ModuleManifest(
|
||||
id="example",
|
||||
name="Example",
|
||||
version="test",
|
||||
documentation=(
|
||||
DocumentationTopic(
|
||||
id="example.user",
|
||||
title="Example user guide",
|
||||
summary="User guide.",
|
||||
documentation_types=("user",),
|
||||
order=20,
|
||||
),
|
||||
DocumentationTopic(
|
||||
id="example.admin",
|
||||
title="Example administrator guide",
|
||||
summary="Administrator guide.",
|
||||
documentation_types=("admin",),
|
||||
order=10,
|
||||
),
|
||||
),
|
||||
frontend=FrontendModule(
|
||||
module_id="example",
|
||||
routes=(
|
||||
FrontendRoute(
|
||||
path="/example",
|
||||
component="ExamplePage",
|
||||
surface_id="example.workspace",
|
||||
),
|
||||
),
|
||||
settings_routes=(
|
||||
FrontendRoute(
|
||||
path="/admin?section=example",
|
||||
component="ExampleAdmin",
|
||||
surface_id="example.admin.settings",
|
||||
),
|
||||
),
|
||||
view_surfaces=(
|
||||
ViewSurface(
|
||||
id="example.workspace",
|
||||
module_id="example",
|
||||
kind="route",
|
||||
label="Example workspace",
|
||||
),
|
||||
ViewSurface(
|
||||
id="example.admin.settings",
|
||||
module_id="example",
|
||||
kind="section",
|
||||
label="Example settings",
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
contexts = {
|
||||
item["id"]: item for item in _documentation_help_contexts(manifest)
|
||||
}
|
||||
|
||||
self.assertEqual("example.user", contexts["example.workspace"]["topic_id"])
|
||||
self.assertEqual(
|
||||
"example.admin",
|
||||
contexts["example.admin.settings"]["topic_id"],
|
||||
)
|
||||
|
||||
def test_registry_rejects_duplicate_public_frontend_routes(self) -> None:
|
||||
registry = PlatformRegistry()
|
||||
for module_id in ("first", "second"):
|
||||
@@ -3300,6 +3366,7 @@ finally:
|
||||
"recovery_notes": "Restore rehearsal completed for the release candidate.",
|
||||
"provides_interfaces": [{"name": "files.spaces", "version": "1.2.0"}],
|
||||
"requires_interfaces": [{"name": "access.directory", "version_min": "1.0.0", "optional": True}],
|
||||
"information_governance": ModuleInformationGovernance().to_dict(),
|
||||
"artifact_integrity": {
|
||||
"python": {
|
||||
"ref": "govoplan-files==0.1.4",
|
||||
@@ -3345,6 +3412,12 @@ finally:
|
||||
[{"name": "access.directory", "optional": True, "version_min": "1.0.0"}],
|
||||
catalog[0]["requires_interfaces"],
|
||||
)
|
||||
self.assertEqual(
|
||||
"contract_only",
|
||||
catalog[0]["information_governance"]["dimensions"]["retention"][
|
||||
"adoption"
|
||||
],
|
||||
)
|
||||
self.assertEqual("0" * 64, catalog[0]["artifact_integrity"]["python"]["sha256"])
|
||||
|
||||
validation = validate_module_package_catalog(catalog_path)
|
||||
|
||||
@@ -40,6 +40,22 @@ class _Consumer:
|
||||
self.added.append(queue)
|
||||
|
||||
|
||||
def test_api_runtime_identity_uses_the_installed_distribution_version(
|
||||
monkeypatch,
|
||||
) -> None:
|
||||
from govoplan_core.server import default_config
|
||||
|
||||
monkeypatch.setattr(
|
||||
default_config,
|
||||
"version",
|
||||
lambda distribution: "0.1.15"
|
||||
if distribution == "govoplan-core"
|
||||
else "unexpected",
|
||||
)
|
||||
|
||||
assert default_config._core_distribution_version() == "0.1.15"
|
||||
|
||||
|
||||
def test_api_runtime_agent_fails_readiness_on_heartbeat_error() -> None:
|
||||
agent = RuntimeNodeAgent(
|
||||
settings=SimpleNamespace(
|
||||
|
||||
@@ -1,12 +1,34 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import unittest
|
||||
from unittest.mock import patch
|
||||
from types import SimpleNamespace
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
from govoplan_core.commands.wait_for_database import main
|
||||
from govoplan_core.db.migrations import configured_migration_heads
|
||||
|
||||
|
||||
class WaitForDatabaseTests(unittest.TestCase):
|
||||
def test_configured_heads_resolve_cross_branch_dependencies(self) -> None:
|
||||
scripts = MagicMock()
|
||||
scripts.get_revisions.return_value = (
|
||||
SimpleNamespace(revision="head-b"),
|
||||
SimpleNamespace(revision="head-a"),
|
||||
)
|
||||
|
||||
with (
|
||||
patch("govoplan_core.db.migrations.alembic_config"),
|
||||
patch(
|
||||
"govoplan_core.db.migrations.ScriptDirectory.from_config",
|
||||
return_value=scripts,
|
||||
),
|
||||
):
|
||||
heads = configured_migration_heads("sqlite://")
|
||||
|
||||
self.assertEqual(("head-a", "head-b"), heads)
|
||||
scripts.get_revisions.assert_called_once_with("heads")
|
||||
scripts.get_heads.assert_not_called()
|
||||
|
||||
def test_waits_until_exact_configured_heads_are_visible(self) -> None:
|
||||
with (
|
||||
patch(
|
||||
|
||||
Generated
+76
-76
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@govoplan/core-webui",
|
||||
"version": "0.1.15",
|
||||
"version": "0.1.16",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@govoplan/core-webui",
|
||||
"version": "0.1.15",
|
||||
"version": "0.1.16",
|
||||
"dependencies": {
|
||||
"@govoplan/access-webui": "file:../../govoplan-access/webui",
|
||||
"@govoplan/addresses-webui": "file:../../govoplan-addresses/webui",
|
||||
@@ -73,12 +73,12 @@
|
||||
},
|
||||
"../../govoplan-access/webui": {
|
||||
"name": "@govoplan/access-webui",
|
||||
"version": "0.1.15",
|
||||
"version": "0.1.16",
|
||||
"devDependencies": {
|
||||
"typescript": "^5.7.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@govoplan/core-webui": "^0.1.15",
|
||||
"@govoplan/core-webui": "^0.1.16",
|
||||
"lucide-react": "^1.23.0",
|
||||
"react": ">=19.2.7 <20",
|
||||
"react-dom": ">=19.2.7 <20",
|
||||
@@ -92,9 +92,9 @@
|
||||
},
|
||||
"../../govoplan-addresses/webui": {
|
||||
"name": "@govoplan/addresses-webui",
|
||||
"version": "0.1.15",
|
||||
"version": "0.1.16",
|
||||
"peerDependencies": {
|
||||
"@govoplan/core-webui": "^0.1.15",
|
||||
"@govoplan/core-webui": "^0.1.16",
|
||||
"lucide-react": "^1.23.0",
|
||||
"react": ">=19.2.7 <20",
|
||||
"react-dom": ">=19.2.7 <20",
|
||||
@@ -108,12 +108,12 @@
|
||||
},
|
||||
"../../govoplan-admin/webui": {
|
||||
"name": "@govoplan/admin-webui",
|
||||
"version": "0.1.15",
|
||||
"version": "0.1.16",
|
||||
"devDependencies": {
|
||||
"typescript": "^5.7.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@govoplan/core-webui": "^0.1.15",
|
||||
"@govoplan/core-webui": "^0.1.16",
|
||||
"lucide-react": "^1.23.0",
|
||||
"react": ">=19.2.7 <20",
|
||||
"react-dom": ">=19.2.7 <20",
|
||||
@@ -127,9 +127,9 @@
|
||||
},
|
||||
"../../govoplan-approvals/webui": {
|
||||
"name": "@govoplan/approvals-webui",
|
||||
"version": "0.1.15",
|
||||
"version": "0.1.16",
|
||||
"peerDependencies": {
|
||||
"@govoplan/core-webui": "^0.1.15",
|
||||
"@govoplan/core-webui": "^0.1.16",
|
||||
"lucide-react": "^1.23.0",
|
||||
"react": ">=19.2.7 <20",
|
||||
"react-dom": ">=19.2.7 <20"
|
||||
@@ -142,9 +142,9 @@
|
||||
},
|
||||
"../../govoplan-audit/webui": {
|
||||
"name": "@govoplan/audit-webui",
|
||||
"version": "0.1.15",
|
||||
"version": "0.1.16",
|
||||
"peerDependencies": {
|
||||
"@govoplan/core-webui": "^0.1.15",
|
||||
"@govoplan/core-webui": "^0.1.16",
|
||||
"lucide-react": "^1.23.0",
|
||||
"react": ">=19.2.7 <20",
|
||||
"react-dom": ">=19.2.7 <20",
|
||||
@@ -158,9 +158,9 @@
|
||||
},
|
||||
"../../govoplan-calendar/webui": {
|
||||
"name": "@govoplan/calendar-webui",
|
||||
"version": "0.1.15",
|
||||
"version": "0.1.16",
|
||||
"peerDependencies": {
|
||||
"@govoplan/core-webui": "^0.1.15",
|
||||
"@govoplan/core-webui": "^0.1.16",
|
||||
"@vitejs/plugin-react": "^5.2.0",
|
||||
"lucide-react": "^1.23.0",
|
||||
"react": ">=19.2.7 <20",
|
||||
@@ -177,7 +177,7 @@
|
||||
},
|
||||
"../../govoplan-campaign/webui": {
|
||||
"name": "@govoplan/campaign-webui",
|
||||
"version": "0.1.15",
|
||||
"version": "0.1.16",
|
||||
"dependencies": {
|
||||
"read-excel-file": "9.2.0"
|
||||
},
|
||||
@@ -185,7 +185,7 @@
|
||||
"typescript": "^5.7.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@govoplan/core-webui": "^0.1.15",
|
||||
"@govoplan/core-webui": "^0.1.16",
|
||||
"lucide-react": "^1.23.0",
|
||||
"react": ">=19.2.7 <20",
|
||||
"react-dom": ">=19.2.7 <20",
|
||||
@@ -199,9 +199,9 @@
|
||||
},
|
||||
"../../govoplan-cases/webui": {
|
||||
"name": "@govoplan/cases-webui",
|
||||
"version": "0.1.15",
|
||||
"version": "0.1.16",
|
||||
"peerDependencies": {
|
||||
"@govoplan/core-webui": "^0.1.15",
|
||||
"@govoplan/core-webui": "^0.1.16",
|
||||
"lucide-react": "^1.23.0",
|
||||
"react": ">=19.2.7 <20",
|
||||
"react-dom": ">=19.2.7 <20",
|
||||
@@ -215,9 +215,9 @@
|
||||
},
|
||||
"../../govoplan-committee/webui": {
|
||||
"name": "@govoplan/committee-webui",
|
||||
"version": "0.1.15",
|
||||
"version": "0.1.16",
|
||||
"peerDependencies": {
|
||||
"@govoplan/core-webui": "^0.1.15",
|
||||
"@govoplan/core-webui": "^0.1.16",
|
||||
"lucide-react": "^1.23.0",
|
||||
"react": ">=19.2.7 <20",
|
||||
"react-dom": ">=19.2.7 <20",
|
||||
@@ -231,9 +231,9 @@
|
||||
},
|
||||
"../../govoplan-dashboard/webui": {
|
||||
"name": "@govoplan/dashboard-webui",
|
||||
"version": "0.1.15",
|
||||
"version": "0.1.16",
|
||||
"peerDependencies": {
|
||||
"@govoplan/core-webui": "^0.1.15",
|
||||
"@govoplan/core-webui": "^0.1.16",
|
||||
"lucide-react": "^1.23.0",
|
||||
"react": ">=19.2.7 <20",
|
||||
"react-dom": ">=19.2.7 <20",
|
||||
@@ -247,9 +247,9 @@
|
||||
},
|
||||
"../../govoplan-dataflow/webui": {
|
||||
"name": "@govoplan/dataflow-webui",
|
||||
"version": "0.1.15",
|
||||
"version": "0.1.16",
|
||||
"peerDependencies": {
|
||||
"@govoplan/core-webui": "^0.1.15",
|
||||
"@govoplan/core-webui": "^0.1.16",
|
||||
"@xyflow/react": "^12.11.2",
|
||||
"lucide-react": "^1.23.0",
|
||||
"react": ">=19.2.7 <20",
|
||||
@@ -265,9 +265,9 @@
|
||||
},
|
||||
"../../govoplan-datasources/webui": {
|
||||
"name": "@govoplan/datasources-webui",
|
||||
"version": "0.1.15",
|
||||
"version": "0.1.16",
|
||||
"peerDependencies": {
|
||||
"@govoplan/core-webui": "^0.1.15",
|
||||
"@govoplan/core-webui": "^0.1.16",
|
||||
"lucide-react": "^1.23.0",
|
||||
"react": ">=19.2.7 <20",
|
||||
"react-dom": ">=19.2.7 <20",
|
||||
@@ -282,9 +282,9 @@
|
||||
},
|
||||
"../../govoplan-dist-lists/webui": {
|
||||
"name": "@govoplan/dist-lists-webui",
|
||||
"version": "0.1.15",
|
||||
"version": "0.1.16",
|
||||
"peerDependencies": {
|
||||
"@govoplan/core-webui": "^0.1.15",
|
||||
"@govoplan/core-webui": "^0.1.16",
|
||||
"lucide-react": "^1.23.0",
|
||||
"react": ">=19.2.7 <20",
|
||||
"react-dom": ">=19.2.7 <20",
|
||||
@@ -299,9 +299,9 @@
|
||||
},
|
||||
"../../govoplan-docs/webui": {
|
||||
"name": "@govoplan/docs-webui",
|
||||
"version": "0.1.15",
|
||||
"version": "0.1.16",
|
||||
"peerDependencies": {
|
||||
"@govoplan/core-webui": "^0.1.15",
|
||||
"@govoplan/core-webui": "^0.1.16",
|
||||
"@vitejs/plugin-react": "^5.2.0",
|
||||
"lucide-react": "^1.23.0",
|
||||
"react": ">=19.2.7 <20",
|
||||
@@ -318,9 +318,9 @@
|
||||
},
|
||||
"../../govoplan-encryption/webui": {
|
||||
"name": "@govoplan/encryption-webui",
|
||||
"version": "0.1.15",
|
||||
"version": "0.1.16",
|
||||
"peerDependencies": {
|
||||
"@govoplan/core-webui": "^0.1.15",
|
||||
"@govoplan/core-webui": "^0.1.16",
|
||||
"lucide-react": "^1.23.0",
|
||||
"react": ">=19.2.7 <20",
|
||||
"react-dom": ">=19.2.7 <20"
|
||||
@@ -333,9 +333,9 @@
|
||||
},
|
||||
"../../govoplan-files/webui": {
|
||||
"name": "@govoplan/files-webui",
|
||||
"version": "0.1.15",
|
||||
"version": "0.1.16",
|
||||
"peerDependencies": {
|
||||
"@govoplan/core-webui": "^0.1.15",
|
||||
"@govoplan/core-webui": "^0.1.16",
|
||||
"@vitejs/plugin-react": "^5.2.0",
|
||||
"lucide-react": "^1.23.0",
|
||||
"react": ">=19.2.7 <20",
|
||||
@@ -352,9 +352,9 @@
|
||||
},
|
||||
"../../govoplan-forms-runtime/webui": {
|
||||
"name": "@govoplan/forms-runtime-webui",
|
||||
"version": "0.1.15",
|
||||
"version": "0.1.16",
|
||||
"peerDependencies": {
|
||||
"@govoplan/core-webui": "^0.1.15",
|
||||
"@govoplan/core-webui": "^0.1.16",
|
||||
"lucide-react": "^1.23.0",
|
||||
"react": ">=19.2.7 <20",
|
||||
"react-dom": ">=19.2.7 <20",
|
||||
@@ -368,9 +368,9 @@
|
||||
},
|
||||
"../../govoplan-forms/webui": {
|
||||
"name": "@govoplan/forms-webui",
|
||||
"version": "0.1.15",
|
||||
"version": "0.1.16",
|
||||
"peerDependencies": {
|
||||
"@govoplan/core-webui": "^0.1.15",
|
||||
"@govoplan/core-webui": "^0.1.16",
|
||||
"lucide-react": "^1.23.0",
|
||||
"react": ">=19.2.7 <20",
|
||||
"react-dom": ">=19.2.7 <20"
|
||||
@@ -383,9 +383,9 @@
|
||||
},
|
||||
"../../govoplan-identity-trust/webui": {
|
||||
"name": "@govoplan/identity-trust-webui",
|
||||
"version": "0.1.15",
|
||||
"version": "0.1.16",
|
||||
"peerDependencies": {
|
||||
"@govoplan/core-webui": "^0.1.15",
|
||||
"@govoplan/core-webui": "^0.1.16",
|
||||
"lucide-react": "^1.23.0",
|
||||
"react": ">=19.2.7 <20",
|
||||
"react-dom": ">=19.2.7 <20"
|
||||
@@ -398,9 +398,9 @@
|
||||
},
|
||||
"../../govoplan-idm/webui": {
|
||||
"name": "@govoplan/idm-webui",
|
||||
"version": "0.1.15",
|
||||
"version": "0.1.16",
|
||||
"peerDependencies": {
|
||||
"@govoplan/core-webui": "^0.1.15",
|
||||
"@govoplan/core-webui": "^0.1.16",
|
||||
"@vitejs/plugin-react": "^5.2.0",
|
||||
"lucide-react": "^1.23.0",
|
||||
"react": ">=19.2.7 <20",
|
||||
@@ -417,12 +417,12 @@
|
||||
},
|
||||
"../../govoplan-mail/webui": {
|
||||
"name": "@govoplan/mail-webui",
|
||||
"version": "0.1.15",
|
||||
"version": "0.1.16",
|
||||
"devDependencies": {
|
||||
"typescript": "^5.7.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@govoplan/core-webui": "^0.1.15",
|
||||
"@govoplan/core-webui": "^0.1.16",
|
||||
"lucide-react": "^1.23.0",
|
||||
"react": ">=19.2.7 <20",
|
||||
"react-dom": ">=19.2.7 <20",
|
||||
@@ -436,9 +436,9 @@
|
||||
},
|
||||
"../../govoplan-notifications/webui": {
|
||||
"name": "@govoplan/notifications-webui",
|
||||
"version": "0.1.15",
|
||||
"version": "0.1.16",
|
||||
"peerDependencies": {
|
||||
"@govoplan/core-webui": "^0.1.15",
|
||||
"@govoplan/core-webui": "^0.1.16",
|
||||
"@vitejs/plugin-react": "^5.2.0",
|
||||
"lucide-react": "^1.23.0",
|
||||
"react": ">=19.2.7 <20",
|
||||
@@ -455,9 +455,9 @@
|
||||
},
|
||||
"../../govoplan-ops/webui": {
|
||||
"name": "@govoplan/ops-webui",
|
||||
"version": "0.1.15",
|
||||
"version": "0.1.16",
|
||||
"peerDependencies": {
|
||||
"@govoplan/core-webui": "^0.1.15",
|
||||
"@govoplan/core-webui": "^0.1.16",
|
||||
"@vitejs/plugin-react": "^5.2.0",
|
||||
"lucide-react": "^1.23.0",
|
||||
"react": ">=19.2.7 <20",
|
||||
@@ -474,9 +474,9 @@
|
||||
},
|
||||
"../../govoplan-organizations/webui": {
|
||||
"name": "@govoplan/organizations-webui",
|
||||
"version": "0.1.15",
|
||||
"version": "0.1.16",
|
||||
"peerDependencies": {
|
||||
"@govoplan/core-webui": "^0.1.15",
|
||||
"@govoplan/core-webui": "^0.1.16",
|
||||
"@vitejs/plugin-react": "^5.2.0",
|
||||
"lucide-react": "^1.23.0",
|
||||
"react": ">=19.2.7 <20",
|
||||
@@ -493,9 +493,9 @@
|
||||
},
|
||||
"../../govoplan-policy/webui": {
|
||||
"name": "@govoplan/policy-webui",
|
||||
"version": "0.1.15",
|
||||
"version": "0.1.16",
|
||||
"peerDependencies": {
|
||||
"@govoplan/core-webui": "^0.1.15",
|
||||
"@govoplan/core-webui": "^0.1.16",
|
||||
"lucide-react": "^1.23.0",
|
||||
"react": ">=19.2.7 <20",
|
||||
"react-dom": ">=19.2.7 <20",
|
||||
@@ -509,9 +509,9 @@
|
||||
},
|
||||
"../../govoplan-portal/webui": {
|
||||
"name": "@govoplan/portal-webui",
|
||||
"version": "0.1.15",
|
||||
"version": "0.1.16",
|
||||
"peerDependencies": {
|
||||
"@govoplan/core-webui": "^0.1.15",
|
||||
"@govoplan/core-webui": "^0.1.16",
|
||||
"lucide-react": "^1.23.0",
|
||||
"react": ">=19.2.7 <20",
|
||||
"react-dom": ">=19.2.7 <20",
|
||||
@@ -525,9 +525,9 @@
|
||||
},
|
||||
"../../govoplan-postbox/webui": {
|
||||
"name": "@govoplan/postbox-webui",
|
||||
"version": "0.1.15",
|
||||
"version": "0.1.16",
|
||||
"peerDependencies": {
|
||||
"@govoplan/core-webui": "^0.1.15",
|
||||
"@govoplan/core-webui": "^0.1.16",
|
||||
"lucide-react": "^1.23.0",
|
||||
"react": ">=19.2.7 <20",
|
||||
"react-dom": ">=19.2.7 <20",
|
||||
@@ -541,9 +541,9 @@
|
||||
},
|
||||
"../../govoplan-projects/webui": {
|
||||
"name": "@govoplan/projects-webui",
|
||||
"version": "0.1.15",
|
||||
"version": "0.1.16",
|
||||
"peerDependencies": {
|
||||
"@govoplan/core-webui": "^0.1.15",
|
||||
"@govoplan/core-webui": "^0.1.16",
|
||||
"lucide-react": "^1.23.0",
|
||||
"react": ">=19.2.7 <20",
|
||||
"react-dom": ">=19.2.7 <20",
|
||||
@@ -557,9 +557,9 @@
|
||||
},
|
||||
"../../govoplan-reporting/webui": {
|
||||
"name": "@govoplan/reporting-webui",
|
||||
"version": "0.1.15",
|
||||
"version": "0.1.16",
|
||||
"peerDependencies": {
|
||||
"@govoplan/core-webui": "^0.1.15",
|
||||
"@govoplan/core-webui": "^0.1.16",
|
||||
"lucide-react": "^1.23.0",
|
||||
"react": ">=19.2.7 <20",
|
||||
"react-dom": ">=19.2.7 <20",
|
||||
@@ -573,9 +573,9 @@
|
||||
},
|
||||
"../../govoplan-risk-compliance/webui": {
|
||||
"name": "@govoplan/risk-compliance-webui",
|
||||
"version": "0.1.15",
|
||||
"version": "0.1.16",
|
||||
"peerDependencies": {
|
||||
"@govoplan/core-webui": "^0.1.15",
|
||||
"@govoplan/core-webui": "^0.1.16",
|
||||
"lucide-react": "^1.23.0",
|
||||
"react": ">=19.2.7 <20",
|
||||
"react-dom": ">=19.2.7 <20",
|
||||
@@ -589,9 +589,9 @@
|
||||
},
|
||||
"../../govoplan-scheduling/webui": {
|
||||
"name": "@govoplan/scheduling-webui",
|
||||
"version": "0.1.15",
|
||||
"version": "0.1.16",
|
||||
"peerDependencies": {
|
||||
"@govoplan/core-webui": "^0.1.15",
|
||||
"@govoplan/core-webui": "^0.1.16",
|
||||
"@vitejs/plugin-react": "^5.2.0",
|
||||
"lucide-react": "^1.23.0",
|
||||
"react": ">=19.2.7 <20",
|
||||
@@ -608,9 +608,9 @@
|
||||
},
|
||||
"../../govoplan-search/webui": {
|
||||
"name": "@govoplan/search-webui",
|
||||
"version": "0.1.15",
|
||||
"version": "0.1.16",
|
||||
"peerDependencies": {
|
||||
"@govoplan/core-webui": "^0.1.15",
|
||||
"@govoplan/core-webui": "^0.1.16",
|
||||
"lucide-react": "^1.23.0",
|
||||
"react": ">=19.2.7 <20",
|
||||
"react-dom": ">=19.2.7 <20",
|
||||
@@ -624,9 +624,9 @@
|
||||
},
|
||||
"../../govoplan-templates/webui": {
|
||||
"name": "@govoplan/templates-webui",
|
||||
"version": "0.1.15",
|
||||
"version": "0.1.16",
|
||||
"peerDependencies": {
|
||||
"@govoplan/core-webui": "^0.1.15",
|
||||
"@govoplan/core-webui": "^0.1.16",
|
||||
"lucide-react": "^1.23.0",
|
||||
"react": ">=19.2.7 <20",
|
||||
"react-dom": ">=19.2.7 <20",
|
||||
@@ -641,9 +641,9 @@
|
||||
},
|
||||
"../../govoplan-tenancy/webui": {
|
||||
"name": "@govoplan/tenancy-webui",
|
||||
"version": "0.1.15",
|
||||
"version": "0.1.16",
|
||||
"peerDependencies": {
|
||||
"@govoplan/core-webui": "^0.1.15",
|
||||
"@govoplan/core-webui": "^0.1.16",
|
||||
"lucide-react": "^1.23.0",
|
||||
"react": ">=19.2.7 <20",
|
||||
"react-dom": ">=19.2.7 <20"
|
||||
@@ -656,9 +656,9 @@
|
||||
},
|
||||
"../../govoplan-views/webui": {
|
||||
"name": "@govoplan/views-webui",
|
||||
"version": "0.1.15",
|
||||
"version": "0.1.16",
|
||||
"peerDependencies": {
|
||||
"@govoplan/core-webui": "^0.1.15",
|
||||
"@govoplan/core-webui": "^0.1.16",
|
||||
"lucide-react": "^1.23.0",
|
||||
"react": ">=19.2.7 <20",
|
||||
"react-dom": ">=19.2.7 <20",
|
||||
@@ -672,9 +672,9 @@
|
||||
},
|
||||
"../../govoplan-voting/webui": {
|
||||
"name": "@govoplan/voting-webui",
|
||||
"version": "0.1.15",
|
||||
"version": "0.1.16",
|
||||
"peerDependencies": {
|
||||
"@govoplan/core-webui": "^0.1.15",
|
||||
"@govoplan/core-webui": "^0.1.16",
|
||||
"lucide-react": "^1.23.0",
|
||||
"react": ">=19.2.7 <20",
|
||||
"react-dom": ">=19.2.7 <20"
|
||||
@@ -687,9 +687,9 @@
|
||||
},
|
||||
"../../govoplan-workflow/webui": {
|
||||
"name": "@govoplan/workflow-webui",
|
||||
"version": "0.1.15",
|
||||
"version": "0.1.16",
|
||||
"peerDependencies": {
|
||||
"@govoplan/core-webui": "^0.1.15",
|
||||
"@govoplan/core-webui": "^0.1.16",
|
||||
"@xyflow/react": "^12.11.2",
|
||||
"lucide-react": "^1.23.0",
|
||||
"react": ">=19.2.7 <20",
|
||||
|
||||
@@ -1,26 +1,26 @@
|
||||
{
|
||||
"name": "@govoplan/core-webui",
|
||||
"version": "0.1.15",
|
||||
"version": "0.1.16",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@govoplan/core-webui",
|
||||
"version": "0.1.15",
|
||||
"version": "0.1.16",
|
||||
"dependencies": {
|
||||
"@govoplan/access-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-access.git#v0.1.15",
|
||||
"@govoplan/admin-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-admin.git#v0.1.15",
|
||||
"@govoplan/audit-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-audit.git#v0.1.15",
|
||||
"@govoplan/calendar-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-calendar.git#v0.1.15",
|
||||
"@govoplan/campaign-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-campaign.git#v0.1.15",
|
||||
"@govoplan/dashboard-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-dashboard.git#v0.1.15",
|
||||
"@govoplan/docs-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-docs.git#v0.1.15",
|
||||
"@govoplan/files-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-files.git#v0.1.15",
|
||||
"@govoplan/idm-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-idm.git#v0.1.15",
|
||||
"@govoplan/mail-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-mail.git#v0.1.15",
|
||||
"@govoplan/ops-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-ops.git#v0.1.15",
|
||||
"@govoplan/organizations-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-organizations.git#v0.1.15",
|
||||
"@govoplan/policy-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-policy.git#v0.1.15",
|
||||
"@govoplan/access-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-access.git#v0.1.16",
|
||||
"@govoplan/admin-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-admin.git#v0.1.16",
|
||||
"@govoplan/audit-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-audit.git#v0.1.16",
|
||||
"@govoplan/calendar-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-calendar.git#v0.1.16",
|
||||
"@govoplan/campaign-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-campaign.git#v0.1.16",
|
||||
"@govoplan/dashboard-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-dashboard.git#v0.1.16",
|
||||
"@govoplan/docs-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-docs.git#v0.1.16",
|
||||
"@govoplan/files-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-files.git#v0.1.16",
|
||||
"@govoplan/idm-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-idm.git#v0.1.16",
|
||||
"@govoplan/mail-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-mail.git#v0.1.16",
|
||||
"@govoplan/ops-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-ops.git#v0.1.16",
|
||||
"@govoplan/organizations-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-organizations.git#v0.1.16",
|
||||
"@govoplan/policy-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-policy.git#v0.1.16",
|
||||
"@tiptap/core": "^3.29.2",
|
||||
"@tiptap/extension-image": "^3.29.2",
|
||||
"@tiptap/pm": "^3.29.2",
|
||||
@@ -753,10 +753,10 @@
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/@govoplan/access-webui": {
|
||||
"version": "0.1.15",
|
||||
"resolved": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-access.git#794fcf90f8d759eb140d3b5b0a880482912a79be",
|
||||
"version": "0.1.16",
|
||||
"resolved": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-access.git#a7ed28f6a3cf9e3f5f549db9b0e0785910a8c41a",
|
||||
"peerDependencies": {
|
||||
"@govoplan/core-webui": "^0.1.15",
|
||||
"@govoplan/core-webui": "^0.1.16",
|
||||
"lucide-react": "^1.23.0",
|
||||
"react": ">=19.2.7 <20",
|
||||
"react-dom": ">=19.2.7 <20",
|
||||
@@ -769,10 +769,10 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@govoplan/admin-webui": {
|
||||
"version": "0.1.15",
|
||||
"resolved": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-admin.git#57e2a34c894e20afa46558b83d167e36a1db26c0",
|
||||
"version": "0.1.16",
|
||||
"resolved": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-admin.git#3842987a4ed2b550a3e3c6932db52fa09995e5b4",
|
||||
"peerDependencies": {
|
||||
"@govoplan/core-webui": "^0.1.15",
|
||||
"@govoplan/core-webui": "^0.1.16",
|
||||
"lucide-react": "^1.23.0",
|
||||
"react": ">=19.2.7 <20",
|
||||
"react-dom": ">=19.2.7 <20",
|
||||
@@ -785,10 +785,10 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@govoplan/audit-webui": {
|
||||
"version": "0.1.15",
|
||||
"resolved": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-audit.git#6f095eb56293fa4bc3f3ca5a52d36491c1713b00",
|
||||
"version": "0.1.16",
|
||||
"resolved": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-audit.git#4613b54c06863689eade1012632bd2398d15fef1",
|
||||
"peerDependencies": {
|
||||
"@govoplan/core-webui": "^0.1.15",
|
||||
"@govoplan/core-webui": "^0.1.16",
|
||||
"lucide-react": "^1.23.0",
|
||||
"react": ">=19.2.7 <20",
|
||||
"react-dom": ">=19.2.7 <20",
|
||||
@@ -801,10 +801,10 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@govoplan/calendar-webui": {
|
||||
"version": "0.1.15",
|
||||
"resolved": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-calendar.git#f6faaf196da1891997c5ad7065bbba69aacd018d",
|
||||
"version": "0.1.16",
|
||||
"resolved": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-calendar.git#128c78597edbc6a6fcac844fe1c08b2f9917a872",
|
||||
"peerDependencies": {
|
||||
"@govoplan/core-webui": "^0.1.15",
|
||||
"@govoplan/core-webui": "^0.1.16",
|
||||
"@vitejs/plugin-react": "^5.2.0",
|
||||
"lucide-react": "^1.23.0",
|
||||
"react": ">=19.2.7 <20",
|
||||
@@ -820,13 +820,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@govoplan/campaign-webui": {
|
||||
"version": "0.1.15",
|
||||
"resolved": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-campaign.git#ea0efe661f10474e0a8df0bffde75f3a78577f27",
|
||||
"version": "0.1.16",
|
||||
"resolved": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-campaign.git#2db99eaf6a36b433093d37f53cef0125f4b98599",
|
||||
"dependencies": {
|
||||
"read-excel-file": "9.2.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@govoplan/core-webui": "^0.1.15",
|
||||
"@govoplan/core-webui": "^0.1.16",
|
||||
"lucide-react": "^1.23.0",
|
||||
"react": ">=19.2.7 <20",
|
||||
"react-dom": ">=19.2.7 <20",
|
||||
@@ -839,10 +839,10 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@govoplan/dashboard-webui": {
|
||||
"version": "0.1.15",
|
||||
"resolved": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-dashboard.git#c2c7ba8fb2090ea0e6abcb2f3cd46916a9016b1e",
|
||||
"version": "0.1.16",
|
||||
"resolved": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-dashboard.git#77924e7a2c8366be39c9305477e085802a578d0b",
|
||||
"peerDependencies": {
|
||||
"@govoplan/core-webui": "^0.1.15",
|
||||
"@govoplan/core-webui": "^0.1.16",
|
||||
"lucide-react": "^1.23.0",
|
||||
"react": ">=19.2.7 <20",
|
||||
"react-dom": ">=19.2.7 <20",
|
||||
@@ -855,10 +855,10 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@govoplan/docs-webui": {
|
||||
"version": "0.1.15",
|
||||
"resolved": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-docs.git#1f5050530589da651a49e8d617bf0825e6332388",
|
||||
"version": "0.1.16",
|
||||
"resolved": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-docs.git#8cbab781bf9126062efc6127c89c7eed308955de",
|
||||
"peerDependencies": {
|
||||
"@govoplan/core-webui": "^0.1.15",
|
||||
"@govoplan/core-webui": "^0.1.16",
|
||||
"@vitejs/plugin-react": "^5.2.0",
|
||||
"lucide-react": "^1.23.0",
|
||||
"react": ">=19.2.7 <20",
|
||||
@@ -874,10 +874,10 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@govoplan/files-webui": {
|
||||
"version": "0.1.15",
|
||||
"resolved": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-files.git#0293e49defd34a958584c9665ba006d22db497e1",
|
||||
"version": "0.1.16",
|
||||
"resolved": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-files.git#edee29af51fba34c569defc98a977a0ed80c5718",
|
||||
"peerDependencies": {
|
||||
"@govoplan/core-webui": "^0.1.15",
|
||||
"@govoplan/core-webui": "^0.1.16",
|
||||
"@vitejs/plugin-react": "^5.2.0",
|
||||
"lucide-react": "^1.23.0",
|
||||
"react": ">=19.2.7 <20",
|
||||
@@ -893,10 +893,10 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@govoplan/idm-webui": {
|
||||
"version": "0.1.15",
|
||||
"resolved": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-idm.git#bd72a3f277ce4f214906e276ee1553f4fd23364d",
|
||||
"version": "0.1.16",
|
||||
"resolved": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-idm.git#6e9393c9c7606e91ced908dceef69fd18cf2abe7",
|
||||
"peerDependencies": {
|
||||
"@govoplan/core-webui": "^0.1.15",
|
||||
"@govoplan/core-webui": "^0.1.16",
|
||||
"@vitejs/plugin-react": "^5.2.0",
|
||||
"lucide-react": "^1.23.0",
|
||||
"react": ">=19.2.7 <20",
|
||||
@@ -912,10 +912,10 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@govoplan/mail-webui": {
|
||||
"version": "0.1.15",
|
||||
"resolved": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-mail.git#622f63b4b5fd427128b1a6a9df0ecbe357059d69",
|
||||
"version": "0.1.16",
|
||||
"resolved": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-mail.git#a040de4fe5d9af4a5f434a87d2c5fa8b79e6a818",
|
||||
"peerDependencies": {
|
||||
"@govoplan/core-webui": "^0.1.15",
|
||||
"@govoplan/core-webui": "^0.1.16",
|
||||
"lucide-react": "^1.23.0",
|
||||
"react": ">=19.2.7 <20",
|
||||
"react-dom": ">=19.2.7 <20",
|
||||
@@ -928,10 +928,10 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@govoplan/ops-webui": {
|
||||
"version": "0.1.15",
|
||||
"resolved": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-ops.git#ce074044056827f253eaa9bae920136ebdd8ada8",
|
||||
"version": "0.1.16",
|
||||
"resolved": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-ops.git#02e5d91d565bf7d42cc1b550f18e73621a93a6fa",
|
||||
"peerDependencies": {
|
||||
"@govoplan/core-webui": "^0.1.15",
|
||||
"@govoplan/core-webui": "^0.1.16",
|
||||
"@vitejs/plugin-react": "^5.2.0",
|
||||
"lucide-react": "^1.23.0",
|
||||
"react": ">=19.2.7 <20",
|
||||
@@ -947,10 +947,10 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@govoplan/organizations-webui": {
|
||||
"version": "0.1.15",
|
||||
"resolved": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-organizations.git#aab7f25e866b9738390e2d3026e784d3ad9c3a54",
|
||||
"version": "0.1.16",
|
||||
"resolved": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-organizations.git#18e4df348a3cf69602b2d2491e6ae28c26551b59",
|
||||
"peerDependencies": {
|
||||
"@govoplan/core-webui": "^0.1.15",
|
||||
"@govoplan/core-webui": "^0.1.16",
|
||||
"@vitejs/plugin-react": "^5.2.0",
|
||||
"lucide-react": "^1.23.0",
|
||||
"react": ">=19.2.7 <20",
|
||||
@@ -966,10 +966,10 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@govoplan/policy-webui": {
|
||||
"version": "0.1.15",
|
||||
"resolved": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-policy.git#44d72914aea27a74511fd50f445c450217e3cd5f",
|
||||
"version": "0.1.16",
|
||||
"resolved": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-policy.git#107cc3b654b7b97ec441650bcc3bac6a7accc8a0",
|
||||
"peerDependencies": {
|
||||
"@govoplan/core-webui": "^0.1.15",
|
||||
"@govoplan/core-webui": "^0.1.16",
|
||||
"lucide-react": "^1.23.0",
|
||||
"react": ">=19.2.7 <20",
|
||||
"react-dom": ">=19.2.7 <20",
|
||||
@@ -2059,9 +2059,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/electron-to-chromium": {
|
||||
"version": "1.5.400",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.400.tgz",
|
||||
"integrity": "sha512-96EWDNjM59SYflgeV5Ylsf4EMiq1a25YjCnJH7cxn/AF2H3pILRweaUnoLax0yKHWdpOzY6JKEu45e8irqZIHA==",
|
||||
"version": "1.5.401",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.401.tgz",
|
||||
"integrity": "sha512-H6ViHN68nGYlChEvlIU67fn8O2/tpbWQPwck98yaJmh+08LSvHiydzDQ6oXNccLU3kNRVIRS9A4mA7CG+i6fLQ==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/esbuild": {
|
||||
@@ -2330,9 +2330,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/prosemirror-commands": {
|
||||
"version": "1.7.1",
|
||||
"resolved": "https://registry.npmjs.org/prosemirror-commands/-/prosemirror-commands-1.7.1.tgz",
|
||||
"integrity": "sha512-rT7qZnQtx5c0/y/KlYaGvtG411S97UaL6gdp6RIZ23DLHanMYLyfGBV5DtSnZdthQql7W+lEVbpSfwtO8T+L2w==",
|
||||
"version": "1.7.2",
|
||||
"resolved": "https://registry.npmjs.org/prosemirror-commands/-/prosemirror-commands-1.7.2.tgz",
|
||||
"integrity": "sha512-q6Q6szxqdu9Xd6EcdKsqXghu5nQdZTpB4Q9yd04WRc7/jt763e/rT60Owh0L1GYY+T46o5rD+9lEN36dZS43tw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"prosemirror-model": "^1.0.0",
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@govoplan/core-webui",
|
||||
"version": "0.1.15",
|
||||
"version": "0.1.16",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
|
||||
+14
-14
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@govoplan/core-webui",
|
||||
"version": "0.1.15",
|
||||
"version": "0.1.16",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
@@ -26,19 +26,19 @@
|
||||
"preview": "vite preview --host 127.0.0.1 --port 4173"
|
||||
},
|
||||
"dependencies": {
|
||||
"@govoplan/access-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-access.git#v0.1.15",
|
||||
"@govoplan/admin-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-admin.git#v0.1.15",
|
||||
"@govoplan/audit-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-audit.git#v0.1.15",
|
||||
"@govoplan/calendar-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-calendar.git#v0.1.15",
|
||||
"@govoplan/dashboard-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-dashboard.git#v0.1.15",
|
||||
"@govoplan/docs-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-docs.git#v0.1.15",
|
||||
"@govoplan/files-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-files.git#v0.1.15",
|
||||
"@govoplan/idm-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-idm.git#v0.1.15",
|
||||
"@govoplan/mail-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-mail.git#v0.1.15",
|
||||
"@govoplan/campaign-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-campaign.git#v0.1.15",
|
||||
"@govoplan/organizations-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-organizations.git#v0.1.15",
|
||||
"@govoplan/ops-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-ops.git#v0.1.15",
|
||||
"@govoplan/policy-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-policy.git#v0.1.15",
|
||||
"@govoplan/access-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-access.git#v0.1.16",
|
||||
"@govoplan/admin-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-admin.git#v0.1.16",
|
||||
"@govoplan/audit-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-audit.git#v0.1.16",
|
||||
"@govoplan/calendar-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-calendar.git#v0.1.16",
|
||||
"@govoplan/dashboard-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-dashboard.git#v0.1.16",
|
||||
"@govoplan/docs-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-docs.git#v0.1.16",
|
||||
"@govoplan/files-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-files.git#v0.1.16",
|
||||
"@govoplan/idm-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-idm.git#v0.1.16",
|
||||
"@govoplan/mail-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-mail.git#v0.1.16",
|
||||
"@govoplan/campaign-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-campaign.git#v0.1.16",
|
||||
"@govoplan/organizations-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-organizations.git#v0.1.16",
|
||||
"@govoplan/ops-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-ops.git#v0.1.16",
|
||||
"@govoplan/policy-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-policy.git#v0.1.16",
|
||||
"@tiptap/core": "^3.29.2",
|
||||
"@tiptap/extension-image": "^3.29.2",
|
||||
"@tiptap/pm": "^3.29.2",
|
||||
|
||||
+1
-1
@@ -644,7 +644,7 @@ function normalizeAuthInfo(response: AuthPayload): AuthInfo {
|
||||
principal,
|
||||
available_languages: response.available_languages,
|
||||
enabled_language_codes: profileLoaded ? response.enabled_language_codes ?? activeTenant.enabled_language_codes ?? [] : undefined,
|
||||
default_language: profileLoaded ? response.default_language ?? user.preferred_language ?? activeTenant.default_locale ?? "en" : undefined,
|
||||
default_language: profileLoaded ? response.default_language ?? user.preferred_language ?? activeTenant.default_locale ?? "de" : undefined,
|
||||
profile_loaded: profileLoaded,
|
||||
roles_loaded: rolesLoaded,
|
||||
groups_loaded: groupsLoaded
|
||||
|
||||
@@ -32,14 +32,15 @@ type PlatformLanguageProviderProps = {
|
||||
|
||||
const STORAGE_KEY = "govoplan.language";
|
||||
const PRIMARY_LANGUAGE = "en";
|
||||
const REFERENCE_LANGUAGE = "de";
|
||||
const I18N_MESSAGE_SEPARATOR = "::";
|
||||
const TRANSLATABLE_ATTRIBUTES = ["aria-label", "alt", "placeholder", "title"];
|
||||
const SKIP_TRANSLATION_SELECTOR = "script, style, code, pre, textarea, [data-i18n-skip]";
|
||||
const textNodeOriginals = new WeakMap<Text, string>();
|
||||
|
||||
export const DEFAULT_AVAILABLE_LANGUAGES: PlatformLanguage[] = [
|
||||
{ code: "en", label: "i18n:govoplan-core.english.649df08a", nativeLabel: "i18n:govoplan-core.language_native_english" },
|
||||
{ code: "de", label: "i18n:govoplan-core.german.da91388c", nativeLabel: "i18n:govoplan-core.language_native_german" }];
|
||||
{ code: "de", label: "i18n:govoplan-core.german.da91388c", nativeLabel: "i18n:govoplan-core.language_native_german" },
|
||||
{ code: "en", label: "i18n:govoplan-core.english.649df08a", nativeLabel: "i18n:govoplan-core.language_native_english" }];
|
||||
|
||||
|
||||
export const DEFAULT_TRANSLATIONS: PlatformTranslations = {
|
||||
@@ -186,13 +187,17 @@ function languagesForCodes(languages: PlatformLanguage[], codes?: string[] | nul
|
||||
|
||||
function preferredLanguage(languages: PlatformLanguage[], preferred?: string | null): string {
|
||||
const codes = new Set(languages.map((item) => item.code));
|
||||
const requested = normalizeLanguageCode(preferred ?? storedLanguage() ?? browserLanguage());
|
||||
if (codes.has(requested)) return requested;
|
||||
const primary = primaryLanguageCode(requested);
|
||||
const primaryMatch = languages.find((item) => primaryLanguageCode(item.code) === primary);
|
||||
if (primaryMatch) return primaryMatch.code;
|
||||
const requestedValue = preferred ?? storedLanguage() ?? browserLanguage();
|
||||
if (requestedValue) {
|
||||
const requested = normalizeLanguageCode(requestedValue);
|
||||
if (codes.has(requested)) return requested;
|
||||
const primary = primaryLanguageCode(requested);
|
||||
const primaryMatch = languages.find((item) => primaryLanguageCode(item.code) === primary);
|
||||
if (primaryMatch) return primaryMatch.code;
|
||||
}
|
||||
if (codes.has(REFERENCE_LANGUAGE)) return REFERENCE_LANGUAGE;
|
||||
if (codes.has(PRIMARY_LANGUAGE)) return PRIMARY_LANGUAGE;
|
||||
return languages[0]?.code ?? PRIMARY_LANGUAGE;
|
||||
return languages[0]?.code ?? REFERENCE_LANGUAGE;
|
||||
}
|
||||
|
||||
function primaryLanguageCode(value: string): string {
|
||||
|
||||
@@ -1124,6 +1124,16 @@ export type PlatformModuleInfo = {
|
||||
target_tested_providers: string[];
|
||||
documentation: Record<string, string[]>;
|
||||
} | null;
|
||||
information_governance?: {
|
||||
contract_version: string;
|
||||
current_authorization_for_historical_reads: boolean;
|
||||
dimensions: Record<string, {
|
||||
adoption: "not_applicable" | "contract_only" | "partial" | "enforced";
|
||||
object_types: string[];
|
||||
evidence: string[];
|
||||
limitation?: string | null;
|
||||
}>;
|
||||
};
|
||||
external_providers?: Array<{
|
||||
contract_version: string;
|
||||
id: string;
|
||||
|
||||
Reference in New Issue
Block a user