@vertesia/common 1.5.0-dev.20260717.131047Z → 1.5.0-dev.20260722.120446Z

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.
Files changed (110) hide show
  1. package/lib/apps.d.ts +144 -47
  2. package/lib/apps.d.ts.map +1 -1
  3. package/lib/apps.js +21 -9
  4. package/lib/apps.js.map +1 -1
  5. package/lib/audit-trail.d.ts +61 -1
  6. package/lib/audit-trail.d.ts.map +1 -1
  7. package/lib/audit-trail.js +15 -0
  8. package/lib/audit-trail.js.map +1 -1
  9. package/lib/index.d.ts +7 -0
  10. package/lib/index.d.ts.map +1 -1
  11. package/lib/index.js +7 -0
  12. package/lib/index.js.map +1 -1
  13. package/lib/interaction.d.ts +4 -0
  14. package/lib/interaction.d.ts.map +1 -1
  15. package/lib/interaction.js.map +1 -1
  16. package/lib/platform-event.d.ts +37 -3
  17. package/lib/platform-event.d.ts.map +1 -1
  18. package/lib/platform-event.js.map +1 -1
  19. package/lib/project.d.ts +119 -20
  20. package/lib/project.d.ts.map +1 -1
  21. package/lib/project.js +65 -0
  22. package/lib/project.js.map +1 -1
  23. package/lib/query.d.ts +6 -0
  24. package/lib/query.d.ts.map +1 -1
  25. package/lib/refs.d.ts +1 -0
  26. package/lib/refs.d.ts.map +1 -1
  27. package/lib/schema-for-extraction.d.ts +21 -0
  28. package/lib/schema-for-extraction.d.ts.map +1 -0
  29. package/lib/schema-for-extraction.js +205 -0
  30. package/lib/schema-for-extraction.js.map +1 -0
  31. package/lib/store/conversation-state.d.ts +1 -1
  32. package/lib/store/doc-analyzer.d.ts +10 -67
  33. package/lib/store/doc-analyzer.d.ts.map +1 -1
  34. package/lib/store/dsl-workflow.d.ts +1 -0
  35. package/lib/store/dsl-workflow.d.ts.map +1 -1
  36. package/lib/store/dsl-workflow.js.map +1 -1
  37. package/lib/store/grounded-extraction.d.ts +146 -0
  38. package/lib/store/grounded-extraction.d.ts.map +1 -0
  39. package/lib/store/grounded-extraction.js +8 -0
  40. package/lib/store/grounded-extraction.js.map +1 -0
  41. package/lib/store/index.d.ts +1 -0
  42. package/lib/store/index.d.ts.map +1 -1
  43. package/lib/store/index.js +1 -0
  44. package/lib/store/index.js.map +1 -1
  45. package/lib/store/store.d.ts +307 -1
  46. package/lib/store/store.d.ts.map +1 -1
  47. package/lib/store/store.js +438 -0
  48. package/lib/store/store.js.map +1 -1
  49. package/lib/store/workflow.d.ts +3 -0
  50. package/lib/store/workflow.d.ts.map +1 -1
  51. package/lib/store/workflow.js.map +1 -1
  52. package/lib/user.d.ts +14 -0
  53. package/lib/user.d.ts.map +1 -1
  54. package/lib/user.js +31 -0
  55. package/lib/user.js.map +1 -1
  56. package/lib/vertesia-common.js +2 -2
  57. package/lib/vertesia-common.js.map +1 -1
  58. package/lib/view-configuration-validation.d.ts +15 -0
  59. package/lib/view-configuration-validation.d.ts.map +1 -0
  60. package/lib/view-configuration-validation.js +63 -0
  61. package/lib/view-configuration-validation.js.map +1 -0
  62. package/lib/view-query-validation.d.ts +13 -0
  63. package/lib/view-query-validation.d.ts.map +1 -0
  64. package/lib/view-query-validation.js +266 -0
  65. package/lib/view-query-validation.js.map +1 -0
  66. package/lib/view-validation-helpers.d.ts +15 -0
  67. package/lib/view-validation-helpers.d.ts.map +1 -0
  68. package/lib/view-validation-helpers.js +25 -0
  69. package/lib/view-validation-helpers.js.map +1 -0
  70. package/lib/views-schema.d.ts +1992 -0
  71. package/lib/views-schema.d.ts.map +1 -0
  72. package/lib/views-schema.js +674 -0
  73. package/lib/views-schema.js.map +1 -0
  74. package/lib/views-validation.d.ts +21 -0
  75. package/lib/views-validation.d.ts.map +1 -0
  76. package/lib/views-validation.js +164 -0
  77. package/lib/views-validation.js.map +1 -0
  78. package/lib/views.d.ts +381 -0
  79. package/lib/views.d.ts.map +1 -0
  80. package/lib/views.js +41 -0
  81. package/lib/views.js.map +1 -0
  82. package/package.json +5 -5
  83. package/src/apps.test.ts +9 -1
  84. package/src/apps.ts +171 -73
  85. package/src/audit-trail.ts +83 -0
  86. package/src/index.ts +12 -0
  87. package/src/interaction.ts +4 -0
  88. package/src/platform-event.ts +39 -2
  89. package/src/project.test.ts +44 -0
  90. package/src/project.ts +205 -22
  91. package/src/query.ts +6 -0
  92. package/src/refs.ts +1 -0
  93. package/src/schema-for-extraction.test.ts +191 -0
  94. package/src/schema-for-extraction.ts +231 -0
  95. package/src/store/conversation-state.ts +1 -1
  96. package/src/store/doc-analyzer.ts +10 -76
  97. package/src/store/dsl-workflow.ts +1 -0
  98. package/src/store/grounded-extraction.ts +154 -0
  99. package/src/store/index.ts +1 -0
  100. package/src/store/store.ts +778 -1
  101. package/src/store/workflow.ts +3 -0
  102. package/src/user.ts +46 -0
  103. package/src/view-configuration-validation.ts +74 -0
  104. package/src/view-query-validation.test.ts +21 -0
  105. package/src/view-query-validation.ts +319 -0
  106. package/src/view-validation-helpers.ts +28 -0
  107. package/src/views-schema.test.ts +364 -0
  108. package/src/views-schema.ts +689 -0
  109. package/src/views-validation.ts +234 -0
  110. package/src/views.ts +484 -0
@@ -0,0 +1,234 @@
1
+ import { validateSupportedViewQuery } from './view-query-validation.js';
2
+ import type {
3
+ ViewDisplayConfiguration,
4
+ ViewExperienceConfiguration,
5
+ ViewNavigationItem,
6
+ ViewResultMedia,
7
+ } from './views.js';
8
+
9
+ export interface ViewValidationIssue {
10
+ path: string;
11
+ message: string;
12
+ }
13
+
14
+ const EXPERIENCE_ID_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
15
+ const APP_VIEW_ID_PATTERN = /^app:([A-Za-z0-9][A-Za-z0-9._@-]*):([A-Za-z0-9][A-Za-z0-9._:-]*)$/;
16
+
17
+ function addDuplicateIssues(
18
+ issues: ViewValidationIssue[],
19
+ values: readonly string[],
20
+ pathForIndex: (index: number) => string,
21
+ message = 'must be unique',
22
+ ): void {
23
+ const seen = new Set<string>();
24
+ values.forEach((value, index) => {
25
+ if (seen.has(value)) {
26
+ issues.push({ path: pathForIndex(index), message });
27
+ }
28
+ seen.add(value);
29
+ });
30
+ }
31
+
32
+ function addMediaIssues(issues: ViewValidationIssue[], media: ViewResultMedia | undefined, path: string): void {
33
+ if (media?.source === 'property' && media.field === undefined) {
34
+ issues.push({ path: `${path}.field`, message: 'is required when media source is property' });
35
+ }
36
+ }
37
+
38
+ function addDisplayIssues(
39
+ issues: ViewValidationIssue[],
40
+ display: ViewDisplayConfiguration,
41
+ index: number,
42
+ sortOptionIds: ReadonlySet<string>,
43
+ ): void {
44
+ const path = `results.displays[${index}]`;
45
+ if (display.type === 'list') {
46
+ addMediaIssues(issues, display.media, `${path}.media`);
47
+ } else if (display.type === 'table') {
48
+ display.columns.forEach((column, columnIndex) => {
49
+ if (column.sort_option !== undefined && !sortOptionIds.has(column.sort_option)) {
50
+ issues.push({
51
+ path: `${path}.columns[${columnIndex}].sort_option`,
52
+ message: 'must reference a configured sort option',
53
+ });
54
+ }
55
+ });
56
+ } else if (display.type === 'cards') {
57
+ addMediaIssues(issues, display.media, `${path}.media`);
58
+ } else if (display.type === 'gallery') {
59
+ addMediaIssues(issues, display.media, `${path}.media`);
60
+ } else {
61
+ addMediaIssues(issues, display.card.media, `${path}.card.media`);
62
+ }
63
+ }
64
+
65
+ function addNavigationIssues(issues: ViewValidationIssue[], navigation: readonly ViewNavigationItem[]): void {
66
+ addDuplicateIssues(
67
+ issues,
68
+ navigation.map((item) => item.id),
69
+ (index) => `navigation[${index}].id`,
70
+ );
71
+
72
+ navigation.forEach((item, index) => {
73
+ const path = `navigation[${index}]`;
74
+ if (item.source === 'hierarchy') {
75
+ addDuplicateIssues(
76
+ issues,
77
+ item.levels.map((level) => level.id),
78
+ (levelIndex) => `${path}.levels[${levelIndex}].id`,
79
+ 'must be unique within the hierarchy',
80
+ );
81
+ addDuplicateIssues(
82
+ issues,
83
+ item.levels.map((level) => level.field),
84
+ (levelIndex) => `${path}.levels[${levelIndex}].field`,
85
+ 'must be unique within the hierarchy',
86
+ );
87
+ } else if (item.source === 'range') {
88
+ addDuplicateIssues(
89
+ issues,
90
+ item.ranges.map((range) => range.id),
91
+ (rangeIndex) => `${path}.ranges[${rangeIndex}].id`,
92
+ 'must be unique within the range facet',
93
+ );
94
+ item.ranges.forEach((range, rangeIndex) => {
95
+ if (range.from !== undefined && range.to !== undefined && range.from >= range.to) {
96
+ issues.push({
97
+ path: `${path}.ranges[${rangeIndex}]`,
98
+ message: 'from must be less than to',
99
+ });
100
+ }
101
+ });
102
+ }
103
+ });
104
+ }
105
+
106
+ function addSearchIssues(issues: ViewValidationIssue[], configuration: ViewExperienceConfiguration): void {
107
+ const search = configuration.search;
108
+ if (search === undefined) return;
109
+
110
+ if (search.mode !== 'agentic' && search.agentic !== undefined) {
111
+ issues.push({ path: 'search.agentic', message: 'requires search.mode to be agentic' });
112
+ }
113
+
114
+ const fields = search.fields ?? [];
115
+ addDuplicateIssues(
116
+ issues,
117
+ fields.map((field) => field.field),
118
+ (index) => `search.fields[${index}].field`,
119
+ );
120
+ fields.forEach((field, index) => {
121
+ if (field.mode === 'full_text' && field.type !== undefined && field.type !== 'text') {
122
+ issues.push({
123
+ path: `search.fields[${index}].type`,
124
+ message: 'must be text when mode is full_text',
125
+ });
126
+ }
127
+ });
128
+
129
+ const keyTerms = search.key_terms ?? [];
130
+ addDuplicateIssues(
131
+ issues,
132
+ keyTerms.map((term) => term.id),
133
+ (index) => `search.key_terms[${index}].id`,
134
+ );
135
+ if (search.mode !== 'agentic') {
136
+ keyTerms.forEach((term, index) => {
137
+ if (term.field === undefined) {
138
+ issues.push({
139
+ path: `search.key_terms[${index}].field`,
140
+ message: 'is required for deterministic search',
141
+ });
142
+ }
143
+ });
144
+ }
145
+ }
146
+
147
+ function addResultsIssues(issues: ViewValidationIssue[], configuration: ViewExperienceConfiguration): void {
148
+ const results = configuration.results;
149
+ if (results === undefined) return;
150
+
151
+ const sortOptions = results.sort_options ?? [];
152
+ addDuplicateIssues(
153
+ issues,
154
+ sortOptions.map((option) => option.id),
155
+ (index) => `results.sort_options[${index}].id`,
156
+ );
157
+ const sortOptionIds = new Set(sortOptions.map((option) => option.id));
158
+ if (results.default_sort !== undefined && !sortOptionIds.has(results.default_sort)) {
159
+ issues.push({
160
+ path: 'results.default_sort',
161
+ message: 'must reference a configured sort option',
162
+ });
163
+ }
164
+
165
+ addDuplicateIssues(
166
+ issues,
167
+ results.displays.map((display) => display.id),
168
+ (index) => `results.displays[${index}].id`,
169
+ );
170
+ const displayIds = new Set(results.displays.map((display) => display.id));
171
+ if (!displayIds.has(results.default_display)) {
172
+ issues.push({
173
+ path: 'results.default_display',
174
+ message: 'must reference a configured display',
175
+ });
176
+ }
177
+ results.displays.forEach((display, index) => {
178
+ addDisplayIssues(issues, display, index, sortOptionIds);
179
+ });
180
+ }
181
+
182
+ /**
183
+ * Validate relationships and runtime constraints that JSON Schema cannot
184
+ * express. Callers must establish structural validity first.
185
+ */
186
+ export function validateViewExperienceSemantics(
187
+ configuration: ViewExperienceConfiguration,
188
+ mode: 'draft' | 'persisted' = 'draft',
189
+ ): ViewValidationIssue[] {
190
+ const issues: ViewValidationIssue[] = [];
191
+ if (mode === 'persisted' && configuration.description?.trim().length === 0) {
192
+ issues.push({
193
+ path: 'description',
194
+ message: 'must explain the View purpose',
195
+ });
196
+ }
197
+ if (configuration.scope?.fixed_filter !== undefined) {
198
+ issues.push(...validateSupportedViewQuery(configuration.scope.fixed_filter, 'scope.fixed_filter'));
199
+ }
200
+ addNavigationIssues(issues, configuration.navigation ?? []);
201
+ addSearchIssues(issues, configuration);
202
+ addResultsIssues(issues, configuration);
203
+ return issues;
204
+ }
205
+
206
+ export function validateViewExperienceId(value: unknown): ViewValidationIssue[] {
207
+ if (typeof value !== 'string' || value.trim() === '') {
208
+ return [{ path: 'id', message: 'must be a non-empty string' }];
209
+ }
210
+ const issues: ViewValidationIssue[] = [];
211
+ if (value.length > 64) {
212
+ issues.push({ path: 'id', message: 'must contain at most 64 characters' });
213
+ }
214
+ if (!EXPERIENCE_ID_PATTERN.test(value)) {
215
+ issues.push({ path: 'id', message: 'must be a lowercase URL-safe slug' });
216
+ }
217
+ return issues;
218
+ }
219
+
220
+ export interface AppViewExperienceId {
221
+ app_name: string;
222
+ local_id: string;
223
+ }
224
+
225
+ /**
226
+ * Parse an app-contributed View id without allowing URL path separators or
227
+ * percent-encoded path material into a downstream privileged request.
228
+ */
229
+ export function parseAppViewExperienceId(value: unknown): AppViewExperienceId | undefined {
230
+ if (typeof value !== 'string') return undefined;
231
+ const match = APP_VIEW_ID_PATTERN.exec(value);
232
+ if (!match) return undefined;
233
+ return { app_name: match[1], local_id: match[2] };
234
+ }
package/src/views.ts ADDED
@@ -0,0 +1,484 @@
1
+ import type { InteractionExecutionConfiguration } from './interaction.js';
2
+ import type { ContentObjectItemApiResponse } from './store/store.js';
3
+
4
+ export const VIEW_EXPERIENCE_SCHEMA_VERSION = 1 as const;
5
+
6
+ export type ViewExperienceSchemaVersion = typeof VIEW_EXPERIENCE_SCHEMA_VERSION;
7
+
8
+ /** Build the generic reusable client route for a persisted or app-contributed View. */
9
+ export function viewExperienceRoute(id: string): string {
10
+ return `/view/${encodeURIComponent(id)}`;
11
+ }
12
+
13
+ /** An author-provided Elasticsearch query subtree validated by the View runtime. */
14
+ export interface ViewElasticsearchQuery {
15
+ [clause: string]: unknown;
16
+ }
17
+
18
+ export interface ViewExperienceLayout {
19
+ mode?: 'browse' | 'worklist';
20
+ navigation_position?: 'sidebar' | 'top';
21
+ }
22
+
23
+ export interface ViewExperienceScope {
24
+ type_ids?: string[];
25
+ locations?: string[];
26
+ collection_ids?: string[];
27
+ include_collection_descendants?: boolean;
28
+ fixed_filter?: ViewElasticsearchQuery;
29
+ head_only?: boolean;
30
+ }
31
+
32
+ export interface ViewNavigationBase {
33
+ id: string;
34
+ label: string;
35
+ presentation?: 'tree' | 'list' | 'select' | 'chips';
36
+ multi_select?: boolean;
37
+ order?: number;
38
+ renderer?: string;
39
+ }
40
+
41
+ export interface ViewLocationNavigation extends ViewNavigationBase {
42
+ source: 'location';
43
+ roots?: string[];
44
+ depth?: number;
45
+ }
46
+
47
+ export interface ViewCollectionNavigation extends ViewNavigationBase {
48
+ source: 'collection';
49
+ roots?: string[];
50
+ include_descendants?: boolean;
51
+ }
52
+
53
+ export interface ViewTermsNavigation extends ViewNavigationBase {
54
+ source: 'terms';
55
+ field: string;
56
+ size?: number;
57
+ sort?: 'count' | 'label';
58
+ }
59
+
60
+ export interface ViewHierarchyLevel {
61
+ id: string;
62
+ label: string;
63
+ field: string;
64
+ size?: number;
65
+ sort?: 'count' | 'label';
66
+ }
67
+
68
+ /**
69
+ * A drill-down hierarchy assembled from independently mapped properties.
70
+ *
71
+ * Hierarchies represent one selected path, so multi_select may only be false.
72
+ * Selection ids are opaque runtime values and must not be constructed by clients.
73
+ */
74
+ export interface ViewHierarchyNavigation extends ViewNavigationBase {
75
+ source: 'hierarchy';
76
+ levels: ViewHierarchyLevel[];
77
+ multi_select?: false;
78
+ }
79
+
80
+ export interface ViewRangeDefinition {
81
+ id: string;
82
+ label: string;
83
+ from?: number;
84
+ to?: number;
85
+ }
86
+
87
+ export interface ViewRangeNavigation extends ViewNavigationBase {
88
+ source: 'range';
89
+ field: string;
90
+ ranges: ViewRangeDefinition[];
91
+ }
92
+
93
+ export type ViewNavigationItem =
94
+ | ViewLocationNavigation
95
+ | ViewCollectionNavigation
96
+ | ViewTermsNavigation
97
+ | ViewHierarchyNavigation
98
+ | ViewRangeNavigation;
99
+
100
+ export interface ViewKeyTermDefinition {
101
+ id: string;
102
+ label: string;
103
+ field?: string;
104
+ type: ViewSearchFieldType;
105
+ multiple?: boolean;
106
+ operator?: 'match' | 'term' | 'range';
107
+ }
108
+
109
+ export const VIEW_SEARCH_FIELD_TYPES = ['text', 'keyword', 'number', 'date', 'boolean'] as const;
110
+
111
+ export type ViewSearchFieldType = (typeof VIEW_SEARCH_FIELD_TYPES)[number];
112
+
113
+ /**
114
+ * A mapped Elasticsearch field that a View may use for query planning and
115
+ * deterministic full-text fallback.
116
+ */
117
+ export interface ViewSearchFieldDefinition {
118
+ field: string;
119
+ /** Meaning of the field for query planners, for example "Full OCR text". */
120
+ description?: string;
121
+ /** Mapping hint used only when the active index mapping does not expose a type. */
122
+ type?: ViewSearchFieldType;
123
+ /**
124
+ * `full_text` enables scoring text queries, `exact` limits the field to
125
+ * structured operators, and `auto` derives behavior from the mapped type.
126
+ */
127
+ mode?: 'auto' | 'full_text' | 'exact';
128
+ /** Relative boost when this field participates in multi-field text search. */
129
+ boost?: number;
130
+ }
131
+
132
+ export interface AgenticViewSearchConfiguration {
133
+ interaction?: string;
134
+ config?: InteractionExecutionConfiguration;
135
+ /** View-specific guidance for Elasticsearch query planning. */
136
+ instructions?: string;
137
+ mode?: 'query';
138
+ timeout_ms?: number;
139
+ minimum_confidence?: number;
140
+ }
141
+
142
+ export interface ViewSearchConfiguration {
143
+ renderer?: string;
144
+ mode?: 'deterministic' | 'agentic';
145
+ placeholder?: string;
146
+ fields?: ViewSearchFieldDefinition[];
147
+ key_terms?: ViewKeyTermDefinition[];
148
+ agentic?: AgenticViewSearchConfiguration;
149
+ }
150
+
151
+ export interface ViewSortClause {
152
+ field: string;
153
+ order: 'asc' | 'desc';
154
+ }
155
+
156
+ export interface ViewSortOption {
157
+ id: string;
158
+ label: string;
159
+ sort: ViewSortClause[];
160
+ }
161
+
162
+ export const VIEW_RESULT_FIELD_FORMATS = [
163
+ 'text',
164
+ 'date',
165
+ 'number',
166
+ 'badge',
167
+ 'user',
168
+ 'content_type',
169
+ 'location',
170
+ ] as const;
171
+
172
+ export type ViewResultFieldFormat = (typeof VIEW_RESULT_FIELD_FORMATS)[number];
173
+
174
+ export interface ViewResultField {
175
+ field: string;
176
+ label?: string;
177
+ format?: ViewResultFieldFormat;
178
+ fallback?: string;
179
+ }
180
+
181
+ export interface ViewResultMedia {
182
+ source: 'content_thumbnail' | 'property' | 'type_icon';
183
+ field?: string;
184
+ fit?: 'cover' | 'contain';
185
+ fallback?: 'type_icon' | 'placeholder' | 'none';
186
+ }
187
+
188
+ export interface ViewDisplayBase {
189
+ id: string;
190
+ label: string;
191
+ renderer?: string;
192
+ page_size?: number;
193
+ }
194
+
195
+ export interface ViewListDisplay extends ViewDisplayBase {
196
+ type: 'list';
197
+ title: ViewResultField;
198
+ subtitle?: ViewResultField[];
199
+ description?: ViewResultField;
200
+ media?: ViewResultMedia;
201
+ badges?: ViewResultField[];
202
+ }
203
+
204
+ export interface ViewTableColumn extends ViewResultField {
205
+ width?: number;
206
+ sortable?: boolean;
207
+ sort_option?: string;
208
+ }
209
+
210
+ export interface ViewTableDisplay extends ViewDisplayBase {
211
+ type: 'table';
212
+ columns: ViewTableColumn[];
213
+ }
214
+
215
+ export interface ViewCardsDisplay extends ViewDisplayBase {
216
+ type: 'cards';
217
+ title: ViewResultField;
218
+ description?: ViewResultField;
219
+ media?: ViewResultMedia;
220
+ fields?: ViewResultField[];
221
+ badges?: ViewResultField[];
222
+ columns?: 2 | 3 | 4 | 5 | 6;
223
+ }
224
+
225
+ export interface ViewGalleryDisplay extends ViewDisplayBase {
226
+ type: 'gallery';
227
+ media: ViewResultMedia;
228
+ title: ViewResultField;
229
+ caption?: ViewResultField[];
230
+ columns?: 2 | 3 | 4 | 5 | 6;
231
+ }
232
+
233
+ export interface ViewBoardColumn {
234
+ value: string;
235
+ label: string;
236
+ order?: number;
237
+ }
238
+
239
+ export interface ViewBoardCardConfiguration {
240
+ title: ViewResultField;
241
+ description?: ViewResultField;
242
+ media?: ViewResultMedia;
243
+ fields?: ViewResultField[];
244
+ badges?: ViewResultField[];
245
+ }
246
+
247
+ export interface ViewBoardDisplay extends ViewDisplayBase {
248
+ type: 'board';
249
+ group_by: string;
250
+ columns?: ViewBoardColumn[];
251
+ card: ViewBoardCardConfiguration;
252
+ }
253
+
254
+ export type ViewDisplayConfiguration =
255
+ | ViewListDisplay
256
+ | ViewTableDisplay
257
+ | ViewCardsDisplay
258
+ | ViewGalleryDisplay
259
+ | ViewBoardDisplay;
260
+
261
+ export interface ViewResultsConfiguration {
262
+ default_display: string;
263
+ allow_display_switch?: boolean;
264
+ displays: ViewDisplayConfiguration[];
265
+ default_sort?: string;
266
+ sort_options?: ViewSortOption[];
267
+ }
268
+
269
+ export interface ViewExperienceConfiguration {
270
+ name: string;
271
+ description?: string;
272
+ enabled?: boolean;
273
+ layout?: ViewExperienceLayout;
274
+ scope?: ViewExperienceScope;
275
+ navigation?: ViewNavigationItem[];
276
+ search?: ViewSearchConfiguration;
277
+ results?: ViewResultsConfiguration;
278
+ }
279
+
280
+ /**
281
+ * A View configuration stored as a project resource.
282
+ *
283
+ * Persisted Views require documentation because they are reusable, discoverable
284
+ * resources surfaced in Studio and to Studio Assistant.
285
+ */
286
+ export interface PersistedViewExperienceConfiguration extends Omit<ViewExperienceConfiguration, 'description'> {
287
+ description: string;
288
+ }
289
+
290
+ /**
291
+ * Project a persisted or extended View value back to its reusable configuration
292
+ * fields. Callers that require a persisted configuration must validate the
293
+ * result at their API or persistence boundary because legacy records may not
294
+ * satisfy newer persisted-only requirements.
295
+ */
296
+ export function getViewExperienceConfiguration(value: ViewExperienceConfiguration): ViewExperienceConfiguration {
297
+ return {
298
+ name: value.name,
299
+ ...(value.description === undefined ? {} : { description: value.description }),
300
+ ...(value.enabled === undefined ? {} : { enabled: value.enabled }),
301
+ ...(value.layout === undefined ? {} : { layout: value.layout }),
302
+ ...(value.scope === undefined ? {} : { scope: value.scope }),
303
+ ...(value.navigation === undefined ? {} : { navigation: value.navigation }),
304
+ ...(value.search === undefined ? {} : { search: value.search }),
305
+ ...(value.results === undefined ? {} : { results: value.results }),
306
+ };
307
+ }
308
+
309
+ /** Project a type-valid persisted View while preserving its required documentation field. */
310
+ export function getPersistedViewExperienceConfiguration(
311
+ value: PersistedViewExperienceConfiguration,
312
+ ): PersistedViewExperienceConfiguration {
313
+ return {
314
+ ...getViewExperienceConfiguration(value),
315
+ description: value.description,
316
+ };
317
+ }
318
+
319
+ /** A View definition contributed by application code through the app package endpoint. */
320
+ export interface InCodeViewDefinition {
321
+ /** App-local id. Studio normalizes it to app:<app-name>:<id>. */
322
+ id: string;
323
+ /** App-local name used for lookup and diagnostics. */
324
+ name: string;
325
+ title?: string;
326
+ description?: string;
327
+ tags?: string[];
328
+ definition: ViewExperienceConfiguration;
329
+ }
330
+
331
+ export interface ViewExperience extends PersistedViewExperienceConfiguration {
332
+ id: string;
333
+ version: ViewExperienceSchemaVersion;
334
+ revision: number;
335
+ created_by: string;
336
+ updated_by: string;
337
+ created_at: string;
338
+ updated_at: string;
339
+ }
340
+
341
+ export interface CreateViewExperienceRequest extends PersistedViewExperienceConfiguration {
342
+ id: string;
343
+ version?: ViewExperienceSchemaVersion;
344
+ }
345
+
346
+ /** PUT uses full replacement so omitted optional configuration is removed. */
347
+ export interface UpdateViewExperienceRequest extends PersistedViewExperienceConfiguration {
348
+ version: ViewExperienceSchemaVersion;
349
+ revision: number;
350
+ }
351
+
352
+ export interface ViewExperienceListQuery {
353
+ limit?: number;
354
+ offset?: number;
355
+ }
356
+
357
+ export interface ExecuteViewRequest {
358
+ query?: string;
359
+ key_terms?: Record<string, string[]>;
360
+ navigation?: Record<string, string[]>;
361
+ display?: string;
362
+ sort?: string;
363
+ offset?: number;
364
+ limit?: number;
365
+ }
366
+
367
+ /**
368
+ * Execute an unsaved (draft) View configuration without persisting it. Combines
369
+ * the inline configuration with the same execution inputs as {@link ExecuteViewRequest}
370
+ * so authors can validate and preview results before calling create/update.
371
+ */
372
+ export interface PreviewViewExperienceRequest extends ExecuteViewRequest {
373
+ /** The unsaved View configuration to validate and execute. */
374
+ configuration: ViewExperienceConfiguration;
375
+ }
376
+
377
+ export interface ViewExecutionWarning {
378
+ code: string;
379
+ message: string;
380
+ path?: string;
381
+ }
382
+
383
+ export type ViewQueryPlanningFailureCode =
384
+ | 'interaction_failed'
385
+ | 'invalid_output'
386
+ | 'invalid_query'
387
+ | 'low_confidence'
388
+ | 'timeout'
389
+ | 'unknown';
390
+
391
+ /**
392
+ * Safe query-planning diagnostics. The query contains only the model-authored
393
+ * subtree; server-owned scope and content-security filters are never exposed.
394
+ */
395
+ export interface ViewExecutionQueryPlan {
396
+ status: 'applied' | 'fallback';
397
+ query?: ViewElasticsearchQuery;
398
+ confidence?: number;
399
+ error_code?: ViewQueryPlanningFailureCode;
400
+ error_message?: string;
401
+ }
402
+
403
+ export interface ViewHitAnnotation {
404
+ why_match?: string;
405
+ answer?: string;
406
+ excerpt?: string;
407
+ }
408
+
409
+ export interface ViewHit {
410
+ id: string;
411
+ score?: number;
412
+ document: ContentObjectItemApiResponse;
413
+ annotation?: ViewHitAnnotation;
414
+ }
415
+
416
+ export interface ViewNavigationNode {
417
+ id: string;
418
+ label: string;
419
+ count: number;
420
+ selected?: boolean;
421
+ expandable?: boolean;
422
+ children?: ViewNavigationNode[];
423
+ path?: string;
424
+ }
425
+
426
+ export interface ViewNavigationResult {
427
+ id: string;
428
+ selected: string[];
429
+ nodes: ViewNavigationNode[];
430
+ /** Selected hierarchy path from its root through the current value. */
431
+ breadcrumbs?: ViewNavigationNode[];
432
+ truncated?: boolean;
433
+ }
434
+
435
+ export interface ViewExecutionSearchResult {
436
+ input?: string;
437
+ interpretation?: string;
438
+ key_terms?: Record<string, string[]>;
439
+ plan?: ViewExecutionQueryPlan;
440
+ requested_mode: 'browse' | 'deterministic' | 'agentic';
441
+ applied_mode: 'browse' | 'deterministic' | 'query';
442
+ fallback_reason?: string;
443
+ warnings: ViewExecutionWarning[];
444
+ }
445
+
446
+ /**
447
+ * Client-visible search controls. Agentic planner instructions, interaction,
448
+ * and model configuration are intentionally omitted.
449
+ */
450
+ export interface ViewExecutionSearchConfiguration {
451
+ renderer?: string;
452
+ mode?: 'deterministic' | 'agentic';
453
+ placeholder?: string;
454
+ fields?: ViewSearchFieldDefinition[];
455
+ key_terms?: ViewKeyTermDefinition[];
456
+ }
457
+
458
+ /**
459
+ * The reusable, client-visible part of the View definition used for an execution.
460
+ * Server-owned scope is intentionally omitted.
461
+ */
462
+ export interface ViewExecutionDefinition {
463
+ name: string;
464
+ description?: string;
465
+ enabled?: boolean;
466
+ layout?: ViewExperienceLayout;
467
+ navigation?: ViewNavigationItem[];
468
+ search?: ViewExecutionSearchConfiguration;
469
+ results?: ViewResultsConfiguration;
470
+ }
471
+
472
+ export interface ViewExecutionResult {
473
+ view: string;
474
+ revision: number;
475
+ /** The runtime-safe rendering definition resolved by Zeno for this execution. */
476
+ definition: ViewExecutionDefinition;
477
+ display?: string;
478
+ sort?: string;
479
+ search: ViewExecutionSearchResult;
480
+ hits: ViewHit[];
481
+ total: number;
482
+ navigation: Record<string, ViewNavigationResult>;
483
+ took: number;
484
+ }