fix(core): preserve data integrity and bound shared UI and response work
Module Package Release / publish-packages (push) Successful in 13s
Module Package Release / publish-packages (push) Successful in 13s
Release v0.1.46. Coordinated integrity review: GovOPlaN/govoplan-core#298.
This commit is contained in:
Generated
+2
-2
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@govoplan/core-webui",
|
||||
"version": "0.1.45",
|
||||
"version": "0.1.46",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@govoplan/core-webui",
|
||||
"version": "0.1.45",
|
||||
"version": "0.1.46",
|
||||
"dependencies": {
|
||||
"@govoplan/access-webui": "file:../../govoplan-access/webui",
|
||||
"@govoplan/addresses-webui": "file:../../govoplan-addresses/webui",
|
||||
|
||||
@@ -1,37 +1,37 @@
|
||||
{
|
||||
"name": "@govoplan/core-webui",
|
||||
"version": "0.1.45",
|
||||
"version": "0.1.46",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@govoplan/core-webui",
|
||||
"version": "0.1.45",
|
||||
"version": "0.1.46",
|
||||
"dependencies": {
|
||||
"@govoplan/access-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-access.git#v0.1.25",
|
||||
"@govoplan/addresses-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-addresses.git#v0.1.22",
|
||||
"@govoplan/addresses-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-addresses.git#v0.1.23",
|
||||
"@govoplan/admin-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-admin.git#v0.1.23",
|
||||
"@govoplan/approvals-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-approvals.git#v0.1.21",
|
||||
"@govoplan/audit-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-audit.git#v0.1.20",
|
||||
"@govoplan/calendar-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-calendar.git#v0.1.23",
|
||||
"@govoplan/campaign-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-campaign.git#v0.1.28",
|
||||
"@govoplan/cases-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-cases.git#v0.1.24",
|
||||
"@govoplan/committee-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-committee.git#v0.1.21",
|
||||
"@govoplan/connectors-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-connectors.git#v0.1.26",
|
||||
"@govoplan/calendar-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-calendar.git#v0.1.24",
|
||||
"@govoplan/campaign-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-campaign.git#v0.1.29",
|
||||
"@govoplan/cases-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-cases.git#v0.1.25",
|
||||
"@govoplan/committee-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-committee.git#v0.1.22",
|
||||
"@govoplan/connectors-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-connectors.git#v0.1.27",
|
||||
"@govoplan/dashboard-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-dashboard.git#v0.1.20",
|
||||
"@govoplan/dataflow-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-dataflow.git#v0.1.24",
|
||||
"@govoplan/datasources-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-datasources.git#v0.1.25",
|
||||
"@govoplan/dataflow-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-dataflow.git#v0.1.25",
|
||||
"@govoplan/datasources-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-datasources.git#v0.1.26",
|
||||
"@govoplan/dist-lists-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-dist-lists.git#v0.1.21",
|
||||
"@govoplan/docs-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-docs.git#v0.1.23",
|
||||
"@govoplan/encryption-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-encryption.git#v0.1.20",
|
||||
"@govoplan/files-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-files.git#v0.1.26",
|
||||
"@govoplan/forms-runtime-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-forms-runtime.git#v0.1.21",
|
||||
"@govoplan/files-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-files.git#v0.1.27",
|
||||
"@govoplan/forms-runtime-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-forms-runtime.git#v0.1.22",
|
||||
"@govoplan/forms-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-forms.git#v0.1.23",
|
||||
"@govoplan/helpdesk-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-helpdesk.git#v0.1.21",
|
||||
"@govoplan/identity-trust-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-identity-trust.git#v0.1.21",
|
||||
"@govoplan/identity-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-identity.git#v0.1.21",
|
||||
"@govoplan/idm-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-idm.git#v0.1.25",
|
||||
"@govoplan/mail-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-mail.git#v0.1.27",
|
||||
"@govoplan/idm-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-idm.git#v0.1.26",
|
||||
"@govoplan/mail-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-mail.git#v0.1.28",
|
||||
"@govoplan/notifications-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-notifications.git#v0.1.20",
|
||||
"@govoplan/ops-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-ops.git#v0.1.22",
|
||||
"@govoplan/organizations-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-organizations.git#v0.1.21",
|
||||
@@ -42,14 +42,14 @@
|
||||
"@govoplan/projects-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-projects.git#v0.1.20",
|
||||
"@govoplan/quick-access-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-quick-access.git#v0.1.21",
|
||||
"@govoplan/records-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-records.git#v0.1.24",
|
||||
"@govoplan/reporting-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-reporting.git#v0.1.21",
|
||||
"@govoplan/reporting-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-reporting.git#v0.1.22",
|
||||
"@govoplan/risk-compliance-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-risk-compliance.git#v0.1.21",
|
||||
"@govoplan/scheduling-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-scheduling.git#v0.1.22",
|
||||
"@govoplan/search-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-search.git#v0.1.20",
|
||||
"@govoplan/tasks-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-tasks.git#v0.1.23",
|
||||
"@govoplan/templates-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-templates.git#v0.1.22",
|
||||
"@govoplan/tenancy-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-tenancy.git#v0.1.22",
|
||||
"@govoplan/tickets-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-tickets.git#v0.1.22",
|
||||
"@govoplan/tickets-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-tickets.git#v0.1.23",
|
||||
"@govoplan/views-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-views.git#v0.1.22",
|
||||
"@govoplan/voting-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-voting.git#v0.1.21",
|
||||
"@govoplan/wiki-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-wiki.git#v0.1.22",
|
||||
@@ -807,8 +807,8 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@govoplan/addresses-webui": {
|
||||
"version": "0.1.22",
|
||||
"resolved": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-addresses.git#53490e7be780e0b378b5aff3c929e5de59b5e44a",
|
||||
"version": "0.1.23",
|
||||
"resolved": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-addresses.git#d4fa024034e52924e9573b13a46dd0cee2287d9e",
|
||||
"peerDependencies": {
|
||||
"@govoplan/core-webui": "^0.1.45",
|
||||
"lucide-react": "^1.23.0",
|
||||
@@ -870,8 +870,8 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@govoplan/calendar-webui": {
|
||||
"version": "0.1.23",
|
||||
"resolved": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-calendar.git#3e5fc05ca3728464131067d1fa1bc25befeae939",
|
||||
"version": "0.1.24",
|
||||
"resolved": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-calendar.git#8e36f8b3d203fc0b08ea0487540fb7ac597cb8c9",
|
||||
"peerDependencies": {
|
||||
"@govoplan/core-webui": "^0.1.44",
|
||||
"@vitejs/plugin-react": "^5.2.0",
|
||||
@@ -889,8 +889,8 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@govoplan/campaign-webui": {
|
||||
"version": "0.1.28",
|
||||
"resolved": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-campaign.git#c51fc180fba75441dbd19c25b02a3ddc37c81a5d",
|
||||
"version": "0.1.29",
|
||||
"resolved": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-campaign.git#19437ce378553f6ab559e3c0cba44150e495ef52",
|
||||
"dependencies": {
|
||||
"read-excel-file": "9.2.0"
|
||||
},
|
||||
@@ -922,8 +922,8 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@govoplan/cases-webui": {
|
||||
"version": "0.1.24",
|
||||
"resolved": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-cases.git#afda3a5ac5f05898dfbc4daa91a74fd50d59f9c1",
|
||||
"version": "0.1.25",
|
||||
"resolved": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-cases.git#0337e0cf0b37a9b89dd51cccf28500649361fb37",
|
||||
"peerDependencies": {
|
||||
"@govoplan/core-webui": "^0.1.30",
|
||||
"lucide-react": "^1.23.0",
|
||||
@@ -938,8 +938,8 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@govoplan/committee-webui": {
|
||||
"version": "0.1.21",
|
||||
"resolved": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-committee.git#7c2ec4ec17e650699164d99a656ba5e48068fdf7",
|
||||
"version": "0.1.22",
|
||||
"resolved": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-committee.git#96c48a7f2d3a8fa36f44d5806a14003e40cc4807",
|
||||
"peerDependencies": {
|
||||
"@govoplan/core-webui": "^0.1.18",
|
||||
"lucide-react": "^1.23.0",
|
||||
@@ -954,8 +954,8 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@govoplan/connectors-webui": {
|
||||
"version": "0.1.26",
|
||||
"resolved": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-connectors.git#33de5cac5584ad41319a447f12d1eb5dbb11f1d7",
|
||||
"version": "0.1.27",
|
||||
"resolved": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-connectors.git#889cafaf207a2e7e495c64ccbac07d9faf5a5dce",
|
||||
"peerDependencies": {
|
||||
"@govoplan/core-webui": "^0.1.18",
|
||||
"react": ">=19.2.7 <20",
|
||||
@@ -984,10 +984,10 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@govoplan/dataflow-webui": {
|
||||
"version": "0.1.24",
|
||||
"resolved": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-dataflow.git#618f10fe89223b8acbda080047f3c2ed39dae67a",
|
||||
"version": "0.1.25",
|
||||
"resolved": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-dataflow.git#a6c5bab3a4b7a52cb0f9b07911216ce61f362bbe",
|
||||
"peerDependencies": {
|
||||
"@govoplan/core-webui": "^0.1.45",
|
||||
"@govoplan/core-webui": "^0.1.46",
|
||||
"@xyflow/react": "^12.11.2",
|
||||
"lucide-react": "^1.23.0",
|
||||
"react": ">=19.2.7 <20",
|
||||
@@ -1002,10 +1002,10 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@govoplan/datasources-webui": {
|
||||
"version": "0.1.25",
|
||||
"resolved": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-datasources.git#8b8c6c548ecd2801533b9c7d5003f13f3ca41f11",
|
||||
"version": "0.1.26",
|
||||
"resolved": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-datasources.git#9d067f1baded6e65a221fbba0b46fdd953404b51",
|
||||
"peerDependencies": {
|
||||
"@govoplan/core-webui": "^0.1.45",
|
||||
"@govoplan/core-webui": "^0.1.46",
|
||||
"lucide-react": "^1.23.0",
|
||||
"react": ">=19.2.7 <20",
|
||||
"react-dom": ">=19.2.7 <20",
|
||||
@@ -1070,8 +1070,8 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@govoplan/files-webui": {
|
||||
"version": "0.1.26",
|
||||
"resolved": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-files.git#ff84812f7f15bab4385fdcbed8605a7cc1de8195",
|
||||
"version": "0.1.27",
|
||||
"resolved": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-files.git#61625fb00fedbd4474aab1854ba9cedaf5fd18f3",
|
||||
"peerDependencies": {
|
||||
"@govoplan/core-webui": "^0.1.45",
|
||||
"@vitejs/plugin-react": "^5.2.0",
|
||||
@@ -1089,8 +1089,8 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@govoplan/forms-runtime-webui": {
|
||||
"version": "0.1.21",
|
||||
"resolved": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-forms-runtime.git#cd78800a6561a5cc1d04dc9430bd03a9466ea6db",
|
||||
"version": "0.1.22",
|
||||
"resolved": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-forms-runtime.git#1236b861b8128b9fa1ce30c51a32856554d8c3df",
|
||||
"peerDependencies": {
|
||||
"@govoplan/core-webui": "^0.1.18",
|
||||
"lucide-react": "^1.23.0",
|
||||
@@ -1167,8 +1167,8 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@govoplan/idm-webui": {
|
||||
"version": "0.1.25",
|
||||
"resolved": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-idm.git#46e09d0c68866eaf7cf46ccfe773854eac0d0b0b",
|
||||
"version": "0.1.26",
|
||||
"resolved": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-idm.git#ba04593e29140298687a29a6bdf46d1376974c02",
|
||||
"peerDependencies": {
|
||||
"@govoplan/core-webui": "^0.1.45",
|
||||
"@vitejs/plugin-react": "^5.2.0",
|
||||
@@ -1186,8 +1186,8 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@govoplan/mail-webui": {
|
||||
"version": "0.1.27",
|
||||
"resolved": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-mail.git#480c18c67cc345b462ec9dd0dde252fbfc13f571",
|
||||
"version": "0.1.28",
|
||||
"resolved": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-mail.git#d9262982147ff70b4edbc8990d0bf8ccb5f190a8",
|
||||
"peerDependencies": {
|
||||
"@govoplan/core-webui": "^0.1.45",
|
||||
"lucide-react": "^1.23.0",
|
||||
@@ -1371,8 +1371,8 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@govoplan/reporting-webui": {
|
||||
"version": "0.1.21",
|
||||
"resolved": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-reporting.git#89755e1924e52a9f4ee225e5699b9e446adbcd30",
|
||||
"version": "0.1.22",
|
||||
"resolved": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-reporting.git#8253bd0dd6fe93b739b7482a04c058f2bed3aff4",
|
||||
"peerDependencies": {
|
||||
"@govoplan/core-webui": "^0.1.45",
|
||||
"lucide-react": "^1.23.0",
|
||||
@@ -1486,8 +1486,8 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@govoplan/tickets-webui": {
|
||||
"version": "0.1.22",
|
||||
"resolved": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-tickets.git#0ad2ef96b43a83ea7e0e9188487392db735b4a85",
|
||||
"version": "0.1.23",
|
||||
"resolved": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-tickets.git#2be4a0c598a32a5ce044320f2a1311a192b9d5e3",
|
||||
"peerDependencies": {
|
||||
"@govoplan/core-webui": "^0.1.30",
|
||||
"lucide-react": "^1.23.0",
|
||||
@@ -1618,6 +1618,9 @@
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -1734,6 +1737,9 @@
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -1747,6 +1753,9 @@
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"libc": [
|
||||
"musl"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -1760,6 +1769,9 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -1773,6 +1785,9 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"libc": [
|
||||
"musl"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -1786,6 +1801,9 @@
|
||||
"cpu": [
|
||||
"loong64"
|
||||
],
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -1799,6 +1817,9 @@
|
||||
"cpu": [
|
||||
"loong64"
|
||||
],
|
||||
"libc": [
|
||||
"musl"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -1812,6 +1833,9 @@
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -1825,6 +1849,9 @@
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
"libc": [
|
||||
"musl"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -1838,6 +1865,9 @@
|
||||
"cpu": [
|
||||
"riscv64"
|
||||
],
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -1851,6 +1881,9 @@
|
||||
"cpu": [
|
||||
"riscv64"
|
||||
],
|
||||
"libc": [
|
||||
"musl"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -1864,6 +1897,9 @@
|
||||
"cpu": [
|
||||
"s390x"
|
||||
],
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -1877,6 +1913,9 @@
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -1890,6 +1929,9 @@
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"libc": [
|
||||
"musl"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -2840,9 +2882,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/electron-to-chromium": {
|
||||
"version": "1.5.422",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.422.tgz",
|
||||
"integrity": "sha512-UvA/32XqrLDdZSn7Jllo1AYNcWji/G0d5M0GTViE7KoGBiMunw3a34Sb2KO4ZZyrSEhqsxFoVhWWJshdyfKqJA==",
|
||||
"version": "1.5.423",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.423.tgz",
|
||||
"integrity": "sha512-rRZfTSY8ptHYMQxa+uIycJMFKmY1T0GIApNMXJYGehguTZa56TEEl19pKPCoBqk5Gpf7QizZn/jt7xur+DYxag==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/esbuild": {
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@govoplan/core-webui",
|
||||
"version": "0.1.45",
|
||||
"version": "0.1.46",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
|
||||
+15
-15
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@govoplan/core-webui",
|
||||
"version": "0.1.45",
|
||||
"version": "0.1.46",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
@@ -35,29 +35,29 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@govoplan/access-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-access.git#v0.1.25",
|
||||
"@govoplan/addresses-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-addresses.git#v0.1.22",
|
||||
"@govoplan/addresses-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-addresses.git#v0.1.23",
|
||||
"@govoplan/admin-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-admin.git#v0.1.23",
|
||||
"@govoplan/approvals-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-approvals.git#v0.1.21",
|
||||
"@govoplan/audit-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-audit.git#v0.1.20",
|
||||
"@govoplan/calendar-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-calendar.git#v0.1.23",
|
||||
"@govoplan/campaign-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-campaign.git#v0.1.28",
|
||||
"@govoplan/cases-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-cases.git#v0.1.24",
|
||||
"@govoplan/committee-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-committee.git#v0.1.21",
|
||||
"@govoplan/connectors-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-connectors.git#v0.1.26",
|
||||
"@govoplan/calendar-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-calendar.git#v0.1.24",
|
||||
"@govoplan/campaign-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-campaign.git#v0.1.29",
|
||||
"@govoplan/cases-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-cases.git#v0.1.25",
|
||||
"@govoplan/committee-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-committee.git#v0.1.22",
|
||||
"@govoplan/connectors-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-connectors.git#v0.1.27",
|
||||
"@govoplan/dashboard-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-dashboard.git#v0.1.20",
|
||||
"@govoplan/dataflow-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-dataflow.git#v0.1.24",
|
||||
"@govoplan/datasources-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-datasources.git#v0.1.25",
|
||||
"@govoplan/dataflow-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-dataflow.git#v0.1.25",
|
||||
"@govoplan/datasources-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-datasources.git#v0.1.26",
|
||||
"@govoplan/dist-lists-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-dist-lists.git#v0.1.21",
|
||||
"@govoplan/docs-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-docs.git#v0.1.23",
|
||||
"@govoplan/encryption-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-encryption.git#v0.1.20",
|
||||
"@govoplan/files-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-files.git#v0.1.26",
|
||||
"@govoplan/forms-runtime-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-forms-runtime.git#v0.1.21",
|
||||
"@govoplan/files-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-files.git#v0.1.27",
|
||||
"@govoplan/forms-runtime-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-forms-runtime.git#v0.1.22",
|
||||
"@govoplan/forms-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-forms.git#v0.1.23",
|
||||
"@govoplan/helpdesk-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-helpdesk.git#v0.1.21",
|
||||
"@govoplan/identity-trust-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-identity-trust.git#v0.1.21",
|
||||
"@govoplan/identity-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-identity.git#v0.1.21",
|
||||
"@govoplan/idm-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-idm.git#v0.1.25",
|
||||
"@govoplan/mail-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-mail.git#v0.1.27",
|
||||
"@govoplan/idm-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-idm.git#v0.1.26",
|
||||
"@govoplan/mail-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-mail.git#v0.1.28",
|
||||
"@govoplan/notifications-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-notifications.git#v0.1.20",
|
||||
"@govoplan/ops-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-ops.git#v0.1.22",
|
||||
"@govoplan/organizations-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-organizations.git#v0.1.21",
|
||||
@@ -68,14 +68,14 @@
|
||||
"@govoplan/projects-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-projects.git#v0.1.20",
|
||||
"@govoplan/quick-access-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-quick-access.git#v0.1.21",
|
||||
"@govoplan/records-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-records.git#v0.1.24",
|
||||
"@govoplan/reporting-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-reporting.git#v0.1.21",
|
||||
"@govoplan/reporting-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-reporting.git#v0.1.22",
|
||||
"@govoplan/risk-compliance-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-risk-compliance.git#v0.1.21",
|
||||
"@govoplan/scheduling-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-scheduling.git#v0.1.22",
|
||||
"@govoplan/search-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-search.git#v0.1.20",
|
||||
"@govoplan/tasks-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-tasks.git#v0.1.23",
|
||||
"@govoplan/templates-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-templates.git#v0.1.22",
|
||||
"@govoplan/tenancy-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-tenancy.git#v0.1.22",
|
||||
"@govoplan/tickets-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-tickets.git#v0.1.22",
|
||||
"@govoplan/tickets-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-tickets.git#v0.1.23",
|
||||
"@govoplan/views-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-views.git#v0.1.22",
|
||||
"@govoplan/voting-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-voting.git#v0.1.21",
|
||||
"@govoplan/wiki-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-wiki.git#v0.1.22",
|
||||
|
||||
+14
-24
@@ -1,6 +1,7 @@
|
||||
import { Navigate, Route, Routes, useLocation } from "react-router";
|
||||
import { lazy, useEffect, useMemo, useState } from "react";
|
||||
import { fetchSession, fetchShellAuth, logout, updateProfile } from "./api/auth";
|
||||
import { createModuleRefresh } from "./platform/moduleRefresh";
|
||||
import type { AuthActionUiCapability } from "./types";
|
||||
import { fetchPlatformModules, fetchPlatformPublicModules, fetchPlatformStatus } from "./api/platform";
|
||||
import { AUTH_REQUIRED_EVENT, apiSettingsForAuthUpdate, clearApiReadCache, isApiError, loadApiSettings, saveApiSettings, type AuthRequiredEventDetail } from "./api/client";
|
||||
@@ -303,45 +304,34 @@ export default function App() {
|
||||
return;
|
||||
}
|
||||
|
||||
let cancelled = false;
|
||||
let inFlight = false;
|
||||
let lastRefreshAt = 0;
|
||||
function loadModules() {
|
||||
inFlight = true;
|
||||
lastRefreshAt = Date.now();
|
||||
return fetchPlatformModules(settings).
|
||||
then((response) => {if (!cancelled) setPlatformModules(response.modules);}).
|
||||
catch(() => {
|
||||
if (!cancelled) {
|
||||
setPlatformModules(null);
|
||||
setWebModulesLoading(false);
|
||||
}
|
||||
}).
|
||||
finally(() => {inFlight = false;});
|
||||
}
|
||||
|
||||
void loadModules();
|
||||
const refresh = createModuleRefresh({
|
||||
load: () => fetchPlatformModules(settings),
|
||||
accept: (response) => setPlatformModules(response.modules),
|
||||
reject: () => {
|
||||
setPlatformModules(null);
|
||||
setWebModulesLoading(false);
|
||||
}
|
||||
});
|
||||
refresh.invalidate();
|
||||
|
||||
function handleModulesChanged() {
|
||||
void loadModules();
|
||||
refresh.invalidate();
|
||||
}
|
||||
|
||||
function refreshVisibleModules() {
|
||||
if (document.visibilityState === "hidden" || inFlight) return;
|
||||
if (Date.now() - lastRefreshAt < 5_000) return;
|
||||
void loadModules();
|
||||
refresh.refreshVisible(document.visibilityState !== "hidden");
|
||||
}
|
||||
|
||||
window.addEventListener(PLATFORM_MODULES_CHANGED_EVENT, handleModulesChanged);
|
||||
window.addEventListener("focus", refreshVisibleModules);
|
||||
document.addEventListener("visibilitychange", refreshVisibleModules);
|
||||
return () => {
|
||||
cancelled = true;
|
||||
refresh.dispose();
|
||||
window.removeEventListener(PLATFORM_MODULES_CHANGED_EVENT, handleModulesChanged);
|
||||
window.removeEventListener("focus", refreshVisibleModules);
|
||||
document.removeEventListener("visibilitychange", refreshVisibleModules);
|
||||
};
|
||||
}, [auth?.user?.id, auth?.active_tenant?.id, auth?.tenant.id, requiredAuthAction, settings.apiBaseUrl, settings.apiKey]);
|
||||
}, [auth, requiredAuthAction, settings.apiBaseUrl, settings.apiKey, settings.accessToken]);
|
||||
|
||||
useEffect(() => {
|
||||
let cancelled = false;
|
||||
|
||||
Executable
+31
@@ -0,0 +1,31 @@
|
||||
import type { ApiSettings, AuthInfo } from "../types";
|
||||
|
||||
/** Stable in-memory async-work fence, not an authorization decision or cache.
|
||||
* Includes credentials: never persist, log, display or send this key. Cosmetic
|
||||
* profile refreshes must not invalidate a server-accepted mutation.
|
||||
*/
|
||||
export function authAuthorityKey(auth: AuthInfo | null | undefined, settings: ApiSettings): string {
|
||||
const set = (values: readonly string[] | null | undefined) => [...new Set(values ?? [])].sort();
|
||||
const principal = auth?.principal;
|
||||
return JSON.stringify([
|
||||
settings.apiBaseUrl, settings.apiKey, settings.accessToken,
|
||||
auth ? [
|
||||
auth.user.id, auth.user.account_id, auth.user.email,
|
||||
Boolean(auth.user.is_tenant_admin), Boolean(auth.user.password_reset_required),
|
||||
auth.user.required_auth_action ?? null, Boolean(auth.user.local_password),
|
||||
auth.tenant.id, auth.tenant.is_active !== false,
|
||||
(auth.active_tenant ?? auth.tenant).id, (auth.active_tenant ?? auth.tenant).is_active !== false,
|
||||
set(auth.scopes),
|
||||
(auth.roles ?? []).map((role) => JSON.stringify([role.id, role.slug, role.level ?? null, set(role.permissions)])).sort(),
|
||||
set((auth.groups ?? []).map((group) => group.id)),
|
||||
principal ? [
|
||||
principal.account_id, principal.membership_id ?? null, principal.tenant_id ?? null,
|
||||
principal.identity_id ?? null, principal.auth_method, principal.api_key_id ?? null,
|
||||
principal.session_id ?? null, principal.service_account_id ?? null,
|
||||
principal.acting_assignment_id ?? null, principal.acting_for_account_id ?? null,
|
||||
principal.email ?? null, set(principal.scopes), set(principal.group_ids),
|
||||
set(principal.role_ids), set(principal.function_assignment_ids), set(principal.delegation_ids)
|
||||
] : null
|
||||
] : null
|
||||
]);
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import { ArrowDown, ArrowUp, ChevronLeft, ChevronRight, ChevronsLeft, ChevronsRi
|
||||
import StatusBadge from "../StatusBadge";
|
||||
import ListSelectionFilter, { type ListFilterOption } from "../ListSelectionFilter";
|
||||
import TableActionGroup from "./TableActionGroup";
|
||||
import { dataGridRowIndices } from "./dataGridRowIndices";
|
||||
import { usePlatformLanguage, i18nMessage } from "../../i18n/LanguageContext";
|
||||
import {
|
||||
DATA_GRID_MAX_TRACK_WIDTH,
|
||||
@@ -545,6 +546,8 @@ export default function DataGrid<T>({
|
||||
return result;
|
||||
}, [columns, rows]);
|
||||
|
||||
const originalRowIndex = useMemo(() => dataGridRowIndices(rows), [rows]);
|
||||
|
||||
const visibleRows = useMemo(() => {
|
||||
if (serverQueryMode) return rows;
|
||||
const filters = state.filters ?? {};
|
||||
@@ -558,14 +561,14 @@ export default function DataGrid<T>({
|
||||
const sortColumn = columns.find((column) => column.id === state.sort?.columnId);
|
||||
if (!sortColumn) return filtered;
|
||||
return [...filtered].sort((a, b) => {
|
||||
const aIndex = rows.indexOf(a);
|
||||
const bIndex = rows.indexOf(b);
|
||||
const aIndex = originalRowIndex(a);
|
||||
const bIndex = originalRowIndex(b);
|
||||
const aValue = sortColumn.sortValue?.(a, aIndex) ?? sortColumn.value?.(a, aIndex) ?? "";
|
||||
const bValue = sortColumn.sortValue?.(b, bIndex) ?? sortColumn.value?.(b, bIndex) ?? "";
|
||||
const result = compareValues(aValue, bValue);
|
||||
return state.sort?.direction === "desc" ? -result : result;
|
||||
});
|
||||
}, [rows, columns, state.filters, state.sort, filterTypes, serverQueryMode]);
|
||||
}, [rows, columns, state.filters, state.sort, filterTypes, serverQueryMode, originalRowIndex]);
|
||||
|
||||
const paginationMode = pagination?.mode ?? "client";
|
||||
const paginationTotal = pagination ?
|
||||
@@ -851,7 +854,7 @@ export default function DataGrid<T>({
|
||||
</div>);
|
||||
|
||||
})() : renderedRows.map((row, visibleIndex) => {
|
||||
const originalIndex = rows.indexOf(row);
|
||||
const originalIndex = originalRowIndex(row);
|
||||
const rowKey = getRowKey(row, originalIndex);
|
||||
const rowClass = rowClassName?.(row, originalIndex);
|
||||
const parityClass = visibleIndex % 2 === 0 ? "data-grid-row-even" : "data-grid-row-odd";
|
||||
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
/** Cache indexOf's first-occurrence semantics without scanning rows per comparison/cell. */
|
||||
export function dataGridRowIndices<T>(rows: readonly T[]): (row: T) => number {
|
||||
const indices = new Map<T, number>();
|
||||
rows.forEach((row, index) => {
|
||||
if (!indices.has(row)) indices.set(row, index);
|
||||
});
|
||||
// Map uses SameValueZero, whereas indexOf never finds NaN.
|
||||
return (row) => row !== row ? -1 : indices.get(row) ?? -1;
|
||||
}
|
||||
@@ -3,6 +3,7 @@ export * from "./types";
|
||||
export * from "./api/client";
|
||||
export * from "./api/concurrency";
|
||||
export * from "./api/auth";
|
||||
export * from "./api/authAuthority";
|
||||
export * from "./api/platform";
|
||||
export * from "./api/notificationSummary";
|
||||
export * from "./api/adminCommon";
|
||||
|
||||
Executable
+44
@@ -0,0 +1,44 @@
|
||||
/** One request at a time, with a mandatory trailing read after an invalidation.
|
||||
* Superseded responses/errors never publish. This does not cache authorization.
|
||||
*/
|
||||
export function createModuleRefresh<T>({
|
||||
load, accept, reject, now = Date.now
|
||||
}: {
|
||||
load: () => Promise<T>;
|
||||
accept: (value: T) => void;
|
||||
reject: (error: unknown) => void;
|
||||
now?: () => number;
|
||||
}) {
|
||||
let disposed = false;
|
||||
let inFlight = false;
|
||||
let generation = 0;
|
||||
let lastRefreshAt = -Infinity;
|
||||
|
||||
async function run() {
|
||||
if (disposed || inFlight) return;
|
||||
inFlight = true;
|
||||
const requestedGeneration = generation;
|
||||
lastRefreshAt = now();
|
||||
try {
|
||||
const value = await load();
|
||||
if (!disposed && requestedGeneration === generation) accept(value);
|
||||
} catch (error) {
|
||||
if (!disposed && requestedGeneration === generation) reject(error);
|
||||
} finally {
|
||||
inFlight = false;
|
||||
if (!disposed && requestedGeneration !== generation) void run();
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
invalidate() {
|
||||
if (disposed) return;
|
||||
generation += 1;
|
||||
void run();
|
||||
},
|
||||
refreshVisible(visible: boolean) {
|
||||
if (visible && !inFlight && now() - lastRefreshAt >= 5_000) void run();
|
||||
},
|
||||
dispose() { disposed = true; }
|
||||
};
|
||||
}
|
||||
@@ -44,3 +44,60 @@ test("lightweight session changes trigger full auth refresh for required actions
|
||||
assert.equal(sessionMatchesAuth({ ...session, user: { ...session.user, local_password: false } }, auth), false);
|
||||
assert.equal(sessionMatchesAuth({ ...session, session_id: "rotated-session" }, auth), false);
|
||||
});
|
||||
|
||||
const authorityContext = vm.createContext({ module: { exports: {} } });
|
||||
authorityContext.exports = authorityContext.module.exports;
|
||||
vm.runInContext(transformSync(readFileSync(new URL("../src/api/authAuthority.ts", import.meta.url), "utf8"), { loader: "ts", format: "cjs" }).code, authorityContext);
|
||||
const { authAuthorityKey } = authorityContext.module.exports;
|
||||
const settings = { apiBaseUrl: "https://fixture.invalid", apiKey: "fixture-key", accessToken: "fixture-token" };
|
||||
|
||||
test("authority fences ignore fresh object identity and cosmetic profile changes", () => {
|
||||
const auth = normalizeAuthInfo(base);
|
||||
const key = authAuthorityKey(auth, settings);
|
||||
const refreshed = structuredClone(auth);
|
||||
refreshed.user.display_name = "Changed display name";
|
||||
refreshed.user.preferred_language = "de";
|
||||
refreshed.user.ui_preferences = { compact_tables: true };
|
||||
refreshed.tenant.name = "Changed tenant name";
|
||||
refreshed.profile_loaded = true;
|
||||
assert.equal(authAuthorityKey(refreshed, { ...settings }), key);
|
||||
});
|
||||
|
||||
test("authority fences include principal, tenant, credential, scope, acting and required-action changes", () => {
|
||||
const auth = normalizeAuthInfo(base);
|
||||
const key = authAuthorityKey(auth, settings);
|
||||
for (const mutate of [
|
||||
(value) => { value.user.account_id = "different-account"; },
|
||||
(value) => { value.user.email = "different@example.test"; },
|
||||
(value) => { value.user.is_tenant_admin = true; },
|
||||
(value) => { value.user.required_auth_action = "change_password"; },
|
||||
(value) => { value.user.local_password = !value.user.local_password; },
|
||||
(value) => { value.tenant.id = "different-tenant"; },
|
||||
(value) => { value.active_tenant = { ...value.tenant, id: "active-tenant" }; },
|
||||
(value) => { value.tenant.is_active = false; },
|
||||
(value) => { value.scopes = ["new:permission"]; },
|
||||
(value) => { value.roles = [{ id: "role", slug: "role", permissions: ["new:permission"] }]; },
|
||||
(value) => { value.groups = [{ id: "group" }]; },
|
||||
(value) => { value.principal.session_id = "rotated-session"; },
|
||||
(value) => { value.principal.acting_assignment_id = "assignment"; },
|
||||
(value) => { value.principal.acting_for_account_id = "actor"; },
|
||||
(value) => { value.principal.delegation_ids = ["delegation"]; }
|
||||
]) {
|
||||
const changed = structuredClone(auth);
|
||||
mutate(changed);
|
||||
assert.notEqual(authAuthorityKey(changed, settings), key);
|
||||
}
|
||||
for (const field of ["apiBaseUrl", "apiKey", "accessToken"]) {
|
||||
assert.notEqual(authAuthorityKey(auth, { ...settings, [field]: "changed" }), key);
|
||||
}
|
||||
});
|
||||
|
||||
test("authority set ordering and duplicate entries do not invent a context change", () => {
|
||||
const auth = normalizeAuthInfo(base);
|
||||
auth.scopes = ["b", "a", "b"];
|
||||
auth.principal.group_ids = ["g2", "g1"];
|
||||
const reordered = structuredClone(auth);
|
||||
reordered.scopes = ["a", "b"];
|
||||
reordered.principal.group_ids = ["g1", "g2", "g1"];
|
||||
assert.equal(authAuthorityKey(auth, settings), authAuthorityKey(reordered, settings));
|
||||
});
|
||||
|
||||
@@ -6,9 +6,47 @@ import { renderToStaticMarkup } from "react-dom/server";
|
||||
import Button from "../src/components/Button";
|
||||
import DataGrid, { DataGridEmptyAction, DataGridRowActions, type DataGridColumn } from "../src/components/table/DataGrid";
|
||||
import TableActionGroup, { runTableAction } from "../src/components/table/TableActionGroup";
|
||||
import { dataGridRowIndices } from "../src/components/table/dataGridRowIndices";
|
||||
|
||||
function noop() {}
|
||||
|
||||
const repeatedRow = { label: "same" };
|
||||
const distinctRow = { label: "same" };
|
||||
const identityRows = [repeatedRow, distinctRow, repeatedRow];
|
||||
const identityIndex = dataGridRowIndices(identityRows);
|
||||
assertEqual(identityIndex(repeatedRow), 0, "repeated object references retain their first original index");
|
||||
assertEqual(identityIndex(distinctRow), 1, "equal-looking objects keep distinct identities");
|
||||
assertEqual(identityIndex({ label: "same" }), -1, "absent objects retain indexOf semantics");
|
||||
const primitiveRows = [NaN, -0, 2, 2, 0];
|
||||
const primitiveIndex = dataGridRowIndices(primitiveRows);
|
||||
for (const value of primitiveRows) {
|
||||
assertEqual(primitiveIndex(value), primitiveRows.indexOf(value), "primitive indices preserve NaN, zero and duplicate behavior");
|
||||
}
|
||||
let linearVisits = 0;
|
||||
const measuredRows = Array.from({ length: 4_000 }, (_, index) => ({ index }));
|
||||
measuredRows.forEach = (callback) => Array.prototype.forEach.call(measuredRows, (row, index, array) => {
|
||||
linearVisits += 1;
|
||||
callback(row, index, array);
|
||||
});
|
||||
const measuredIndex = dataGridRowIndices(measuredRows);
|
||||
for (let pass = 0; pass < 10; pass += 1) measuredRows.map(measuredIndex);
|
||||
assertEqual(linearVisits, 4_000, "index construction traverses once, independent of lookup count");
|
||||
|
||||
const callbackIndices: number[] = [];
|
||||
const stableRows = [{ id: "a", rank: 2 }, { id: "b", rank: 1 }, { id: "c", rank: 1 }];
|
||||
const stableMarkup = renderToStaticMarkup(<DataGrid
|
||||
id="stable-original-row-index" rows={stableRows}
|
||||
columns={[{ id: "rank", header: "Rank", sortValue: (row, index) => {
|
||||
assertEqual(stableRows[index], row, "sort callbacks receive original indices");
|
||||
return row.rank;
|
||||
}, render: (row, index) => { callbackIndices.push(index); return row.id; } }]}
|
||||
getRowKey={(row, index) => { assertEqual(stableRows[index], row, "row keys receive original indices"); return row.id; }}
|
||||
initialSort={{ columnId: "rank", direction: "asc" }}
|
||||
/>);
|
||||
assertEqual(stableMarkup.includes("stable-original-row-index"), true, "sorted grid renders");
|
||||
assertEqual(callbackIndices.slice(-3).join(), "1,2,0", "equal sort values remain stable without mutating source order after the unchanged sizing pass");
|
||||
assertEqual(stableRows.map((row) => row.id).join(), "a,b,c", "sorting never reorders input data");
|
||||
|
||||
function buttonCount(markup: string): number {
|
||||
return markup.match(/<button\b/g)?.length ?? 0;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { importModuleWithRetry } from "../src/platform/moduleLoading";
|
||||
import { createModuleRefresh } from "../src/platform/moduleRefresh";
|
||||
|
||||
function assert(condition: unknown, message: string): asserts condition {
|
||||
if (!condition) throw new Error(message);
|
||||
@@ -34,3 +35,48 @@ async function verifyModuleImportRetry() {
|
||||
}
|
||||
|
||||
void verifyModuleImportRetry().catch((error) => { throw error; });
|
||||
|
||||
async function verifyModuleRefresh() {
|
||||
const requests: Array<{ resolve: (value: number) => void; reject: (error: unknown) => void }> = [];
|
||||
const accepted: number[] = [];
|
||||
const errors: unknown[] = [];
|
||||
let time = 0;
|
||||
const refresh = createModuleRefresh({
|
||||
load: () => new Promise<number>((resolve, reject) => requests.push({ resolve, reject })),
|
||||
accept: (value) => accepted.push(value), reject: (error) => errors.push(error), now: () => time
|
||||
});
|
||||
const settle = async () => { await Promise.resolve(); await Promise.resolve(); };
|
||||
refresh.invalidate();
|
||||
refresh.invalidate();
|
||||
refresh.invalidate();
|
||||
assert(requests.length === 1, "mutation bursts cannot start overlapping requests");
|
||||
requests[0].resolve(0);
|
||||
await settle();
|
||||
assert(accepted.length === 0 && Number(requests.length) === 2, "obsolete success is ignored and invalidation gets one trailing read");
|
||||
refresh.invalidate();
|
||||
requests[1].reject(new Error("obsolete"));
|
||||
await settle();
|
||||
assert(errors.length === 0 && Number(requests.length) === 3, "obsolete error cannot clear the current catalogue or drop invalidation");
|
||||
requests[2].resolve(2);
|
||||
await settle();
|
||||
assert(accepted.join() === "2", "only the current generation publishes");
|
||||
refresh.refreshVisible(true);
|
||||
time = 6_000;
|
||||
refresh.refreshVisible(false);
|
||||
assert(Number(requests.length) === 3, "focus throttle and hidden-document checks remain");
|
||||
refresh.refreshVisible(true);
|
||||
assert(Number(requests.length) === 4, "visible focus refresh remains available after the throttle");
|
||||
requests[3].reject("current failure");
|
||||
await settle();
|
||||
assert(errors.join() === "current failure", "current errors retain fail-closed handling without automatic retries");
|
||||
refresh.invalidate();
|
||||
refresh.invalidate();
|
||||
refresh.dispose();
|
||||
requests[4].resolve(4);
|
||||
await settle();
|
||||
refresh.invalidate();
|
||||
assert(Number(requests.length) === 5 && accepted.join() === "2", "authority change/unmount discards both old response and queued work");
|
||||
console.log("Module refresh coalescing and generation tests passed.");
|
||||
}
|
||||
|
||||
void verifyModuleRefresh().catch((error) => { throw error; });
|
||||
|
||||
Reference in New Issue
Block a user