@vertesia/common 1.5.0-dev.20260714.072725Z → 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.
- package/lib/apikey.d.ts +1 -0
- package/lib/apikey.d.ts.map +1 -1
- package/lib/apikey.js.map +1 -1
- package/lib/apps.d.ts +500 -34
- package/lib/apps.d.ts.map +1 -1
- package/lib/apps.js +63 -70
- package/lib/apps.js.map +1 -1
- package/lib/audit-trail.d.ts +61 -1
- package/lib/audit-trail.d.ts.map +1 -1
- package/lib/audit-trail.js +15 -0
- package/lib/audit-trail.js.map +1 -1
- package/lib/data-platform.d.ts +121 -5
- package/lib/data-platform.d.ts.map +1 -1
- package/lib/index.d.ts +7 -0
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +7 -0
- package/lib/index.js.map +1 -1
- package/lib/interaction.d.ts +19 -1
- package/lib/interaction.d.ts.map +1 -1
- package/lib/interaction.js.map +1 -1
- package/lib/json-schema.d.ts +1 -1
- package/lib/json-schema.d.ts.map +1 -1
- package/lib/platform-event.d.ts +79 -3
- package/lib/platform-event.d.ts.map +1 -1
- package/lib/platform-event.js.map +1 -1
- package/lib/project.d.ts +119 -20
- package/lib/project.d.ts.map +1 -1
- package/lib/project.js +65 -0
- package/lib/project.js.map +1 -1
- package/lib/query.d.ts +6 -0
- package/lib/query.d.ts.map +1 -1
- package/lib/refs.d.ts +1 -0
- package/lib/refs.d.ts.map +1 -1
- package/lib/schema-for-extraction.d.ts +21 -0
- package/lib/schema-for-extraction.d.ts.map +1 -0
- package/lib/schema-for-extraction.js +205 -0
- package/lib/schema-for-extraction.js.map +1 -0
- package/lib/store/agent-run.d.ts +2 -0
- package/lib/store/agent-run.d.ts.map +1 -1
- package/lib/store/conversation-state.d.ts +39 -0
- package/lib/store/conversation-state.d.ts.map +1 -1
- package/lib/store/conversation-state.js +3 -0
- package/lib/store/conversation-state.js.map +1 -1
- package/lib/store/doc-analyzer.d.ts +10 -67
- package/lib/store/doc-analyzer.d.ts.map +1 -1
- package/lib/store/dsl-workflow.d.ts +1 -0
- package/lib/store/dsl-workflow.d.ts.map +1 -1
- package/lib/store/dsl-workflow.js.map +1 -1
- package/lib/store/grounded-extraction.d.ts +146 -0
- package/lib/store/grounded-extraction.d.ts.map +1 -0
- package/lib/store/grounded-extraction.js +8 -0
- package/lib/store/grounded-extraction.js.map +1 -0
- package/lib/store/index.d.ts +1 -0
- package/lib/store/index.d.ts.map +1 -1
- package/lib/store/index.js +1 -0
- package/lib/store/index.js.map +1 -1
- package/lib/store/store.d.ts +307 -1
- package/lib/store/store.d.ts.map +1 -1
- package/lib/store/store.js +438 -0
- package/lib/store/store.js.map +1 -1
- package/lib/store/workflow.d.ts +3 -0
- package/lib/store/workflow.d.ts.map +1 -1
- package/lib/store/workflow.js.map +1 -1
- package/lib/user.d.ts +14 -0
- package/lib/user.d.ts.map +1 -1
- package/lib/user.js +31 -0
- package/lib/user.js.map +1 -1
- package/lib/vertesia-common.js +2 -2
- package/lib/vertesia-common.js.map +1 -1
- package/lib/view-configuration-validation.d.ts +15 -0
- package/lib/view-configuration-validation.d.ts.map +1 -0
- package/lib/view-configuration-validation.js +63 -0
- package/lib/view-configuration-validation.js.map +1 -0
- package/lib/view-query-validation.d.ts +13 -0
- package/lib/view-query-validation.d.ts.map +1 -0
- package/lib/view-query-validation.js +266 -0
- package/lib/view-query-validation.js.map +1 -0
- package/lib/view-validation-helpers.d.ts +15 -0
- package/lib/view-validation-helpers.d.ts.map +1 -0
- package/lib/view-validation-helpers.js +25 -0
- package/lib/view-validation-helpers.js.map +1 -0
- package/lib/views-schema.d.ts +1992 -0
- package/lib/views-schema.d.ts.map +1 -0
- package/lib/views-schema.js +674 -0
- package/lib/views-schema.js.map +1 -0
- package/lib/views-validation.d.ts +21 -0
- package/lib/views-validation.d.ts.map +1 -0
- package/lib/views-validation.js +164 -0
- package/lib/views-validation.js.map +1 -0
- package/lib/views.d.ts +381 -0
- package/lib/views.d.ts.map +1 -0
- package/lib/views.js +41 -0
- package/lib/views.js.map +1 -0
- package/package.json +5 -4
- package/src/apikey.ts +1 -0
- package/src/apps.test.ts +9 -1
- package/src/apps.ts +583 -90
- package/src/audit-trail.ts +83 -0
- package/src/data-platform.ts +129 -5
- package/src/index.ts +12 -0
- package/src/interaction.ts +20 -1
- package/src/json-schema.ts +0 -1
- package/src/platform-event.ts +92 -2
- package/src/project.test.ts +44 -0
- package/src/project.ts +205 -22
- package/src/query.ts +6 -0
- package/src/refs.ts +1 -0
- package/src/roles.test.ts +32 -0
- package/src/schema-for-extraction.test.ts +191 -0
- package/src/schema-for-extraction.ts +231 -0
- package/src/store/agent-run.ts +2 -0
- package/src/store/conversation-state.ts +47 -0
- package/src/store/doc-analyzer.ts +10 -76
- package/src/store/dsl-workflow.ts +1 -0
- package/src/store/grounded-extraction.ts +154 -0
- package/src/store/index.ts +1 -0
- package/src/store/store.ts +778 -1
- package/src/store/workflow.ts +3 -0
- package/src/user.ts +46 -0
- package/src/view-configuration-validation.ts +74 -0
- package/src/view-query-validation.test.ts +21 -0
- package/src/view-query-validation.ts +319 -0
- package/src/view-validation-helpers.ts +28 -0
- package/src/views-schema.test.ts +364 -0
- package/src/views-schema.ts +689 -0
- package/src/views-validation.ts +234 -0
- package/src/views.ts +484 -0
package/src/project.ts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import type { JSONSchemaType } from 'ajv';
|
|
2
2
|
import type { SupportedIntegrations } from './integrations.js';
|
|
3
|
+
import type { ContentTypeIntakePolicy, IntakeVisionDetail, IntakeVisionProfileSettings } from './store/store.js';
|
|
3
4
|
import type { WorkflowRunStatus } from './store/workflow.js';
|
|
4
5
|
import type { AccountRef } from './user.js';
|
|
6
|
+
import { ELASTICSEARCH_FIELD_PATH_PATTERN } from './view-validation-helpers.js';
|
|
5
7
|
|
|
6
8
|
export interface ICreateProjectPayload {
|
|
7
9
|
name: string;
|
|
@@ -125,6 +127,7 @@ export const SYSTEM_INTERACTION_CATEGORIES: Record<string, SystemInteractionCate
|
|
|
125
127
|
Mediator: SystemInteractionCategory.non_applicable,
|
|
126
128
|
AnalyzeConversation: SystemInteractionCategory.analysis,
|
|
127
129
|
GetAgentConversationTopic: SystemInteractionCategory.analysis,
|
|
130
|
+
ContentSearchAgent: SystemInteractionCategory.analysis,
|
|
128
131
|
StudioAssistant: SystemInteractionCategory.agent,
|
|
129
132
|
};
|
|
130
133
|
|
|
@@ -269,7 +272,70 @@ export const BrowserUseProjectConfigurationSchema: JSONSchemaType<BrowserUseProj
|
|
|
269
272
|
export type ProjectSearchTier = 'standard' | 'performance';
|
|
270
273
|
export type ElasticsearchBackend = 'serverless' | 'hosted';
|
|
271
274
|
|
|
275
|
+
/**
|
|
276
|
+
* Fast pre-conversion type identification (the "sniff") for untyped documents.
|
|
277
|
+
* The sniff classifies a document from cheap local evidence (first/last page text,
|
|
278
|
+
* a low-res first-page image, office docProps) BEFORE any conversion, so a
|
|
279
|
+
* high-confidence match can apply the type's intake policy — including skipping
|
|
280
|
+
* conversion — without paying for it first.
|
|
281
|
+
*/
|
|
282
|
+
export interface ProjectIntakeSniffConfiguration {
|
|
283
|
+
/**
|
|
284
|
+
* Enable the pre-conversion sniff for untyped documents. Defaults to true.
|
|
285
|
+
* Can be overridden per run with the `sniffEnabled` workflow var.
|
|
286
|
+
*/
|
|
287
|
+
enabled?: boolean;
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* Confidence at or above which the sniffed type is committed and its full policy applied
|
|
291
|
+
* (including conversion-skip). 0..1, defaults to 0.85.
|
|
292
|
+
*/
|
|
293
|
+
high_confidence?: number;
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* Confidence at or above which the sniffed type is treated as provisional: the document
|
|
297
|
+
* still converts and the post-conversion selector confirms on neutral evidence.
|
|
298
|
+
* 0..1, defaults to 0.6. Below this the sniff result is advisory provenance only.
|
|
299
|
+
*/
|
|
300
|
+
medium_confidence?: number;
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
* Minimum page count for the sniff LLM call. Below this, conversion is cheap and full
|
|
304
|
+
* converted text is better selection evidence, so intake uses the standard
|
|
305
|
+
* convert-then-select path. Documents with unknown page counts are sniffed.
|
|
306
|
+
* Defaults to 5; 0 means always sniff.
|
|
307
|
+
*/
|
|
308
|
+
min_pages?: number;
|
|
309
|
+
}
|
|
310
|
+
|
|
272
311
|
export interface ProjectIntakeConfiguration {
|
|
312
|
+
/**
|
|
313
|
+
* Master switch for the standard intake pipeline. When false, StandardIntake exits as a
|
|
314
|
+
* no-op WITHOUT touching object status (objects stay in `created`, identifiable as
|
|
315
|
+
* unprocessed). Defaults to true.
|
|
316
|
+
*/
|
|
317
|
+
enabled?: boolean;
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* Fast pre-conversion type identification for untyped documents. Absent means enabled
|
|
321
|
+
* with platform default thresholds.
|
|
322
|
+
*/
|
|
323
|
+
sniff?: ProjectIntakeSniffConfiguration;
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* Project-level intake policy defaults. Same shape as the per-content-type policy; a
|
|
327
|
+
* type's `intake` block wins field-by-field over these defaults, which in turn win over
|
|
328
|
+
* the legacy flat fields below. `identification` is type-specific and ignored here.
|
|
329
|
+
*/
|
|
330
|
+
default_policy?: ContentTypeIntakePolicy;
|
|
331
|
+
|
|
332
|
+
/**
|
|
333
|
+
* Project overrides for the platform vision detail profiles used by intake visual
|
|
334
|
+
* extraction (`low`/`standard`/`high`). Partial: omitted profiles or fields inherit the
|
|
335
|
+
* platform defaults. Types reference detail NAMES only; the profile settings live here.
|
|
336
|
+
*/
|
|
337
|
+
vision_profiles?: Partial<Record<IntakeVisionDetail, Partial<IntakeVisionProfileSettings>>>;
|
|
338
|
+
|
|
273
339
|
/**
|
|
274
340
|
* Generate table-of-content sections during standard document intake.
|
|
275
341
|
* Defaults to false.
|
|
@@ -331,28 +397,7 @@ export interface ProjectConfiguration {
|
|
|
331
397
|
* Indexing configuration for this project.
|
|
332
398
|
* Controls whether indexing and querying are enabled at the project level.
|
|
333
399
|
*/
|
|
334
|
-
indexing?:
|
|
335
|
-
/**
|
|
336
|
-
* Enable indexing for content objects in this project.
|
|
337
|
-
* When enabled, content changes trigger indexing workflows.
|
|
338
|
-
* Defaults to true - indexing is always on when ES infrastructure is available.
|
|
339
|
-
*/
|
|
340
|
-
enabled?: boolean;
|
|
341
|
-
|
|
342
|
-
/**
|
|
343
|
-
* Search tier for this project.
|
|
344
|
-
* standard uses the regional hosted Elasticsearch deployment.
|
|
345
|
-
* performance uses the regional serverless Elasticsearch project.
|
|
346
|
-
* Defaults to standard when omitted.
|
|
347
|
-
*/
|
|
348
|
-
search_tier?: ProjectSearchTier;
|
|
349
|
-
|
|
350
|
-
/**
|
|
351
|
-
* Elasticsearch backend override for this project.
|
|
352
|
-
* Prefer search_tier for project configuration unless an explicit backend override is needed.
|
|
353
|
-
*/
|
|
354
|
-
backend?: ElasticsearchBackend;
|
|
355
|
-
};
|
|
400
|
+
indexing?: ProjectIndexingConfiguration;
|
|
356
401
|
|
|
357
402
|
/**
|
|
358
403
|
* Standard content intake behavior.
|
|
@@ -382,6 +427,142 @@ export interface ProjectConfiguration {
|
|
|
382
427
|
pdf_template_object_id?: string;
|
|
383
428
|
}
|
|
384
429
|
|
|
430
|
+
/**
|
|
431
|
+
* Elasticsearch field types that may be explicitly assigned to content-object
|
|
432
|
+
* properties. Paths are relative to the object's `properties` field.
|
|
433
|
+
*/
|
|
434
|
+
export type ProjectSearchPropertyType = 'keyword' | 'text' | 'boolean' | 'long' | 'double' | 'date';
|
|
435
|
+
|
|
436
|
+
/**
|
|
437
|
+
* Explicit search mapping for one content-object property.
|
|
438
|
+
*
|
|
439
|
+
* Changing a mapping requires a full reindex. Existing Elasticsearch fields
|
|
440
|
+
* cannot change type in place.
|
|
441
|
+
*/
|
|
442
|
+
export interface ProjectSearchPropertyMapping {
|
|
443
|
+
type: ProjectSearchPropertyType;
|
|
444
|
+
|
|
445
|
+
/** Elasticsearch date format. Valid only when type is `date`. */
|
|
446
|
+
format?: string;
|
|
447
|
+
|
|
448
|
+
/** Maximum indexed string length. Valid only when type is `keyword`. */
|
|
449
|
+
ignore_above?: number;
|
|
450
|
+
|
|
451
|
+
/**
|
|
452
|
+
* Skip malformed values instead of rejecting the whole document. Valid only
|
|
453
|
+
* for long, double, and date mappings.
|
|
454
|
+
*/
|
|
455
|
+
ignore_malformed?: boolean;
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
export const PROJECT_SEARCH_PROPERTY_TYPES: readonly ProjectSearchPropertyType[] = [
|
|
459
|
+
'keyword',
|
|
460
|
+
'text',
|
|
461
|
+
'boolean',
|
|
462
|
+
'long',
|
|
463
|
+
'double',
|
|
464
|
+
'date',
|
|
465
|
+
];
|
|
466
|
+
|
|
467
|
+
const MAX_PROJECT_SEARCH_PROPERTY_MAPPINGS = 200;
|
|
468
|
+
const MAX_KEYWORD_IGNORE_ABOVE = 8191;
|
|
469
|
+
|
|
470
|
+
/**
|
|
471
|
+
* Validate property mappings at API and index-creation boundaries.
|
|
472
|
+
*
|
|
473
|
+
* Returns user-facing issue strings instead of throwing so callers can map the
|
|
474
|
+
* result to the error type appropriate for their boundary.
|
|
475
|
+
*/
|
|
476
|
+
export function validateProjectSearchPropertyMappings(value: unknown): string[] {
|
|
477
|
+
if (value === undefined) return [];
|
|
478
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
479
|
+
return ['indexing.property_mappings must be an object keyed by property path'];
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
const entries = Object.entries(value as Record<string, unknown>);
|
|
483
|
+
const issues: string[] = [];
|
|
484
|
+
if (entries.length > MAX_PROJECT_SEARCH_PROPERTY_MAPPINGS) {
|
|
485
|
+
issues.push(`indexing.property_mappings must contain at most ${MAX_PROJECT_SEARCH_PROPERTY_MAPPINGS} fields`);
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
const supportedTypes = new Set<string>(PROJECT_SEARCH_PROPERTY_TYPES);
|
|
489
|
+
for (const [path, rawMapping] of entries) {
|
|
490
|
+
const field = `indexing.property_mappings.${path}`;
|
|
491
|
+
if (!ELASTICSEARCH_FIELD_PATH_PATTERN.test(path)) {
|
|
492
|
+
issues.push(`${field} must be a dot-separated path containing only letters, numbers, and underscores`);
|
|
493
|
+
}
|
|
494
|
+
if (!rawMapping || typeof rawMapping !== 'object' || Array.isArray(rawMapping)) {
|
|
495
|
+
issues.push(`${field} must be an object`);
|
|
496
|
+
continue;
|
|
497
|
+
}
|
|
498
|
+
const mapping = rawMapping as Record<string, unknown>;
|
|
499
|
+
const extraKeys = Object.keys(mapping).filter(
|
|
500
|
+
(key) => !['type', 'format', 'ignore_above', 'ignore_malformed'].includes(key),
|
|
501
|
+
);
|
|
502
|
+
if (extraKeys.length > 0) {
|
|
503
|
+
issues.push(`${field} contains unsupported option(s): ${extraKeys.join(', ')}`);
|
|
504
|
+
}
|
|
505
|
+
if (typeof mapping.type !== 'string' || !supportedTypes.has(mapping.type)) {
|
|
506
|
+
issues.push(`${field}.type must be one of: ${PROJECT_SEARCH_PROPERTY_TYPES.join(', ')}`);
|
|
507
|
+
}
|
|
508
|
+
if (mapping.format !== undefined && (mapping.type !== 'date' || typeof mapping.format !== 'string')) {
|
|
509
|
+
issues.push(`${field}.format is supported only for date mappings`);
|
|
510
|
+
}
|
|
511
|
+
if (
|
|
512
|
+
mapping.ignore_above !== undefined &&
|
|
513
|
+
(mapping.type !== 'keyword' ||
|
|
514
|
+
!Number.isInteger(mapping.ignore_above) ||
|
|
515
|
+
(mapping.ignore_above as number) < 1 ||
|
|
516
|
+
(mapping.ignore_above as number) > MAX_KEYWORD_IGNORE_ABOVE)
|
|
517
|
+
) {
|
|
518
|
+
issues.push(
|
|
519
|
+
`${field}.ignore_above is supported only for keyword mappings and must be an integer from 1 to ${MAX_KEYWORD_IGNORE_ABOVE}`,
|
|
520
|
+
);
|
|
521
|
+
}
|
|
522
|
+
if (
|
|
523
|
+
mapping.ignore_malformed !== undefined &&
|
|
524
|
+
(!['long', 'double', 'date'].includes(String(mapping.type)) ||
|
|
525
|
+
typeof mapping.ignore_malformed !== 'boolean')
|
|
526
|
+
) {
|
|
527
|
+
issues.push(`${field}.ignore_malformed is supported only for long, double, and date mappings`);
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
return issues;
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
export interface ProjectIndexingConfiguration {
|
|
534
|
+
/**
|
|
535
|
+
* Enable indexing for content objects in this project.
|
|
536
|
+
* When enabled, content changes trigger indexing workflows.
|
|
537
|
+
* Defaults to true - indexing is always on when ES infrastructure is available.
|
|
538
|
+
*/
|
|
539
|
+
enabled?: boolean;
|
|
540
|
+
|
|
541
|
+
/**
|
|
542
|
+
* Search tier for this project.
|
|
543
|
+
* standard uses the regional hosted Elasticsearch deployment.
|
|
544
|
+
* performance uses the regional serverless Elasticsearch project.
|
|
545
|
+
* Defaults to standard when omitted.
|
|
546
|
+
*/
|
|
547
|
+
search_tier?: ProjectSearchTier;
|
|
548
|
+
|
|
549
|
+
/**
|
|
550
|
+
* Elasticsearch backend override for this project.
|
|
551
|
+
* Prefer search_tier for project configuration unless an explicit backend override is needed.
|
|
552
|
+
*/
|
|
553
|
+
backend?: ElasticsearchBackend;
|
|
554
|
+
|
|
555
|
+
/**
|
|
556
|
+
* Explicit mappings for selected content-object property paths.
|
|
557
|
+
*
|
|
558
|
+
* Keys are dot-separated paths relative to `properties`, for example
|
|
559
|
+
* `order_total` or `customer.account_number`. Unlisted fields are mapped
|
|
560
|
+
* dynamically from their JSON values. Changing this value requires a full
|
|
561
|
+
* reindex.
|
|
562
|
+
*/
|
|
563
|
+
property_mappings?: Record<string, ProjectSearchPropertyMapping>;
|
|
564
|
+
}
|
|
565
|
+
|
|
385
566
|
// export interface ProjectConfigurationEmbeddings {
|
|
386
567
|
// environment: string;
|
|
387
568
|
// max_tokens: number;
|
|
@@ -937,6 +1118,8 @@ export interface IndexConfiguration {
|
|
|
937
1118
|
};
|
|
938
1119
|
/** ISO 639-1 language code for text analysis */
|
|
939
1120
|
language?: string;
|
|
1121
|
+
/** Explicit mappings for selected content-object property paths. */
|
|
1122
|
+
property_mappings?: Record<string, ProjectSearchPropertyMapping>;
|
|
940
1123
|
field_mappings?: Record<string, unknown>;
|
|
941
1124
|
project_embeddings_config?: {
|
|
942
1125
|
text?: EmbeddingTypeConfig;
|
package/src/query.ts
CHANGED
|
@@ -88,6 +88,12 @@ export interface RunSearchQuery extends SimpleSearchQuery {
|
|
|
88
88
|
model?: string;
|
|
89
89
|
status?: ExecutionRunStatus;
|
|
90
90
|
tags?: string[];
|
|
91
|
+
/**
|
|
92
|
+
* Tags to exclude. Runs carrying any of these tags are filtered out of the results,
|
|
93
|
+
* counts, and facet buckets. Combined with `tags` (which requires all of the listed
|
|
94
|
+
* tags) as an additional `$nin` constraint on the same field.
|
|
95
|
+
*/
|
|
96
|
+
exclude_tags?: string[];
|
|
91
97
|
query?: string;
|
|
92
98
|
default_query_path?: string;
|
|
93
99
|
parent?: string[];
|
package/src/refs.ts
CHANGED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { describe, expect, test } from 'vitest';
|
|
2
|
+
import { SystemRoles } from './project.js';
|
|
3
|
+
import { AbacScopes } from './roles/types.js';
|
|
4
|
+
|
|
5
|
+
describe('shared role vocabulary', () => {
|
|
6
|
+
test('exports all system roles', () => {
|
|
7
|
+
expect(Object.values(SystemRoles).sort()).toEqual(
|
|
8
|
+
[
|
|
9
|
+
'admin',
|
|
10
|
+
'app_member',
|
|
11
|
+
'application',
|
|
12
|
+
'auditor',
|
|
13
|
+
'automation',
|
|
14
|
+
'billing',
|
|
15
|
+
'consumer',
|
|
16
|
+
'content_processor',
|
|
17
|
+
'content_superadmin',
|
|
18
|
+
'developer',
|
|
19
|
+
'executor',
|
|
20
|
+
'manager',
|
|
21
|
+
'member',
|
|
22
|
+
'owner',
|
|
23
|
+
'reader',
|
|
24
|
+
'support',
|
|
25
|
+
].sort(),
|
|
26
|
+
);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
test('exports ABAC scopes used by role wire types', () => {
|
|
30
|
+
expect(AbacScopes).toEqual(['document', 'collection', 'task']);
|
|
31
|
+
});
|
|
32
|
+
});
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import {
|
|
3
|
+
isExtractableSchemaNode,
|
|
4
|
+
mergePreservingNonExtractable,
|
|
5
|
+
schemaForExtraction,
|
|
6
|
+
} from './schema-for-extraction.js';
|
|
7
|
+
|
|
8
|
+
describe('schemaForExtraction', () => {
|
|
9
|
+
it('drops top-level properties marked x-extract: false and cleans required', () => {
|
|
10
|
+
const schema = {
|
|
11
|
+
type: 'object',
|
|
12
|
+
properties: {
|
|
13
|
+
po_number: { type: 'string' },
|
|
14
|
+
match: { type: 'string', 'x-extract': false },
|
|
15
|
+
match_reason: { type: 'string', 'x-extract': false },
|
|
16
|
+
},
|
|
17
|
+
required: ['po_number', 'match'],
|
|
18
|
+
};
|
|
19
|
+
const filtered = schemaForExtraction(schema);
|
|
20
|
+
expect(Object.keys(filtered.properties as object).sort()).toEqual(['po_number']);
|
|
21
|
+
expect(filtered.required).toEqual(['po_number']);
|
|
22
|
+
expect((filtered.properties as Record<string, unknown>).po_number).toEqual({ type: 'string' });
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it('drops nested and array-item properties with x-extract: false', () => {
|
|
26
|
+
const schema = {
|
|
27
|
+
type: 'object',
|
|
28
|
+
properties: {
|
|
29
|
+
line_items: {
|
|
30
|
+
type: 'array',
|
|
31
|
+
items: {
|
|
32
|
+
type: 'object',
|
|
33
|
+
properties: {
|
|
34
|
+
part_number: { type: 'string' },
|
|
35
|
+
match: { type: 'string', 'x-extract': false },
|
|
36
|
+
quantity: { type: 'string' },
|
|
37
|
+
},
|
|
38
|
+
required: ['part_number', 'match'],
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
const filtered = schemaForExtraction(schema);
|
|
44
|
+
const items = (filtered.properties as Record<string, Record<string, unknown>>).line_items.items as Record<
|
|
45
|
+
string,
|
|
46
|
+
unknown
|
|
47
|
+
>;
|
|
48
|
+
expect(Object.keys(items.properties as object).sort()).toEqual(['part_number', 'quantity']);
|
|
49
|
+
expect(items.required).toEqual(['part_number']);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it('treats missing x-extract as extractable', () => {
|
|
53
|
+
expect(isExtractableSchemaNode({ type: 'string' })).toBe(true);
|
|
54
|
+
expect(isExtractableSchemaNode({ type: 'string', 'x-extract': true })).toBe(true);
|
|
55
|
+
expect(isExtractableSchemaNode({ type: 'string', 'x-extract': false })).toBe(false);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it('filters $defs and drops properties that reference non-extractable definitions', () => {
|
|
59
|
+
const schema = {
|
|
60
|
+
type: 'object',
|
|
61
|
+
$defs: {
|
|
62
|
+
hidden: { type: 'string', 'x-extract': false },
|
|
63
|
+
visible: {
|
|
64
|
+
type: 'object',
|
|
65
|
+
properties: {
|
|
66
|
+
value: { type: 'string' },
|
|
67
|
+
match: { type: 'string', 'x-extract': false },
|
|
68
|
+
},
|
|
69
|
+
required: ['value', 'match'],
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
properties: {
|
|
73
|
+
hidden: { $ref: '#/$defs/hidden' },
|
|
74
|
+
visible: { $ref: '#/$defs/visible' },
|
|
75
|
+
},
|
|
76
|
+
required: ['hidden', 'visible'],
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
const filtered = schemaForExtraction(schema);
|
|
80
|
+
|
|
81
|
+
expect(filtered.$defs.hidden).toBeUndefined();
|
|
82
|
+
expect(filtered.$defs.visible).toEqual({
|
|
83
|
+
type: 'object',
|
|
84
|
+
properties: { value: { type: 'string' } },
|
|
85
|
+
required: ['value'],
|
|
86
|
+
});
|
|
87
|
+
expect(filtered.properties).toEqual({ visible: { $ref: '#/$defs/visible' } });
|
|
88
|
+
expect(filtered.required).toEqual(['visible']);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it('drops non-extractable anyOf branches', () => {
|
|
92
|
+
const schema = {
|
|
93
|
+
type: 'object',
|
|
94
|
+
properties: {
|
|
95
|
+
value: {
|
|
96
|
+
anyOf: [{ type: 'string', 'x-extract': false }, { type: 'number' }],
|
|
97
|
+
},
|
|
98
|
+
},
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
const filtered = schemaForExtraction(schema);
|
|
102
|
+
|
|
103
|
+
expect((filtered.properties.value as { anyOf: unknown[] }).anyOf).toEqual([{ type: 'number' }]);
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
describe('mergePreservingNonExtractable', () => {
|
|
108
|
+
it('preserves non-extractable top-level values over model output', () => {
|
|
109
|
+
const schema = {
|
|
110
|
+
type: 'object',
|
|
111
|
+
properties: {
|
|
112
|
+
po_number: { type: 'string' },
|
|
113
|
+
match: { type: 'string', 'x-extract': false },
|
|
114
|
+
},
|
|
115
|
+
};
|
|
116
|
+
const existing = { po_number: 'OLD', match: 'erp-hit' };
|
|
117
|
+
const extracted = { po_number: 'NEW', match: 'model-invented' };
|
|
118
|
+
expect(mergePreservingNonExtractable(existing, extracted, schema)).toEqual({
|
|
119
|
+
po_number: 'NEW',
|
|
120
|
+
match: 'erp-hit',
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
it('clears extractable fields omitted by the latest extraction', () => {
|
|
125
|
+
const schema = {
|
|
126
|
+
type: 'object',
|
|
127
|
+
properties: {
|
|
128
|
+
po_number: { type: 'string' },
|
|
129
|
+
total: { type: 'number' },
|
|
130
|
+
match: { type: 'string', 'x-extract': false },
|
|
131
|
+
},
|
|
132
|
+
};
|
|
133
|
+
const existing = { po_number: 'STALE', total: 42, match: 'erp-hit' };
|
|
134
|
+
const extracted = { total: 50 };
|
|
135
|
+
|
|
136
|
+
expect(mergePreservingNonExtractable(existing, extracted, schema)).toEqual({
|
|
137
|
+
total: 50,
|
|
138
|
+
match: 'erp-hit',
|
|
139
|
+
});
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
it('does not preserve non-extractable array item fields by index', () => {
|
|
143
|
+
const schema = {
|
|
144
|
+
type: 'object',
|
|
145
|
+
properties: {
|
|
146
|
+
line_items: {
|
|
147
|
+
type: 'array',
|
|
148
|
+
items: {
|
|
149
|
+
type: 'object',
|
|
150
|
+
properties: {
|
|
151
|
+
part: { type: 'string' },
|
|
152
|
+
match: { type: 'string', 'x-extract': false },
|
|
153
|
+
},
|
|
154
|
+
},
|
|
155
|
+
},
|
|
156
|
+
},
|
|
157
|
+
};
|
|
158
|
+
const existing = {
|
|
159
|
+
line_items: [
|
|
160
|
+
{ part: 'A', match: 'hit-a' },
|
|
161
|
+
{ part: 'B', match: 'hit-b' },
|
|
162
|
+
],
|
|
163
|
+
};
|
|
164
|
+
const extracted = {
|
|
165
|
+
line_items: [{ part: 'B2' }],
|
|
166
|
+
};
|
|
167
|
+
expect(mergePreservingNonExtractable(existing, extracted, schema)).toEqual({
|
|
168
|
+
line_items: [{ part: 'B2' }],
|
|
169
|
+
});
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
it('preserves top-level values referenced through non-extractable $defs', () => {
|
|
173
|
+
const schema = {
|
|
174
|
+
type: 'object',
|
|
175
|
+
$defs: {
|
|
176
|
+
match: { type: 'string', 'x-extract': false },
|
|
177
|
+
},
|
|
178
|
+
properties: {
|
|
179
|
+
po_number: { type: 'string' },
|
|
180
|
+
match: { $ref: '#/$defs/match' },
|
|
181
|
+
},
|
|
182
|
+
};
|
|
183
|
+
const existing = { po_number: 'OLD', match: 'erp-hit' };
|
|
184
|
+
const extracted = { po_number: 'NEW', match: 'model-invented' };
|
|
185
|
+
|
|
186
|
+
expect(mergePreservingNonExtractable(existing, extracted, schema)).toEqual({
|
|
187
|
+
po_number: 'NEW',
|
|
188
|
+
match: 'erp-hit',
|
|
189
|
+
});
|
|
190
|
+
});
|
|
191
|
+
});
|