@conduction/nextcloud-vue 2.119.0 → 2.121.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/esm/components/CnAiFeaturesWidget/CnAiFeaturesWidget.vue.js +9 -0
- package/dist/esm/components/CnAiFeaturesWidget/CnAiFeaturesWidget.vue.js.map +1 -0
- package/dist/esm/components/CnAiFeaturesWidget/CnAiFeaturesWidget.vue2.js +226 -0
- package/dist/esm/components/CnAiFeaturesWidget/CnAiFeaturesWidget.vue2.js.map +1 -0
- package/dist/esm/components/CnAiFeaturesWidget/CnAiFeaturesWidget.vue3.js +211 -0
- package/dist/esm/components/CnAiFeaturesWidget/CnAiFeaturesWidget.vue3.js.map +1 -0
- package/dist/esm/components/CnAiFeaturesWidget/CnAiSuggestionDecision.vue.js +9 -0
- package/dist/esm/components/CnAiFeaturesWidget/CnAiSuggestionDecision.vue.js.map +1 -0
- package/dist/esm/components/CnAiFeaturesWidget/CnAiSuggestionDecision.vue2.js +64 -0
- package/dist/esm/components/CnAiFeaturesWidget/CnAiSuggestionDecision.vue2.js.map +1 -0
- package/dist/esm/components/CnAiFeaturesWidget/CnAiSuggestionDecision.vue3.js +89 -0
- package/dist/esm/components/CnAiFeaturesWidget/CnAiSuggestionDecision.vue3.js.map +1 -0
- package/dist/esm/components/CnIcon/semanticIcons.js +2 -2
- package/dist/esm/components/CnIcon/semanticIcons.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js +53 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js +546 -523
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js.map +1 -1
- package/dist/esm/components/CnIndexPage/useNamedSource.js +9 -0
- package/dist/esm/components/CnIndexPage/useNamedSource.js.map +1 -1
- package/dist/esm/components/CnObjectPermissionsTab/CnObjectPermissionsTab.vue.js +9 -0
- package/dist/esm/components/CnObjectPermissionsTab/CnObjectPermissionsTab.vue.js.map +1 -0
- package/dist/esm/components/CnObjectPermissionsTab/CnObjectPermissionsTab.vue2.js +257 -0
- package/dist/esm/components/CnObjectPermissionsTab/CnObjectPermissionsTab.vue2.js.map +1 -0
- package/dist/esm/components/CnObjectPermissionsTab/CnObjectPermissionsTab.vue3.js +266 -0
- package/dist/esm/components/CnObjectPermissionsTab/CnObjectPermissionsTab.vue3.js.map +1 -0
- package/dist/esm/components/CnObjectSidebar/CnObjectSidebar.vue.js +1 -1
- package/dist/esm/components/CnObjectSidebar/CnObjectSidebar.vue2.js +14 -7
- package/dist/esm/components/CnObjectSidebar/CnObjectSidebar.vue2.js.map +1 -1
- package/dist/esm/components/CnObjectSidebar/CnObjectSidebar.vue3.js.map +1 -1
- package/dist/esm/components/CnObjectTranslationsWidget/CnObjectTranslationsWidget.vue.js +9 -0
- package/dist/esm/components/CnObjectTranslationsWidget/CnObjectTranslationsWidget.vue.js.map +1 -0
- package/dist/esm/components/CnObjectTranslationsWidget/CnObjectTranslationsWidget.vue2.js +278 -0
- package/dist/esm/components/CnObjectTranslationsWidget/CnObjectTranslationsWidget.vue2.js.map +1 -0
- package/dist/esm/components/CnObjectTranslationsWidget/CnObjectTranslationsWidget.vue3.js +128 -0
- package/dist/esm/components/CnObjectTranslationsWidget/CnObjectTranslationsWidget.vue3.js.map +1 -0
- package/dist/esm/components/CnReadinessChecklist/CnReadinessChecklist.vue.js +9 -0
- package/dist/esm/components/CnReadinessChecklist/CnReadinessChecklist.vue.js.map +1 -0
- package/dist/esm/components/CnReadinessChecklist/CnReadinessChecklist.vue2.js +214 -0
- package/dist/esm/components/CnReadinessChecklist/CnReadinessChecklist.vue2.js.map +1 -0
- package/dist/esm/components/CnReadinessChecklist/CnReadinessChecklist.vue3.js +125 -0
- package/dist/esm/components/CnReadinessChecklist/CnReadinessChecklist.vue3.js.map +1 -0
- package/dist/esm/components/CnSchemaRulesWidget/CnSchemaRulesWidget.vue.js +9 -0
- package/dist/esm/components/CnSchemaRulesWidget/CnSchemaRulesWidget.vue.js.map +1 -0
- package/dist/esm/components/CnSchemaRulesWidget/CnSchemaRulesWidget.vue2.js +163 -0
- package/dist/esm/components/CnSchemaRulesWidget/CnSchemaRulesWidget.vue2.js.map +1 -0
- package/dist/esm/components/CnSchemaRulesWidget/CnSchemaRulesWidget.vue3.js +131 -0
- package/dist/esm/components/CnSchemaRulesWidget/CnSchemaRulesWidget.vue3.js.map +1 -0
- package/dist/esm/components/CnSchemaRulesWidget/schemaRules.js +127 -0
- package/dist/esm/components/CnSchemaRulesWidget/schemaRules.js.map +1 -0
- package/dist/esm/components/CnSearchIndexStatus/CnSearchIndexStatus.vue.js +9 -0
- package/dist/esm/components/CnSearchIndexStatus/CnSearchIndexStatus.vue.js.map +1 -0
- package/dist/esm/components/CnSearchIndexStatus/CnSearchIndexStatus.vue2.js +82 -0
- package/dist/esm/components/CnSearchIndexStatus/CnSearchIndexStatus.vue2.js.map +1 -0
- package/dist/esm/components/CnSearchIndexStatus/CnSearchIndexStatus.vue3.js +61 -0
- package/dist/esm/components/CnSearchIndexStatus/CnSearchIndexStatus.vue3.js.map +1 -0
- package/dist/esm/components/CnSearchIndexStatus/searchIndexStatus.js +60 -0
- package/dist/esm/components/CnSearchIndexStatus/searchIndexStatus.js.map +1 -0
- package/dist/esm/components/CnShippedConfigurationSection/CnShippedConfigurationSection.vue.js +10 -0
- package/dist/esm/components/CnShippedConfigurationSection/CnShippedConfigurationSection.vue.js.map +1 -0
- package/dist/esm/components/CnShippedConfigurationSection/CnShippedConfigurationSection.vue2.js +226 -0
- package/dist/esm/components/CnShippedConfigurationSection/CnShippedConfigurationSection.vue2.js.map +1 -0
- package/dist/esm/components/CnShippedConfigurationSection/CnShippedConfigurationSection.vue3.js +126 -0
- package/dist/esm/components/CnShippedConfigurationSection/CnShippedConfigurationSection.vue3.js.map +1 -0
- package/dist/esm/components/CnStateMachineEditor/CnStateMachineEditor.vue.js +10 -0
- package/dist/esm/components/CnStateMachineEditor/CnStateMachineEditor.vue.js.map +1 -0
- package/dist/esm/components/CnStateMachineEditor/CnStateMachineEditor.vue2.js +944 -0
- package/dist/esm/components/CnStateMachineEditor/CnStateMachineEditor.vue2.js.map +1 -0
- package/dist/esm/components/CnStateMachineEditor/CnStateMachineEditor.vue3.js +276 -0
- package/dist/esm/components/CnStateMachineEditor/CnStateMachineEditor.vue3.js.map +1 -0
- package/dist/esm/components/CnStateMachineEditor/CnStateMachineFields.vue.js +9 -0
- package/dist/esm/components/CnStateMachineEditor/CnStateMachineFields.vue.js.map +1 -0
- package/dist/esm/components/CnStateMachineEditor/CnStateMachineFields.vue2.js +354 -0
- package/dist/esm/components/CnStateMachineEditor/CnStateMachineFields.vue2.js.map +1 -0
- package/dist/esm/components/CnStateMachineEditor/CnStateMachineFields.vue3.js +392 -0
- package/dist/esm/components/CnStateMachineEditor/CnStateMachineFields.vue3.js.map +1 -0
- package/dist/esm/components/CnStateMachineEditor/CnStateMachineNode.vue.js +9 -0
- package/dist/esm/components/CnStateMachineEditor/CnStateMachineNode.vue.js.map +1 -0
- package/dist/esm/components/CnStateMachineEditor/CnStateMachineNode.vue2.js +180 -0
- package/dist/esm/components/CnStateMachineEditor/CnStateMachineNode.vue2.js.map +1 -0
- package/dist/esm/components/CnStateMachineEditor/CnStateMachineNode.vue3.js +208 -0
- package/dist/esm/components/CnStateMachineEditor/CnStateMachineNode.vue3.js.map +1 -0
- package/dist/esm/components/CnStateMachineEditor/CnStateMachinePanel.vue.js +9 -0
- package/dist/esm/components/CnStateMachineEditor/CnStateMachinePanel.vue.js.map +1 -0
- package/dist/esm/components/CnStateMachineEditor/CnStateMachinePanel.vue2.js +244 -0
- package/dist/esm/components/CnStateMachineEditor/CnStateMachinePanel.vue2.js.map +1 -0
- package/dist/esm/components/CnStateMachineEditor/CnStateMachinePanel.vue3.js +76 -0
- package/dist/esm/components/CnStateMachineEditor/CnStateMachinePanel.vue3.js.map +1 -0
- package/dist/esm/components/CnStateMachineEditor/stateMachineFields.js +290 -0
- package/dist/esm/components/CnStateMachineEditor/stateMachineFields.js.map +1 -0
- package/dist/esm/components/CnTimelineWidget/CnTimelineManage.vue.js +9 -0
- package/dist/esm/components/CnTimelineWidget/CnTimelineManage.vue.js.map +1 -0
- package/dist/esm/components/CnTimelineWidget/CnTimelineManage.vue2.js +448 -0
- package/dist/esm/components/CnTimelineWidget/CnTimelineManage.vue2.js.map +1 -0
- package/dist/esm/components/CnTimelineWidget/CnTimelineManage.vue3.js +303 -0
- package/dist/esm/components/CnTimelineWidget/CnTimelineManage.vue3.js.map +1 -0
- package/dist/esm/components/CnTimelineWidget/CnTimelineWidget.vue.js +1 -1
- package/dist/esm/components/CnTimelineWidget/CnTimelineWidget.vue2.js +19 -2
- package/dist/esm/components/CnTimelineWidget/CnTimelineWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnTimelineWidget/CnTimelineWidget.vue3.js +73 -62
- package/dist/esm/components/CnTimelineWidget/CnTimelineWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnWidgetGrid/builtInWidgets.js +6 -0
- package/dist/esm/components/CnWidgetGrid/builtInWidgets.js.map +1 -1
- package/dist/esm/components/CnWidgetGrid/registerDashboardWidgets.js +42 -32
- package/dist/esm/components/CnWidgetGrid/registerDashboardWidgets.js.map +1 -1
- package/dist/esm/composables/indexSources.js +116 -16
- package/dist/esm/composables/indexSources.js.map +1 -1
- package/dist/esm/index.js +9 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/l10n/en.json.js +207 -1
- package/dist/esm/l10n/en.json.js.map +1 -1
- package/dist/esm/l10n/nl.json.js +207 -1
- package/dist/esm/l10n/nl.json.js.map +1 -1
- package/dist/esm/library-style.js +1 -1
- package/dist/esm/nextcloud-vue.css +951 -16
- package/dist/esm/utils/aiFeatures.js +245 -0
- package/dist/esm/utils/aiFeatures.js.map +1 -0
- package/dist/esm/utils/aiSuggestionTracker.js +182 -0
- package/dist/esm/utils/aiSuggestionTracker.js.map +1 -0
- package/dist/esm/utils/libraryWidgetKeys.js +2 -0
- package/dist/esm/utils/libraryWidgetKeys.js.map +1 -1
- package/dist/esm/utils/objectPermissions.js +552 -0
- package/dist/esm/utils/objectPermissions.js.map +1 -0
- package/dist/esm/utils/objectTranslations.js +180 -0
- package/dist/esm/utils/objectTranslations.js.map +1 -0
- package/dist/esm/utils/stateMachineValidation.js +254 -0
- package/dist/esm/utils/stateMachineValidation.js.map +1 -0
- package/dist/esm/utils/widgetDispatch.js +3 -1
- package/dist/esm/utils/widgetDispatch.js.map +1 -1
- package/dist/nextcloud-vue.cjs.js +23038 -14567
- package/dist/nextcloud-vue.cjs.js.map +2 -2
- package/dist/nextcloud-vue.css +951 -16
- package/l10n/en.json +207 -1
- package/l10n/nl.json +207 -1
- package/package.json +1 -1
- package/src/components/CnAiFeaturesWidget/CnAiFeaturesWidget.vue +416 -0
- package/src/components/CnAiFeaturesWidget/CnAiSuggestionDecision.vue +136 -0
- package/src/components/CnAiFeaturesWidget/__tests__/CnAiFeaturesWidget.spec.js +213 -0
- package/src/components/CnAiFeaturesWidget/index.js +14 -0
- package/src/components/CnIndexPage/CnIndexPage.vue +74 -3
- package/src/components/CnIndexPage/useNamedSource.js +9 -0
- package/src/components/CnObjectPermissionsTab/CnObjectPermissionsTab.vue +499 -0
- package/src/components/CnObjectPermissionsTab/__tests__/CnObjectPermissionsTab.spec.js +186 -0
- package/src/components/CnObjectPermissionsTab/index.js +13 -0
- package/src/components/CnObjectSidebar/CnObjectSidebar.vue +8 -1
- package/src/components/CnObjectTranslationsWidget/CnObjectTranslationsWidget.vue +424 -0
- package/src/components/CnObjectTranslationsWidget/__tests__/CnObjectTranslationsWidget.spec.js +189 -0
- package/src/components/CnObjectTranslationsWidget/index.js +13 -0
- package/src/components/CnReadinessChecklist/CnReadinessChecklist.vue +335 -0
- package/src/components/CnReadinessChecklist/index.js +9 -0
- package/src/components/CnSchemaRulesWidget/CnSchemaRulesWidget.vue +300 -0
- package/src/components/CnSchemaRulesWidget/index.js +10 -0
- package/src/components/CnSchemaRulesWidget/schemaRules.js +123 -0
- package/src/components/CnSearchIndexStatus/CnSearchIndexStatus.vue +160 -0
- package/src/components/CnSearchIndexStatus/index.js +10 -0
- package/src/components/CnSearchIndexStatus/searchIndexStatus.js +56 -0
- package/src/components/CnShippedConfigurationSection/CnShippedConfigurationSection.vue +328 -0
- package/src/components/CnShippedConfigurationSection/index.js +9 -0
- package/src/components/CnStateMachineEditor/CnStateMachineEditor.vue +1332 -0
- package/src/components/CnStateMachineEditor/CnStateMachineFields.vue +791 -0
- package/src/components/CnStateMachineEditor/CnStateMachineNode.vue +442 -0
- package/src/components/CnStateMachineEditor/CnStateMachinePanel.vue +326 -0
- package/src/components/CnStateMachineEditor/index.js +8 -0
- package/src/components/CnStateMachineEditor/stateMachineFields.js +287 -0
- package/src/components/CnTimelineWidget/CnTimelineManage.vue +827 -0
- package/src/components/CnTimelineWidget/CnTimelineWidget.vue +29 -3
- package/src/components/CnWidgetGrid/builtInWidgets.js +6 -0
- package/src/components/CnWidgetGrid/registerDashboardWidgets.js +10 -0
- package/src/components/index.js +8 -0
- package/src/composables/indexSources.js +116 -16
- package/src/css/index-page.css +7 -0
- package/src/index.js +9 -0
- package/src/utils/aiFeatures.js +241 -0
- package/src/utils/aiSuggestionTracker.js +178 -0
- package/src/utils/libraryWidgetKeys.js +2 -0
- package/src/utils/objectPermissions.js +548 -0
- package/src/utils/objectTranslations.js +176 -0
- package/src/utils/stateMachineValidation.js +250 -0
- package/src/utils/widgetDispatch.js +3 -1
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
import axios__default from '@nextcloud/axios';
|
|
2
|
+
import { translate } from '@nextcloud/l10n';
|
|
3
|
+
import { prefixUrl } from './headers.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* SPDX-FileCopyrightText: 2026 Conduction B.V.
|
|
7
|
+
* SPDX-License-Identifier: EUPL-1.2
|
|
8
|
+
*
|
|
9
|
+
* The reads and the run behind CnAiFeaturesWidget: the AI features a record's
|
|
10
|
+
* TYPE object places on a surface, where hermiq runs each one, a run on one of
|
|
11
|
+
* the record's documents, and the sentence a refusal is shown as.
|
|
12
|
+
*
|
|
13
|
+
* An undeclared type asks nobody: the declaration is read first and the
|
|
14
|
+
* features endpoint is called only when something is placed on the surface.
|
|
15
|
+
* Every read answers null when it failed, so the widget can say it is
|
|
16
|
+
* incomplete instead of drawing an empty list.
|
|
17
|
+
*
|
|
18
|
+
* The run goes to hermiq in the reader's own session, so their own access
|
|
19
|
+
* decides which document can be read. A request without a usable document
|
|
20
|
+
* reference is refused before it is sent: hermiq's redaction gate sits on the
|
|
21
|
+
* document, and a request without one would run unchecked.
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
/** The gate name for a run refused before it was sent. */
|
|
25
|
+
const MISSING_REFERENCE = 'missing-reference';
|
|
26
|
+
|
|
27
|
+
/** The hermiq endpoint a feature runs on a document through; `{slug}` is filled in. */
|
|
28
|
+
const DEFAULT_RUN_ENDPOINT = '/apps/hermiq/api/ai-features/{slug}/run-on-document';
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* A run that was refused, before sending or by one of hermiq's gates.
|
|
32
|
+
*/
|
|
33
|
+
class AiFeatureRefusal extends Error {
|
|
34
|
+
/**
|
|
35
|
+
* @param {string} feature The feature slug.
|
|
36
|
+
* @param {string} reason The refusing side's own sentence.
|
|
37
|
+
* @param {string|null} gate The gate that refused, when named.
|
|
38
|
+
*/
|
|
39
|
+
constructor(feature, reason, gate) {
|
|
40
|
+
super(reason);
|
|
41
|
+
this.name = 'AiFeatureRefusal';
|
|
42
|
+
this.feature = feature;
|
|
43
|
+
this.reason = reason;
|
|
44
|
+
this.gate = gate;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* The id inside a reference that may be a plain id or an object carrying one.
|
|
50
|
+
*
|
|
51
|
+
* @param {string|object|null|undefined} value The reference.
|
|
52
|
+
* @return {string} The id, or ''.
|
|
53
|
+
*/
|
|
54
|
+
function referenceId(value) {
|
|
55
|
+
if (value !== null && typeof value === 'object') {
|
|
56
|
+
return String(value.id ?? value.uuid ?? '').trim()
|
|
57
|
+
}
|
|
58
|
+
return String(value ?? '').trim()
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* The slugs a type object places on one surface. The declaration is a map of
|
|
63
|
+
* feature slug to surface name.
|
|
64
|
+
*
|
|
65
|
+
* @param {object} typeObject The type object.
|
|
66
|
+
* @param {string} field The declaration field.
|
|
67
|
+
* @param {string} surface The surface.
|
|
68
|
+
* @return {Array<string>} The slugs, in declaration order.
|
|
69
|
+
*/
|
|
70
|
+
function surfaceSlugs(typeObject, field, surface) {
|
|
71
|
+
const declared = typeObject?.[field];
|
|
72
|
+
if (declared === null || typeof declared !== 'object' || Array.isArray(declared)) {
|
|
73
|
+
return []
|
|
74
|
+
}
|
|
75
|
+
return Object.keys(declared).filter((slug) => declared[slug] === surface)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Read one OpenRegister object.
|
|
80
|
+
*
|
|
81
|
+
* @param {string} apiBase API root.
|
|
82
|
+
* @param {string} register Register.
|
|
83
|
+
* @param {string} schema Schema.
|
|
84
|
+
* @param {string} id Object id.
|
|
85
|
+
* @return {Promise<object>} The object.
|
|
86
|
+
*/
|
|
87
|
+
async function readObject(apiBase, register, schema, id) {
|
|
88
|
+
const { data } = await axios__default.get(prefixUrl(`${apiBase}/objects/${register}/${schema}/${encodeURIComponent(id)}`));
|
|
89
|
+
return data?.object ?? data ?? {}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* The AI features the record's type places on the surface, each with where it
|
|
94
|
+
* runs as the features endpoint answers it.
|
|
95
|
+
*
|
|
96
|
+
* @param {object} options The options.
|
|
97
|
+
* @param {string} options.apiBase OpenRegister API root.
|
|
98
|
+
* @param {string} options.register Register.
|
|
99
|
+
* @param {string} options.schema Schema of the record.
|
|
100
|
+
* @param {string} options.objectId The record id.
|
|
101
|
+
* @param {object} options.declaration `{typeField, typeSchema, field, surface}`.
|
|
102
|
+
* @param {string} options.featuresEndpoint The endpoint that asks hermiq.
|
|
103
|
+
* @param {string} options.featuresQueryKey Query key the slugs are sent under.
|
|
104
|
+
* @return {Promise<Array<object>|null>} The features, or null when they could not be read.
|
|
105
|
+
*/
|
|
106
|
+
async function fetchFeatures({ apiBase, register, schema, objectId, declaration, featuresEndpoint, featuresQueryKey }) {
|
|
107
|
+
try {
|
|
108
|
+
const record = await readObject(apiBase, register, schema, objectId);
|
|
109
|
+
const typeId = referenceId(record[declaration.typeField]);
|
|
110
|
+
if (typeId === '') {
|
|
111
|
+
return []
|
|
112
|
+
}
|
|
113
|
+
const typeObject = await readObject(apiBase, register, declaration.typeSchema, typeId);
|
|
114
|
+
const slugs = surfaceSlugs(typeObject, declaration.field || 'aiFeatures', declaration.surface);
|
|
115
|
+
if (slugs.length === 0) {
|
|
116
|
+
return []
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const query = new URLSearchParams({ surface: declaration.surface });
|
|
120
|
+
for (const slug of slugs) {
|
|
121
|
+
query.append(`${featuresQueryKey}[${slug}]`, declaration.surface);
|
|
122
|
+
}
|
|
123
|
+
const { data } = await axios__default.get(`${prefixUrl(featuresEndpoint)}?${query.toString()}`);
|
|
124
|
+
return Array.isArray(data?.features) ? data.features : []
|
|
125
|
+
} catch {
|
|
126
|
+
return null
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* The record's documents a feature can be run on.
|
|
132
|
+
*
|
|
133
|
+
* @param {string} url The files endpoint.
|
|
134
|
+
* @return {Promise<Array<{id: string, title: string}>|null>} The documents, or null.
|
|
135
|
+
*/
|
|
136
|
+
async function fetchDocuments(url) {
|
|
137
|
+
try {
|
|
138
|
+
const { data } = await axios__default.get(prefixUrl(url));
|
|
139
|
+
const rows = Array.isArray(data?.results) ? data.results : (Array.isArray(data) ? data : []);
|
|
140
|
+
return rows
|
|
141
|
+
.filter((row) => row && row.id !== undefined && row.id !== null)
|
|
142
|
+
.map((row) => ({ id: String(row.id), title: String(row.title ?? row.name ?? row.id) }))
|
|
143
|
+
} catch {
|
|
144
|
+
return null
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* The prompts offered on the record, as the endpoint holds them.
|
|
150
|
+
*
|
|
151
|
+
* @param {string} url The prompts endpoint.
|
|
152
|
+
* @return {Promise<Array<{id: string, label: string, prompt: string}>|null>} The prompts, or null.
|
|
153
|
+
*/
|
|
154
|
+
async function fetchPrompts(url) {
|
|
155
|
+
try {
|
|
156
|
+
const { data } = await axios__default.get(prefixUrl(url));
|
|
157
|
+
return Array.isArray(data?.prompts) ? data.prompts : []
|
|
158
|
+
} catch {
|
|
159
|
+
return null
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Ask one prompt about the record.
|
|
165
|
+
*
|
|
166
|
+
* @param {string} url The ask endpoint.
|
|
167
|
+
* @param {string} objectKey Body key naming the record.
|
|
168
|
+
* @param {string} objectId The record id.
|
|
169
|
+
* @param {string} message The prompt.
|
|
170
|
+
* @return {Promise<object>} `{reply, model?}`.
|
|
171
|
+
*/
|
|
172
|
+
async function askAbout(url, objectKey, objectId, message) {
|
|
173
|
+
const { data } = await axios__default.post(prefixUrl(url), { [objectKey]: objectId, message });
|
|
174
|
+
return data
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Where a feature runs, in words. A feature hermiq does not offer reads
|
|
179
|
+
* unavailable, never local.
|
|
180
|
+
*
|
|
181
|
+
* @param {object} feature One feature row.
|
|
182
|
+
* @return {string} The sentence.
|
|
183
|
+
*/
|
|
184
|
+
function whereItRuns(feature) {
|
|
185
|
+
if (feature?.available !== true) {
|
|
186
|
+
return translate('nextcloud-vue', 'Unavailable: hermiq does not offer this feature here.')
|
|
187
|
+
}
|
|
188
|
+
const provider = String(feature.provider ?? '').trim();
|
|
189
|
+
const residency = String(feature.residency ?? '').trim();
|
|
190
|
+
if (provider === '' || residency === '') {
|
|
191
|
+
return translate('nextcloud-vue', 'hermiq did not say which provider runs this, or where.')
|
|
192
|
+
}
|
|
193
|
+
return translate('nextcloud-vue', '{provider}, runs in {residency}', { provider, residency })
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Run one AI feature on one document, through hermiq.
|
|
198
|
+
*
|
|
199
|
+
* @param {object} request The run.
|
|
200
|
+
* @param {string} request.feature The feature slug.
|
|
201
|
+
* @param {string|number} request.documentReference The document's file id.
|
|
202
|
+
* @param {string} request.instruction What to do with it.
|
|
203
|
+
* @param {string} [request.runEndpoint] Endpoint template with `{slug}`.
|
|
204
|
+
* @return {Promise<object>} hermiq's answer.
|
|
205
|
+
* @throws {AiFeatureRefusal} When the reference is missing, or hermiq refuses.
|
|
206
|
+
*/
|
|
207
|
+
async function runFeatureOnDocument({ feature, documentReference, instruction, runEndpoint = DEFAULT_RUN_ENDPOINT }) {
|
|
208
|
+
const reference = String(documentReference ?? '').trim();
|
|
209
|
+
if (/^\d+$/.test(reference) === false) {
|
|
210
|
+
throw new AiFeatureRefusal(feature, translate('nextcloud-vue', 'No document was chosen.'), MISSING_REFERENCE)
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
const url = prefixUrl(runEndpoint.replace('{slug}', encodeURIComponent(feature)));
|
|
214
|
+
try {
|
|
215
|
+
const response = await axios__default.post(url, { documentReference: reference, instruction });
|
|
216
|
+
return response.data
|
|
217
|
+
} catch (error) {
|
|
218
|
+
const status = error?.response?.status;
|
|
219
|
+
const data = error?.response?.data ?? {};
|
|
220
|
+
if ([400, 403, 404, 422].includes(status) === true) {
|
|
221
|
+
throw new AiFeatureRefusal(feature, String(data.error ?? ''), data.gate ?? null)
|
|
222
|
+
}
|
|
223
|
+
throw error
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* The sentence a reader sees for a refusal, naming the feature and the reason.
|
|
229
|
+
*
|
|
230
|
+
* @param {AiFeatureRefusal} refusal The refusal.
|
|
231
|
+
* @param {string} featureLabel The feature's name.
|
|
232
|
+
* @return {string} The sentence.
|
|
233
|
+
*/
|
|
234
|
+
function refusalSentence(refusal, featureLabel) {
|
|
235
|
+
if (refusal.gate === MISSING_REFERENCE) {
|
|
236
|
+
return translate('nextcloud-vue', '{feature} reads a document, and no document was chosen.', { feature: featureLabel })
|
|
237
|
+
}
|
|
238
|
+
if (refusal.gate === 'redaction') {
|
|
239
|
+
return translate('nextcloud-vue', '{feature} will not read a document that has not been redacted.', { feature: featureLabel })
|
|
240
|
+
}
|
|
241
|
+
return translate('nextcloud-vue', '{feature} did not run: {reason}', { feature: featureLabel, reason: refusal.reason })
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
export { AiFeatureRefusal, DEFAULT_RUN_ENDPOINT, MISSING_REFERENCE, askAbout, fetchDocuments, fetchFeatures, fetchPrompts, referenceId, refusalSentence, runFeatureOnDocument, surfaceSlugs, whereItRuns };
|
|
245
|
+
//# sourceMappingURL=aiFeatures.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"aiFeatures.js","sources":["../../../src/utils/aiFeatures.js"],"sourcesContent":["/**\n * SPDX-FileCopyrightText: 2026 Conduction B.V.\n * SPDX-License-Identifier: EUPL-1.2\n *\n * The reads and the run behind CnAiFeaturesWidget: the AI features a record's\n * TYPE object places on a surface, where hermiq runs each one, a run on one of\n * the record's documents, and the sentence a refusal is shown as.\n *\n * An undeclared type asks nobody: the declaration is read first and the\n * features endpoint is called only when something is placed on the surface.\n * Every read answers null when it failed, so the widget can say it is\n * incomplete instead of drawing an empty list.\n *\n * The run goes to hermiq in the reader's own session, so their own access\n * decides which document can be read. A request without a usable document\n * reference is refused before it is sent: hermiq's redaction gate sits on the\n * document, and a request without one would run unchecked.\n */\nimport axios from '@nextcloud/axios'\nimport { translate as t } from '@nextcloud/l10n'\nimport { prefixUrl } from './headers.js'\n\n/** The gate name for a run refused before it was sent. */\nexport const MISSING_REFERENCE = 'missing-reference'\n\n/** The hermiq endpoint a feature runs on a document through; `{slug}` is filled in. */\nexport const DEFAULT_RUN_ENDPOINT = '/apps/hermiq/api/ai-features/{slug}/run-on-document'\n\n/**\n * A run that was refused, before sending or by one of hermiq's gates.\n */\nexport class AiFeatureRefusal extends Error {\n\t/**\n\t * @param {string} feature The feature slug.\n\t * @param {string} reason The refusing side's own sentence.\n\t * @param {string|null} gate The gate that refused, when named.\n\t */\n\tconstructor(feature, reason, gate) {\n\t\tsuper(reason)\n\t\tthis.name = 'AiFeatureRefusal'\n\t\tthis.feature = feature\n\t\tthis.reason = reason\n\t\tthis.gate = gate\n\t}\n}\n\n/**\n * The id inside a reference that may be a plain id or an object carrying one.\n *\n * @param {string|object|null|undefined} value The reference.\n * @return {string} The id, or ''.\n */\nexport function referenceId(value) {\n\tif (value !== null && typeof value === 'object') {\n\t\treturn String(value.id ?? value.uuid ?? '').trim()\n\t}\n\treturn String(value ?? '').trim()\n}\n\n/**\n * The slugs a type object places on one surface. The declaration is a map of\n * feature slug to surface name.\n *\n * @param {object} typeObject The type object.\n * @param {string} field The declaration field.\n * @param {string} surface The surface.\n * @return {Array<string>} The slugs, in declaration order.\n */\nexport function surfaceSlugs(typeObject, field, surface) {\n\tconst declared = typeObject?.[field]\n\tif (declared === null || typeof declared !== 'object' || Array.isArray(declared)) {\n\t\treturn []\n\t}\n\treturn Object.keys(declared).filter((slug) => declared[slug] === surface)\n}\n\n/**\n * Read one OpenRegister object.\n *\n * @param {string} apiBase API root.\n * @param {string} register Register.\n * @param {string} schema Schema.\n * @param {string} id Object id.\n * @return {Promise<object>} The object.\n */\nasync function readObject(apiBase, register, schema, id) {\n\tconst { data } = await axios.get(prefixUrl(`${apiBase}/objects/${register}/${schema}/${encodeURIComponent(id)}`))\n\treturn data?.object ?? data ?? {}\n}\n\n/**\n * The AI features the record's type places on the surface, each with where it\n * runs as the features endpoint answers it.\n *\n * @param {object} options The options.\n * @param {string} options.apiBase OpenRegister API root.\n * @param {string} options.register Register.\n * @param {string} options.schema Schema of the record.\n * @param {string} options.objectId The record id.\n * @param {object} options.declaration `{typeField, typeSchema, field, surface}`.\n * @param {string} options.featuresEndpoint The endpoint that asks hermiq.\n * @param {string} options.featuresQueryKey Query key the slugs are sent under.\n * @return {Promise<Array<object>|null>} The features, or null when they could not be read.\n */\nexport async function fetchFeatures({ apiBase, register, schema, objectId, declaration, featuresEndpoint, featuresQueryKey }) {\n\ttry {\n\t\tconst record = await readObject(apiBase, register, schema, objectId)\n\t\tconst typeId = referenceId(record[declaration.typeField])\n\t\tif (typeId === '') {\n\t\t\treturn []\n\t\t}\n\t\tconst typeObject = await readObject(apiBase, register, declaration.typeSchema, typeId)\n\t\tconst slugs = surfaceSlugs(typeObject, declaration.field || 'aiFeatures', declaration.surface)\n\t\tif (slugs.length === 0) {\n\t\t\treturn []\n\t\t}\n\n\t\tconst query = new URLSearchParams({ surface: declaration.surface })\n\t\tfor (const slug of slugs) {\n\t\t\tquery.append(`${featuresQueryKey}[${slug}]`, declaration.surface)\n\t\t}\n\t\tconst { data } = await axios.get(`${prefixUrl(featuresEndpoint)}?${query.toString()}`)\n\t\treturn Array.isArray(data?.features) ? data.features : []\n\t} catch {\n\t\treturn null\n\t}\n}\n\n/**\n * The record's documents a feature can be run on.\n *\n * @param {string} url The files endpoint.\n * @return {Promise<Array<{id: string, title: string}>|null>} The documents, or null.\n */\nexport async function fetchDocuments(url) {\n\ttry {\n\t\tconst { data } = await axios.get(prefixUrl(url))\n\t\tconst rows = Array.isArray(data?.results) ? data.results : (Array.isArray(data) ? data : [])\n\t\treturn rows\n\t\t\t.filter((row) => row && row.id !== undefined && row.id !== null)\n\t\t\t.map((row) => ({ id: String(row.id), title: String(row.title ?? row.name ?? row.id) }))\n\t} catch {\n\t\treturn null\n\t}\n}\n\n/**\n * The prompts offered on the record, as the endpoint holds them.\n *\n * @param {string} url The prompts endpoint.\n * @return {Promise<Array<{id: string, label: string, prompt: string}>|null>} The prompts, or null.\n */\nexport async function fetchPrompts(url) {\n\ttry {\n\t\tconst { data } = await axios.get(prefixUrl(url))\n\t\treturn Array.isArray(data?.prompts) ? data.prompts : []\n\t} catch {\n\t\treturn null\n\t}\n}\n\n/**\n * Ask one prompt about the record.\n *\n * @param {string} url The ask endpoint.\n * @param {string} objectKey Body key naming the record.\n * @param {string} objectId The record id.\n * @param {string} message The prompt.\n * @return {Promise<object>} `{reply, model?}`.\n */\nexport async function askAbout(url, objectKey, objectId, message) {\n\tconst { data } = await axios.post(prefixUrl(url), { [objectKey]: objectId, message })\n\treturn data\n}\n\n/**\n * Where a feature runs, in words. A feature hermiq does not offer reads\n * unavailable, never local.\n *\n * @param {object} feature One feature row.\n * @return {string} The sentence.\n */\nexport function whereItRuns(feature) {\n\tif (feature?.available !== true) {\n\t\treturn t('nextcloud-vue', 'Unavailable: hermiq does not offer this feature here.')\n\t}\n\tconst provider = String(feature.provider ?? '').trim()\n\tconst residency = String(feature.residency ?? '').trim()\n\tif (provider === '' || residency === '') {\n\t\treturn t('nextcloud-vue', 'hermiq did not say which provider runs this, or where.')\n\t}\n\treturn t('nextcloud-vue', '{provider}, runs in {residency}', { provider, residency })\n}\n\n/**\n * Run one AI feature on one document, through hermiq.\n *\n * @param {object} request The run.\n * @param {string} request.feature The feature slug.\n * @param {string|number} request.documentReference The document's file id.\n * @param {string} request.instruction What to do with it.\n * @param {string} [request.runEndpoint] Endpoint template with `{slug}`.\n * @return {Promise<object>} hermiq's answer.\n * @throws {AiFeatureRefusal} When the reference is missing, or hermiq refuses.\n */\nexport async function runFeatureOnDocument({ feature, documentReference, instruction, runEndpoint = DEFAULT_RUN_ENDPOINT }) {\n\tconst reference = String(documentReference ?? '').trim()\n\tif (/^\\d+$/.test(reference) === false) {\n\t\tthrow new AiFeatureRefusal(feature, t('nextcloud-vue', 'No document was chosen.'), MISSING_REFERENCE)\n\t}\n\n\tconst url = prefixUrl(runEndpoint.replace('{slug}', encodeURIComponent(feature)))\n\ttry {\n\t\tconst response = await axios.post(url, { documentReference: reference, instruction })\n\t\treturn response.data\n\t} catch (error) {\n\t\tconst status = error?.response?.status\n\t\tconst data = error?.response?.data ?? {}\n\t\tif ([400, 403, 404, 422].includes(status) === true) {\n\t\t\tthrow new AiFeatureRefusal(feature, String(data.error ?? ''), data.gate ?? null)\n\t\t}\n\t\tthrow error\n\t}\n}\n\n/**\n * The sentence a reader sees for a refusal, naming the feature and the reason.\n *\n * @param {AiFeatureRefusal} refusal The refusal.\n * @param {string} featureLabel The feature's name.\n * @return {string} The sentence.\n */\nexport function refusalSentence(refusal, featureLabel) {\n\tif (refusal.gate === MISSING_REFERENCE) {\n\t\treturn t('nextcloud-vue', '{feature} reads a document, and no document was chosen.', { feature: featureLabel })\n\t}\n\tif (refusal.gate === 'redaction') {\n\t\treturn t('nextcloud-vue', '{feature} will not read a document that has not been redacted.', { feature: featureLabel })\n\t}\n\treturn t('nextcloud-vue', '{feature} did not run: {reason}', { feature: featureLabel, reason: refusal.reason })\n}\n"],"names":["axios","t"],"mappings":"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"}
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import { getRequestToken } from '@nextcloud/auth';
|
|
2
|
+
import axios__default from '@nextcloud/axios';
|
|
3
|
+
import { prefixUrl } from './headers.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* SPDX-FileCopyrightText: 2026 Conduction B.V.
|
|
7
|
+
* SPDX-License-Identifier: EUPL-1.2
|
|
8
|
+
*
|
|
9
|
+
* Reports what a person did with each AI suggestion on a record, for the AI
|
|
10
|
+
* oversight trail (EU AI Act art. 14). Every suggestion that is shown ends in
|
|
11
|
+
* exactly one report: accepted, modified or rejected when the person acts on
|
|
12
|
+
* it, and ignored when they do not. An ignored suggestion says why: a newer
|
|
13
|
+
* suggestion replaced it (superseded), its surface was closed (closed), or it
|
|
14
|
+
* stayed open past its lifetime (expired).
|
|
15
|
+
*
|
|
16
|
+
* The app's record-action endpoint writes the local audit entry and hands the
|
|
17
|
+
* decision to hermiq; without hermiq it keeps only the local entry.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
/** How long a shown suggestion waits for a decision before it expires. */
|
|
21
|
+
const SUGGESTION_LIFETIME_MS = 30 * 60 * 1000;
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Post one decision to the record-action endpoint.
|
|
25
|
+
*
|
|
26
|
+
* @param {string} endpoint The record-action endpoint.
|
|
27
|
+
* @param {object} body The record-action body.
|
|
28
|
+
* @param {object} options Options.
|
|
29
|
+
* @param {boolean} options.leaving True while the page unloads: the request
|
|
30
|
+
* then uses `fetch` with `keepalive`, which outlives the page.
|
|
31
|
+
* @return {Promise<void>}
|
|
32
|
+
*/
|
|
33
|
+
async function postDecision(endpoint, body, { leaving = false } = {}) {
|
|
34
|
+
const url = prefixUrl(endpoint);
|
|
35
|
+
if (leaving === true) {
|
|
36
|
+
await fetch(url, {
|
|
37
|
+
method: 'POST',
|
|
38
|
+
keepalive: true,
|
|
39
|
+
headers: {
|
|
40
|
+
'Content-Type': 'application/json',
|
|
41
|
+
requesttoken: getRequestToken() ?? '',
|
|
42
|
+
},
|
|
43
|
+
body: JSON.stringify(body),
|
|
44
|
+
});
|
|
45
|
+
return
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
await axios__default.post(url, body);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Track the suggestions shown on one record.
|
|
53
|
+
*
|
|
54
|
+
* A suggestion is shown under a slot key (one feature, or the questions); a
|
|
55
|
+
* newer suggestion in the same slot supersedes an undecided older one.
|
|
56
|
+
*
|
|
57
|
+
* @param {object} options Options.
|
|
58
|
+
* @param {string} options.objectId The record id.
|
|
59
|
+
* @param {string} [options.objectKey] Body key naming the record (default `objectId`).
|
|
60
|
+
* @param {string} [options.endpoint] The record-action endpoint; empty reports nothing.
|
|
61
|
+
* @param {function(object, object): Promise<void>} [options.report] Sends one decision; defaults to posting to `endpoint`.
|
|
62
|
+
* @param {number} [options.lifetimeMs] Lifetime of an undecided suggestion.
|
|
63
|
+
* @param {function(function(): void, number): number} [options.setTimer] Timer factory, for tests.
|
|
64
|
+
* @param {function(number): void} [options.clearTimer] Timer cancel, for tests.
|
|
65
|
+
* @return {object} The tracker: show, decide, dismiss, closeAll, pending.
|
|
66
|
+
*/
|
|
67
|
+
function createSuggestionTracker({
|
|
68
|
+
objectId,
|
|
69
|
+
objectKey = 'objectId',
|
|
70
|
+
endpoint = '',
|
|
71
|
+
report = (body, options) => (endpoint ? postDecision(endpoint, body, options) : Promise.resolve()),
|
|
72
|
+
lifetimeMs = SUGGESTION_LIFETIME_MS,
|
|
73
|
+
setTimer = setTimeout,
|
|
74
|
+
clearTimer = clearTimeout,
|
|
75
|
+
}) {
|
|
76
|
+
const pending = new Map();
|
|
77
|
+
let counter = 0;
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Send the report for one slot's suggestion and forget it.
|
|
81
|
+
*
|
|
82
|
+
* @param {string} slot The slot key.
|
|
83
|
+
* @param {object} decision `{userAction, unusedBecause?, actualValue?, reason?}`.
|
|
84
|
+
* @param {object} options Passed to report.
|
|
85
|
+
* @return {Promise<void>}
|
|
86
|
+
*/
|
|
87
|
+
function settle(slot, decision, options = {}) {
|
|
88
|
+
const shown = pending.get(slot);
|
|
89
|
+
if (shown === undefined) {
|
|
90
|
+
return Promise.resolve()
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
pending.delete(slot);
|
|
94
|
+
clearTimer(shown.timer);
|
|
95
|
+
const body = {
|
|
96
|
+
[objectKey]: objectId,
|
|
97
|
+
type: shown.type,
|
|
98
|
+
suggestion: shown.text,
|
|
99
|
+
prompt: shown.prompt ?? '',
|
|
100
|
+
model: shown.model ?? '',
|
|
101
|
+
documentId: shown.documentId ?? '',
|
|
102
|
+
suggestionId: shown.id,
|
|
103
|
+
...decision,
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
return Promise.resolve(report(body, options)).catch(() => {
|
|
107
|
+
// The person's work must not fail because the oversight trail
|
|
108
|
+
// could not be written; the server logs its own failures.
|
|
109
|
+
})
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return {
|
|
113
|
+
/**
|
|
114
|
+
* Register a suggestion as shown in a slot.
|
|
115
|
+
*
|
|
116
|
+
* @param {string} slot The slot key.
|
|
117
|
+
* @param {object} suggestion `{type, text, prompt?, model?, documentId?}`.
|
|
118
|
+
* @return {string} The suggestion id.
|
|
119
|
+
*/
|
|
120
|
+
show(slot, suggestion) {
|
|
121
|
+
settle(slot, { userAction: 'ignored', unusedBecause: 'superseded' });
|
|
122
|
+
counter += 1;
|
|
123
|
+
const id = `${Date.now().toString(36)}-${counter}`;
|
|
124
|
+
const timer = setTimer(() => {
|
|
125
|
+
settle(slot, { userAction: 'ignored', unusedBecause: 'expired' });
|
|
126
|
+
}, lifetimeMs);
|
|
127
|
+
pending.set(slot, { ...suggestion, id, timer });
|
|
128
|
+
return id
|
|
129
|
+
},
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Report the person's decision on a slot's suggestion.
|
|
133
|
+
*
|
|
134
|
+
* @param {string} slot The slot key.
|
|
135
|
+
* @param {string} userAction accepted, modified or rejected.
|
|
136
|
+
* @param {object} [extra] `{actualValue?, reason?}`.
|
|
137
|
+
* @return {Promise<void>}
|
|
138
|
+
*/
|
|
139
|
+
decide(slot, userAction, extra = {}) {
|
|
140
|
+
return settle(slot, { userAction, ...extra })
|
|
141
|
+
},
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Report a slot's undecided suggestion as ignored, for a reason.
|
|
145
|
+
*
|
|
146
|
+
* @param {string} slot The slot key.
|
|
147
|
+
* @param {string} because closed, expired or superseded.
|
|
148
|
+
* @return {Promise<void>}
|
|
149
|
+
*/
|
|
150
|
+
dismiss(slot, because) {
|
|
151
|
+
return settle(slot, { userAction: 'ignored', unusedBecause: because })
|
|
152
|
+
},
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Report every undecided suggestion as closed.
|
|
156
|
+
*
|
|
157
|
+
* @param {object} [options] `{leaving}` while the page unloads.
|
|
158
|
+
* @return {Promise<void>}
|
|
159
|
+
*/
|
|
160
|
+
closeAll(options = {}) {
|
|
161
|
+
const slots = [...pending.keys()];
|
|
162
|
+
return Promise.all(slots.map((slot) => settle(
|
|
163
|
+
slot,
|
|
164
|
+
{ userAction: 'ignored', unusedBecause: 'closed' },
|
|
165
|
+
options,
|
|
166
|
+
))).then(() => undefined)
|
|
167
|
+
},
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Whether a slot holds an undecided suggestion.
|
|
171
|
+
*
|
|
172
|
+
* @param {string} slot The slot key.
|
|
173
|
+
* @return {boolean} True while undecided.
|
|
174
|
+
*/
|
|
175
|
+
pending(slot) {
|
|
176
|
+
return pending.has(slot)
|
|
177
|
+
},
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
export { SUGGESTION_LIFETIME_MS, createSuggestionTracker, postDecision };
|
|
182
|
+
//# sourceMappingURL=aiSuggestionTracker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"aiSuggestionTracker.js","sources":["../../../src/utils/aiSuggestionTracker.js"],"sourcesContent":["/**\n * SPDX-FileCopyrightText: 2026 Conduction B.V.\n * SPDX-License-Identifier: EUPL-1.2\n *\n * Reports what a person did with each AI suggestion on a record, for the AI\n * oversight trail (EU AI Act art. 14). Every suggestion that is shown ends in\n * exactly one report: accepted, modified or rejected when the person acts on\n * it, and ignored when they do not. An ignored suggestion says why: a newer\n * suggestion replaced it (superseded), its surface was closed (closed), or it\n * stayed open past its lifetime (expired).\n *\n * The app's record-action endpoint writes the local audit entry and hands the\n * decision to hermiq; without hermiq it keeps only the local entry.\n */\nimport { getRequestToken } from '@nextcloud/auth'\nimport axios from '@nextcloud/axios'\nimport { prefixUrl } from './headers.js'\n\n/** How long a shown suggestion waits for a decision before it expires. */\nexport const SUGGESTION_LIFETIME_MS = 30 * 60 * 1000\n\n/**\n * Post one decision to the record-action endpoint.\n *\n * @param {string} endpoint The record-action endpoint.\n * @param {object} body The record-action body.\n * @param {object} options Options.\n * @param {boolean} options.leaving True while the page unloads: the request\n * then uses `fetch` with `keepalive`, which outlives the page.\n * @return {Promise<void>}\n */\nexport async function postDecision(endpoint, body, { leaving = false } = {}) {\n\tconst url = prefixUrl(endpoint)\n\tif (leaving === true) {\n\t\tawait fetch(url, {\n\t\t\tmethod: 'POST',\n\t\t\tkeepalive: true,\n\t\t\theaders: {\n\t\t\t\t'Content-Type': 'application/json',\n\t\t\t\trequesttoken: getRequestToken() ?? '',\n\t\t\t},\n\t\t\tbody: JSON.stringify(body),\n\t\t})\n\t\treturn\n\t}\n\n\tawait axios.post(url, body)\n}\n\n/**\n * Track the suggestions shown on one record.\n *\n * A suggestion is shown under a slot key (one feature, or the questions); a\n * newer suggestion in the same slot supersedes an undecided older one.\n *\n * @param {object} options Options.\n * @param {string} options.objectId The record id.\n * @param {string} [options.objectKey] Body key naming the record (default `objectId`).\n * @param {string} [options.endpoint] The record-action endpoint; empty reports nothing.\n * @param {function(object, object): Promise<void>} [options.report] Sends one decision; defaults to posting to `endpoint`.\n * @param {number} [options.lifetimeMs] Lifetime of an undecided suggestion.\n * @param {function(function(): void, number): number} [options.setTimer] Timer factory, for tests.\n * @param {function(number): void} [options.clearTimer] Timer cancel, for tests.\n * @return {object} The tracker: show, decide, dismiss, closeAll, pending.\n */\nexport function createSuggestionTracker({\n\tobjectId,\n\tobjectKey = 'objectId',\n\tendpoint = '',\n\treport = (body, options) => (endpoint ? postDecision(endpoint, body, options) : Promise.resolve()),\n\tlifetimeMs = SUGGESTION_LIFETIME_MS,\n\tsetTimer = setTimeout,\n\tclearTimer = clearTimeout,\n}) {\n\tconst pending = new Map()\n\tlet counter = 0\n\n\t/**\n\t * Send the report for one slot's suggestion and forget it.\n\t *\n\t * @param {string} slot The slot key.\n\t * @param {object} decision `{userAction, unusedBecause?, actualValue?, reason?}`.\n\t * @param {object} options Passed to report.\n\t * @return {Promise<void>}\n\t */\n\tfunction settle(slot, decision, options = {}) {\n\t\tconst shown = pending.get(slot)\n\t\tif (shown === undefined) {\n\t\t\treturn Promise.resolve()\n\t\t}\n\n\t\tpending.delete(slot)\n\t\tclearTimer(shown.timer)\n\t\tconst body = {\n\t\t\t[objectKey]: objectId,\n\t\t\ttype: shown.type,\n\t\t\tsuggestion: shown.text,\n\t\t\tprompt: shown.prompt ?? '',\n\t\t\tmodel: shown.model ?? '',\n\t\t\tdocumentId: shown.documentId ?? '',\n\t\t\tsuggestionId: shown.id,\n\t\t\t...decision,\n\t\t}\n\n\t\treturn Promise.resolve(report(body, options)).catch(() => {\n\t\t\t// The person's work must not fail because the oversight trail\n\t\t\t// could not be written; the server logs its own failures.\n\t\t})\n\t}\n\n\treturn {\n\t\t/**\n\t\t * Register a suggestion as shown in a slot.\n\t\t *\n\t\t * @param {string} slot The slot key.\n\t\t * @param {object} suggestion `{type, text, prompt?, model?, documentId?}`.\n\t\t * @return {string} The suggestion id.\n\t\t */\n\t\tshow(slot, suggestion) {\n\t\t\tsettle(slot, { userAction: 'ignored', unusedBecause: 'superseded' })\n\t\t\tcounter += 1\n\t\t\tconst id = `${Date.now().toString(36)}-${counter}`\n\t\t\tconst timer = setTimer(() => {\n\t\t\t\tsettle(slot, { userAction: 'ignored', unusedBecause: 'expired' })\n\t\t\t}, lifetimeMs)\n\t\t\tpending.set(slot, { ...suggestion, id, timer })\n\t\t\treturn id\n\t\t},\n\n\t\t/**\n\t\t * Report the person's decision on a slot's suggestion.\n\t\t *\n\t\t * @param {string} slot The slot key.\n\t\t * @param {string} userAction accepted, modified or rejected.\n\t\t * @param {object} [extra] `{actualValue?, reason?}`.\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tdecide(slot, userAction, extra = {}) {\n\t\t\treturn settle(slot, { userAction, ...extra })\n\t\t},\n\n\t\t/**\n\t\t * Report a slot's undecided suggestion as ignored, for a reason.\n\t\t *\n\t\t * @param {string} slot The slot key.\n\t\t * @param {string} because closed, expired or superseded.\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tdismiss(slot, because) {\n\t\t\treturn settle(slot, { userAction: 'ignored', unusedBecause: because })\n\t\t},\n\n\t\t/**\n\t\t * Report every undecided suggestion as closed.\n\t\t *\n\t\t * @param {object} [options] `{leaving}` while the page unloads.\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tcloseAll(options = {}) {\n\t\t\tconst slots = [...pending.keys()]\n\t\t\treturn Promise.all(slots.map((slot) => settle(\n\t\t\t\tslot,\n\t\t\t\t{ userAction: 'ignored', unusedBecause: 'closed' },\n\t\t\t\toptions,\n\t\t\t))).then(() => undefined)\n\t\t},\n\n\t\t/**\n\t\t * Whether a slot holds an undecided suggestion.\n\t\t *\n\t\t * @param {string} slot The slot key.\n\t\t * @return {boolean} True while undecided.\n\t\t */\n\t\tpending(slot) {\n\t\t\treturn pending.has(slot)\n\t\t},\n\t}\n}\n"],"names":["axios"],"mappings":"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"}
|
|
@@ -76,6 +76,7 @@ const BUILT_IN_WIDGET_KEYS = Object.freeze([
|
|
|
76
76
|
'divider',
|
|
77
77
|
'saved-view-alert',
|
|
78
78
|
'exports',
|
|
79
|
+
'object-translations',
|
|
79
80
|
]);
|
|
80
81
|
|
|
81
82
|
/**
|
|
@@ -151,6 +152,7 @@ const DASHBOARD_CATALOG_WIDGET_KEYS = Object.freeze([
|
|
|
151
152
|
'retention',
|
|
152
153
|
'saved-view',
|
|
153
154
|
'saved-views',
|
|
155
|
+
'schema-rules',
|
|
154
156
|
'sections',
|
|
155
157
|
'settings-form',
|
|
156
158
|
'spend-analytics',
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"libraryWidgetKeys.js","sources":["../../../src/utils/libraryWidgetKeys.js"],"sourcesContent":["/**\n * SPDX-FileCopyrightText: 2026 Conduction B.V.\n * SPDX-License-Identifier: EUPL-1.2\n *\n * libraryWidgetKeys — the one vocabulary of widget keys the LIBRARY itself\n * renders, and the only list `validateManifestV2` is allowed to consult.\n *\n * WHY THIS MODULE EXISTS\n * ----------------------\n * The validator used to carry its own hand-written array of \"library built-in\n * widget keys\", with a comment instructing whoever added a built-in to append\n * it \"in the same PR\". That instruction was followed for five keys and missed\n * for six, so the validator knew 11 keys while the library actually rendered\n * 45. Nothing failed when the two disagreed, because the only consumer is an\n * exemption: a key the validator does not recognise is treated as a CUSTOM\n * registry component, which makes a legitimate single-widget library dashboard\n * fail the ADR-036 Decision 1 anti-pattern rule with advice (\"declare it as\n * type:'custom'\") that is wrong for a library widget.\n *\n * A comment cannot enforce anything, so the array moved here and\n * `tests/utils/libraryWidgetKeys.spec.js` now binds it to the two registries\n * that actually resolve a key at runtime:\n *\n * 1. `BUILT_IN_WIDGETS` (components/CnWidgetGrid/builtInWidgets.js) — the v2\n * manifest widget set `CnWidgetGrid` resolves a `widgetKey` against.\n * 2. The dashboard widget catalog (`dashboardWidgetRegistry`), populated by\n * `registerDashboardWidgets.js`. `CnWidgetGrid` falls back to it (third in\n * the chain, after the consumer registry and `BUILT_IN_WIDGETS`), so a\n * catalog key renders on a v2 page exactly like a `BUILT_IN_WIDGETS` one.\n *\n * The test asserts SET EQUALITY in both directions against the live registries,\n * so adding a widget to either registry without touching this file fails CI,\n * and deleting one without removing it here fails too. The lists below are the\n * validator's view; the registries are the truth the test holds them to.\n *\n * THE CATALOG IS NOT LEGACY. It is a live half of the same vocabulary, and the\n * old array already conceded the point by exempting `chart` and `stats-block`,\n * which are catalog-only keys that `BUILT_IN_WIDGETS` has never held. What it\n * got wrong was doing that for two keys and not the other thirty-seven.\n * `widgetDispatch.js` unified the two resolution paths for the same reason: a\n * widget authored against one vocabulary was invisible to the other and said\n * nothing about why (101 of 236 detail widgets on hrmq).\n *\n * @module utils/libraryWidgetKeys\n */\n\nimport { WIDGET_TYPE_ALIASES } from './widgetTypeAliases.js'\n\n/**\n * Keys of `BUILT_IN_WIDGETS` — the v2 manifest widget set.\n *\n * Kept in the registry's own declaration order so a diff against\n * `builtInWidgets.js` reads straight down.\n *\n * @type {string[]} Frozen.\n */\nexport const BUILT_IN_WIDGET_KEYS = Object.freeze([\n\t'object-table',\n\t'form-renderer',\n\t'map-viewer',\n\t'notice',\n\t'decision-list',\n\t'object-geo',\n\t'card-grid',\n\t'nav-card-grid',\n\t'data',\n\t'metadata',\n\t'related',\n\t'integration',\n\t'banner',\n\t'audit-trail',\n\t'timeline',\n\t'header',\n\t'text',\n\t'divider',\n\t'saved-view-alert',\n\t'exports',\n])\n\n/**\n * Widget types the LIBRARY registers into the dashboard catalog at import time\n * (`registerDashboardWidgets.js` and the widget `index.js` modules it pulls in).\n *\n * Deliberately a static list rather than a read of `dashboardWidgetRegistry`:\n * that object is mutable and consumer-extensible, so deriving the exemption\n * from it at call time would let an app register its own full-page component in\n * the catalog and walk straight through the ADR-036 Decision 1 rule the\n * exemption exists to enforce. Only keys the library itself ships are exempt.\n *\n * Sorted, because unlike `BUILT_IN_WIDGETS` these registrations are spread over\n * ~30 files and have no meaningful declaration order.\n *\n * @type {string[]} Frozen.\n */\nexport const DASHBOARD_CATALOG_WIDGET_KEYS = Object.freeze([\n\t'ai-chat',\n\t'attention-flag',\n\t'audit-trail',\n\t'banner',\n\t'calendar',\n\t'chart',\n\t'checklist',\n\t'checklist-run',\n\t'chips-field',\n\t'choice-picker',\n\t'container',\n\t'conversation',\n\t'countdown',\n\t'data',\n\t'delta',\n\t'device-frames',\n\t'divider',\n\t'document-review',\n\t'events',\n\t'facts',\n\t'field-list',\n\t'files',\n\t'flow-runs',\n\t'gauge',\n\t'grouped-cards',\n\t'header',\n\t'image',\n\t'interaction-form',\n\t'kb-search',\n\t'label',\n\t'link',\n\t'linked',\n\t'links',\n\t'macros',\n\t'mail',\n\t'map',\n\t'markdown',\n\t'menu',\n\t'nc-widget',\n\t'next-step',\n\t'notepad',\n\t// `news` was here until the widget moved to LaunchPad, which owns the feed\n\t// fetching. An app that renders one now gets it from LaunchPad's registry.\n\t'object-geo',\n\t'object-list',\n\t'object-table',\n\t'parties',\n\t'party-actions',\n\t'people',\n\t'plan',\n\t'quicklinks',\n\t'radio-cards',\n\t'record-list',\n\t'related',\n\t'retention',\n\t'saved-view',\n\t'saved-views',\n\t'sections',\n\t'settings-form',\n\t'spend-analytics',\n\t'stacked-bar',\n\t'stages',\n\t'stat',\n\t'stats-block',\n\t'status-explainer',\n\t'switch-rows',\n\t'table',\n\t'tabs',\n\t'tasks',\n\t'team-workload',\n\t'text',\n\t'tile',\n\t'timeline',\n\t'toggle-table',\n\t'video',\n\t'week-strip',\n\t'working-calendar',\n\t'work-queue',\n\t'workspace-filter',\n])\n\n/**\n * Expand a key set over `WIDGET_TYPE_ALIASES` so both spellings of an aliased\n * widget are present whenever either one is.\n *\n * `CnWidgetGrid` resolves a key and its canonical form (`object-table` ⇄\n * `table`, `map-viewer` ⇄ `map`), so both spellings render and both must be\n * exempt. Today each registry happens to hold one side of each pair, which is\n * exactly the accident that would silently stop being true when the next alias\n * lands.\n *\n * @param {Set<string>} keys The key set to expand, mutated in place.\n * @return {Set<string>} The same set.\n */\nfunction expandAliases(keys) {\n\tfor (const [alias, canonical] of Object.entries(WIDGET_TYPE_ALIASES)) {\n\t\tif (keys.has(alias) || keys.has(canonical)) {\n\t\t\tkeys.add(alias)\n\t\t\tkeys.add(canonical)\n\t\t}\n\t}\n\treturn keys\n}\n\n/**\n * Every widget key the library renders itself: both registries, plus both\n * spellings of every alias.\n *\n * A `widgetKey` in this list is served by a library `Cn*` SFC and does NOT\n * count as a custom registry component for the single-12×12-widget dashboard\n * rule (ADR-036 Decision 1).\n *\n * @type {string[]} Frozen.\n */\nexport const LIBRARY_WIDGET_KEYS = Object.freeze([\n\t...expandAliases(new Set([\n\t\t...BUILT_IN_WIDGET_KEYS,\n\t\t...DASHBOARD_CATALOG_WIDGET_KEYS,\n\t])),\n])\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"libraryWidgetKeys.js","sources":["../../../src/utils/libraryWidgetKeys.js"],"sourcesContent":["/**\n * SPDX-FileCopyrightText: 2026 Conduction B.V.\n * SPDX-License-Identifier: EUPL-1.2\n *\n * libraryWidgetKeys — the one vocabulary of widget keys the LIBRARY itself\n * renders, and the only list `validateManifestV2` is allowed to consult.\n *\n * WHY THIS MODULE EXISTS\n * ----------------------\n * The validator used to carry its own hand-written array of \"library built-in\n * widget keys\", with a comment instructing whoever added a built-in to append\n * it \"in the same PR\". That instruction was followed for five keys and missed\n * for six, so the validator knew 11 keys while the library actually rendered\n * 45. Nothing failed when the two disagreed, because the only consumer is an\n * exemption: a key the validator does not recognise is treated as a CUSTOM\n * registry component, which makes a legitimate single-widget library dashboard\n * fail the ADR-036 Decision 1 anti-pattern rule with advice (\"declare it as\n * type:'custom'\") that is wrong for a library widget.\n *\n * A comment cannot enforce anything, so the array moved here and\n * `tests/utils/libraryWidgetKeys.spec.js` now binds it to the two registries\n * that actually resolve a key at runtime:\n *\n * 1. `BUILT_IN_WIDGETS` (components/CnWidgetGrid/builtInWidgets.js) — the v2\n * manifest widget set `CnWidgetGrid` resolves a `widgetKey` against.\n * 2. The dashboard widget catalog (`dashboardWidgetRegistry`), populated by\n * `registerDashboardWidgets.js`. `CnWidgetGrid` falls back to it (third in\n * the chain, after the consumer registry and `BUILT_IN_WIDGETS`), so a\n * catalog key renders on a v2 page exactly like a `BUILT_IN_WIDGETS` one.\n *\n * The test asserts SET EQUALITY in both directions against the live registries,\n * so adding a widget to either registry without touching this file fails CI,\n * and deleting one without removing it here fails too. The lists below are the\n * validator's view; the registries are the truth the test holds them to.\n *\n * THE CATALOG IS NOT LEGACY. It is a live half of the same vocabulary, and the\n * old array already conceded the point by exempting `chart` and `stats-block`,\n * which are catalog-only keys that `BUILT_IN_WIDGETS` has never held. What it\n * got wrong was doing that for two keys and not the other thirty-seven.\n * `widgetDispatch.js` unified the two resolution paths for the same reason: a\n * widget authored against one vocabulary was invisible to the other and said\n * nothing about why (101 of 236 detail widgets on hrmq).\n *\n * @module utils/libraryWidgetKeys\n */\n\nimport { WIDGET_TYPE_ALIASES } from './widgetTypeAliases.js'\n\n/**\n * Keys of `BUILT_IN_WIDGETS` — the v2 manifest widget set.\n *\n * Kept in the registry's own declaration order so a diff against\n * `builtInWidgets.js` reads straight down.\n *\n * @type {string[]} Frozen.\n */\nexport const BUILT_IN_WIDGET_KEYS = Object.freeze([\n\t'object-table',\n\t'form-renderer',\n\t'map-viewer',\n\t'notice',\n\t'decision-list',\n\t'object-geo',\n\t'card-grid',\n\t'nav-card-grid',\n\t'data',\n\t'metadata',\n\t'related',\n\t'integration',\n\t'banner',\n\t'audit-trail',\n\t'timeline',\n\t'header',\n\t'text',\n\t'divider',\n\t'saved-view-alert',\n\t'exports',\n\t'object-translations',\n])\n\n/**\n * Widget types the LIBRARY registers into the dashboard catalog at import time\n * (`registerDashboardWidgets.js` and the widget `index.js` modules it pulls in).\n *\n * Deliberately a static list rather than a read of `dashboardWidgetRegistry`:\n * that object is mutable and consumer-extensible, so deriving the exemption\n * from it at call time would let an app register its own full-page component in\n * the catalog and walk straight through the ADR-036 Decision 1 rule the\n * exemption exists to enforce. Only keys the library itself ships are exempt.\n *\n * Sorted, because unlike `BUILT_IN_WIDGETS` these registrations are spread over\n * ~30 files and have no meaningful declaration order.\n *\n * @type {string[]} Frozen.\n */\nexport const DASHBOARD_CATALOG_WIDGET_KEYS = Object.freeze([\n\t'ai-chat',\n\t'attention-flag',\n\t'audit-trail',\n\t'banner',\n\t'calendar',\n\t'chart',\n\t'checklist',\n\t'checklist-run',\n\t'chips-field',\n\t'choice-picker',\n\t'container',\n\t'conversation',\n\t'countdown',\n\t'data',\n\t'delta',\n\t'device-frames',\n\t'divider',\n\t'document-review',\n\t'events',\n\t'facts',\n\t'field-list',\n\t'files',\n\t'flow-runs',\n\t'gauge',\n\t'grouped-cards',\n\t'header',\n\t'image',\n\t'interaction-form',\n\t'kb-search',\n\t'label',\n\t'link',\n\t'linked',\n\t'links',\n\t'macros',\n\t'mail',\n\t'map',\n\t'markdown',\n\t'menu',\n\t'nc-widget',\n\t'next-step',\n\t'notepad',\n\t// `news` was here until the widget moved to LaunchPad, which owns the feed\n\t// fetching. An app that renders one now gets it from LaunchPad's registry.\n\t'object-geo',\n\t'object-list',\n\t'object-table',\n\t'parties',\n\t'party-actions',\n\t'people',\n\t'plan',\n\t'quicklinks',\n\t'radio-cards',\n\t'record-list',\n\t'related',\n\t'retention',\n\t'saved-view',\n\t'saved-views',\n\t'schema-rules',\n\t'sections',\n\t'settings-form',\n\t'spend-analytics',\n\t'stacked-bar',\n\t'stages',\n\t'stat',\n\t'stats-block',\n\t'status-explainer',\n\t'switch-rows',\n\t'table',\n\t'tabs',\n\t'tasks',\n\t'team-workload',\n\t'text',\n\t'tile',\n\t'timeline',\n\t'toggle-table',\n\t'video',\n\t'week-strip',\n\t'working-calendar',\n\t'work-queue',\n\t'workspace-filter',\n])\n\n/**\n * Expand a key set over `WIDGET_TYPE_ALIASES` so both spellings of an aliased\n * widget are present whenever either one is.\n *\n * `CnWidgetGrid` resolves a key and its canonical form (`object-table` ⇄\n * `table`, `map-viewer` ⇄ `map`), so both spellings render and both must be\n * exempt. Today each registry happens to hold one side of each pair, which is\n * exactly the accident that would silently stop being true when the next alias\n * lands.\n *\n * @param {Set<string>} keys The key set to expand, mutated in place.\n * @return {Set<string>} The same set.\n */\nfunction expandAliases(keys) {\n\tfor (const [alias, canonical] of Object.entries(WIDGET_TYPE_ALIASES)) {\n\t\tif (keys.has(alias) || keys.has(canonical)) {\n\t\t\tkeys.add(alias)\n\t\t\tkeys.add(canonical)\n\t\t}\n\t}\n\treturn keys\n}\n\n/**\n * Every widget key the library renders itself: both registries, plus both\n * spellings of every alias.\n *\n * A `widgetKey` in this list is served by a library `Cn*` SFC and does NOT\n * count as a custom registry component for the single-12×12-widget dashboard\n * rule (ADR-036 Decision 1).\n *\n * @type {string[]} Frozen.\n */\nexport const LIBRARY_WIDGET_KEYS = Object.freeze([\n\t...expandAliases(new Set([\n\t\t...BUILT_IN_WIDGET_KEYS,\n\t\t...DASHBOARD_CATALOG_WIDGET_KEYS,\n\t])),\n])\n"],"names":[],"mappings":";;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACY,MAAC,oBAAoB,GAAG,MAAM,CAAC,MAAM,CAAC;AAClD,CAAC,cAAc;AACf,CAAC,eAAe;AAChB,CAAC,YAAY;AACb,CAAC,QAAQ;AACT,CAAC,eAAe;AAChB,CAAC,YAAY;AACb,CAAC,WAAW;AACZ,CAAC,eAAe;AAChB,CAAC,MAAM;AACP,CAAC,UAAU;AACX,CAAC,SAAS;AACV,CAAC,aAAa;AACd,CAAC,QAAQ;AACT,CAAC,aAAa;AACd,CAAC,UAAU;AACX,CAAC,QAAQ;AACT,CAAC,MAAM;AACP,CAAC,SAAS;AACV,CAAC,kBAAkB;AACnB,CAAC,SAAS;AACV,CAAC,qBAAqB;AACtB,CAAC,EAAC;AACF;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACY,MAAC,6BAA6B,GAAG,MAAM,CAAC,MAAM,CAAC;AAC3D,CAAC,SAAS;AACV,CAAC,gBAAgB;AACjB,CAAC,aAAa;AACd,CAAC,QAAQ;AACT,CAAC,UAAU;AACX,CAAC,OAAO;AACR,CAAC,WAAW;AACZ,CAAC,eAAe;AAChB,CAAC,aAAa;AACd,CAAC,eAAe;AAChB,CAAC,WAAW;AACZ,CAAC,cAAc;AACf,CAAC,WAAW;AACZ,CAAC,MAAM;AACP,CAAC,OAAO;AACR,CAAC,eAAe;AAChB,CAAC,SAAS;AACV,CAAC,iBAAiB;AAClB,CAAC,QAAQ;AACT,CAAC,OAAO;AACR,CAAC,YAAY;AACb,CAAC,OAAO;AACR,CAAC,WAAW;AACZ,CAAC,OAAO;AACR,CAAC,eAAe;AAChB,CAAC,QAAQ;AACT,CAAC,OAAO;AACR,CAAC,kBAAkB;AACnB,CAAC,WAAW;AACZ,CAAC,OAAO;AACR,CAAC,MAAM;AACP,CAAC,QAAQ;AACT,CAAC,OAAO;AACR,CAAC,QAAQ;AACT,CAAC,MAAM;AACP,CAAC,KAAK;AACN,CAAC,UAAU;AACX,CAAC,MAAM;AACP,CAAC,WAAW;AACZ,CAAC,WAAW;AACZ,CAAC,SAAS;AACV;AACA;AACA,CAAC,YAAY;AACb,CAAC,aAAa;AACd,CAAC,cAAc;AACf,CAAC,SAAS;AACV,CAAC,eAAe;AAChB,CAAC,QAAQ;AACT,CAAC,MAAM;AACP,CAAC,YAAY;AACb,CAAC,aAAa;AACd,CAAC,aAAa;AACd,CAAC,SAAS;AACV,CAAC,WAAW;AACZ,CAAC,YAAY;AACb,CAAC,aAAa;AACd,CAAC,cAAc;AACf,CAAC,UAAU;AACX,CAAC,eAAe;AAChB,CAAC,iBAAiB;AAClB,CAAC,aAAa;AACd,CAAC,QAAQ;AACT,CAAC,MAAM;AACP,CAAC,aAAa;AACd,CAAC,kBAAkB;AACnB,CAAC,aAAa;AACd,CAAC,OAAO;AACR,CAAC,MAAM;AACP,CAAC,OAAO;AACR,CAAC,eAAe;AAChB,CAAC,MAAM;AACP,CAAC,MAAM;AACP,CAAC,UAAU;AACX,CAAC,cAAc;AACf,CAAC,OAAO;AACR,CAAC,YAAY;AACb,CAAC,kBAAkB;AACnB,CAAC,YAAY;AACb,CAAC,kBAAkB;AACnB,CAAC,EAAC;AACF;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAAS,aAAa,CAAC,IAAI,EAAE;AAC7B,CAAC,KAAK,MAAM,CAAC,KAAK,EAAE,SAAS,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,mBAAmB,CAAC,EAAE;AACvE,EAAE,IAAI,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE;AAC9C,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,EAAC;AAClB,GAAG,IAAI,CAAC,GAAG,CAAC,SAAS,EAAC;AACtB,GAAG;AACH,EAAE;AACF,CAAC,OAAO,IAAI;AACZ,CAAC;AACD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACY,MAAC,mBAAmB,GAAG,MAAM,CAAC,MAAM,CAAC;AACjD,CAAC,GAAG,aAAa,CAAC,IAAI,GAAG,CAAC;AAC1B,EAAE,GAAG,oBAAoB;AACzB,EAAE,GAAG,6BAA6B;AAClC,EAAE,CAAC,CAAC;AACJ,CAAC;;;;"}
|