@byline/core 6.12.0 → 6.13.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/@types/extraction-types.d.ts +423 -0
- package/dist/@types/extraction-types.js +8 -0
- package/dist/@types/index.d.ts +1 -0
- package/dist/@types/index.js +1 -0
- package/dist/extraction/artifact.d.ts +46 -0
- package/dist/extraction/artifact.js +121 -0
- package/dist/extraction/artifact.test.node.d.ts +8 -0
- package/dist/extraction/artifact.test.node.js +194 -0
- package/dist/extraction/errors.d.ts +47 -0
- package/dist/extraction/errors.js +57 -0
- package/dist/extraction/errors.test.node.d.ts +8 -0
- package/dist/extraction/errors.test.node.js +68 -0
- package/dist/extraction/evidence.d.ts +22 -0
- package/dist/extraction/evidence.js +54 -0
- package/dist/extraction/evidence.test.node.d.ts +8 -0
- package/dist/extraction/evidence.test.node.js +138 -0
- package/dist/extraction/index.d.ts +14 -0
- package/dist/extraction/index.js +17 -0
- package/dist/extraction/internal.d.ts +30 -0
- package/dist/extraction/internal.js +93 -0
- package/dist/extraction/outcomes.d.ts +20 -0
- package/dist/extraction/outcomes.js +30 -0
- package/dist/extraction/outcomes.test.node.d.ts +8 -0
- package/dist/extraction/outcomes.test.node.js +55 -0
- package/dist/extraction/page-map.d.ts +40 -0
- package/dist/extraction/page-map.js +190 -0
- package/dist/extraction/page-map.test.node.d.ts +8 -0
- package/dist/extraction/page-map.test.node.js +371 -0
- package/dist/extraction/producer.d.ts +62 -0
- package/dist/extraction/producer.js +76 -0
- package/dist/extraction/producer.test.node.d.ts +8 -0
- package/dist/extraction/producer.test.node.js +230 -0
- package/dist/extraction/root-graph-guard.test.node.d.ts +8 -0
- package/dist/extraction/root-graph-guard.test.node.js +293 -0
- package/dist/extraction/work.d.ts +55 -0
- package/dist/extraction/work.js +102 -0
- package/dist/extraction/work.test.node.d.ts +8 -0
- package/dist/extraction/work.test.node.js +409 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/storage/stored-files-server.d.ts +1 -0
- package/dist/storage/stored-files-server.js +9 -24
- package/dist/storage/stored-files-server.test.node.js +6 -0
- package/dist/storage/verified-read.d.ts +131 -0
- package/dist/storage/verified-read.js +328 -0
- package/dist/storage/verified-read.test.node.d.ts +8 -0
- package/dist/storage/verified-read.test.node.js +645 -0
- package/package.json +6 -6
|
@@ -0,0 +1,423 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This Source Code is subject to the terms of the Mozilla Public
|
|
3
|
+
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
4
|
+
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
5
|
+
*
|
|
6
|
+
* Copyright (c) Infonomic Company Limited
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* The extraction-provider seam: the contract that makes document extractors
|
|
10
|
+
* interchangeable drivers, in the same way `SearchProvider` makes search engines
|
|
11
|
+
* interchangeable.
|
|
12
|
+
*
|
|
13
|
+
* Every export in this module is **provisional**. Until the contract is declared
|
|
14
|
+
* stable, a minor release may change it, and the changelog will say so.
|
|
15
|
+
* `ExtractionArtifact.schemaVersion` and `PageMapV1.version` version the persisted
|
|
16
|
+
* shapes independently of the package version.
|
|
17
|
+
*
|
|
18
|
+
* A provider is a pure `bytes -> output` driver. It never reads Byline documents,
|
|
19
|
+
* storage, search or access state, never persists anything and never decides
|
|
20
|
+
* eligibility. Those duties belong to orchestration.
|
|
21
|
+
*/
|
|
22
|
+
import type { JsonValue } from './field-data-types.js';
|
|
23
|
+
import type { StorageValidator } from './storage-types.js';
|
|
24
|
+
/**
|
|
25
|
+
* What a driver supports. Consumers gate on it and never assume it. A provider
|
|
26
|
+
* that declares `markdown`, `structure` or `pageMap` as `false` must not emit that
|
|
27
|
+
* member of its result.
|
|
28
|
+
*
|
|
29
|
+
* @provisional
|
|
30
|
+
*/
|
|
31
|
+
export interface ExtractorCapabilities {
|
|
32
|
+
/** Recognises text on image-only pages. */
|
|
33
|
+
ocr: boolean;
|
|
34
|
+
/** Emits structure-aware markdown. */
|
|
35
|
+
markdown: boolean;
|
|
36
|
+
/** Reconstructs table structure. */
|
|
37
|
+
tables: boolean;
|
|
38
|
+
/** Recognises formulas. */
|
|
39
|
+
formulas: boolean;
|
|
40
|
+
/** Attempts figure and chart understanding. */
|
|
41
|
+
charts: boolean;
|
|
42
|
+
/** Reports block positions or reading order. */
|
|
43
|
+
coordinates: boolean;
|
|
44
|
+
/** Emits a page map version 1 from a page-aware assembly. */
|
|
45
|
+
pageMap: boolean;
|
|
46
|
+
/** Emits provider-native `structure`. */
|
|
47
|
+
structure: boolean;
|
|
48
|
+
/** Accepted MIME types; `*` + `/*` or `type/*` wildcards are allowed. */
|
|
49
|
+
formats: string[];
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Hints a driver can act on.
|
|
53
|
+
*
|
|
54
|
+
* @provisional
|
|
55
|
+
*/
|
|
56
|
+
export interface ExtractionHints {
|
|
57
|
+
/** The extension is a parser-selection hint. */
|
|
58
|
+
filename?: string;
|
|
59
|
+
/** The declared type; a parser-selection hint. */
|
|
60
|
+
mimeType?: string;
|
|
61
|
+
/** BCP 47; selects an OCR model. */
|
|
62
|
+
language?: string;
|
|
63
|
+
/** An explicit OCR language list, when it differs from `language`. */
|
|
64
|
+
ocrLanguages?: string[];
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* The name of one extraction hint.
|
|
68
|
+
*
|
|
69
|
+
* @provisional
|
|
70
|
+
*/
|
|
71
|
+
export type ExtractionHintName = keyof ExtractionHints;
|
|
72
|
+
/**
|
|
73
|
+
* One file to extract.
|
|
74
|
+
*
|
|
75
|
+
* @provisional
|
|
76
|
+
*/
|
|
77
|
+
export interface ExtractionInput extends ExtractionHints {
|
|
78
|
+
/**
|
|
79
|
+
* Verified source bytes: a web stream for large files, or a byte array. A
|
|
80
|
+
* provider consumes the stream to its end or cancels it. The caller owns any
|
|
81
|
+
* spool file behind it.
|
|
82
|
+
*/
|
|
83
|
+
data: ReadableStream<Uint8Array> | Uint8Array;
|
|
84
|
+
/** Aborts the extraction. The provider releases its resources. */
|
|
85
|
+
signal?: AbortSignal;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Document-level metadata a driver recovered from the file.
|
|
89
|
+
*
|
|
90
|
+
* @provisional
|
|
91
|
+
*/
|
|
92
|
+
export interface ExtractionMetadata {
|
|
93
|
+
/** Detected MIME type. */
|
|
94
|
+
contentType?: string;
|
|
95
|
+
/** Pages as reported by the file's metadata. Prefer `pageCount` on the result. */
|
|
96
|
+
pages?: number;
|
|
97
|
+
title?: string;
|
|
98
|
+
authors?: string[];
|
|
99
|
+
/** Declared or detected language, as embedded. */
|
|
100
|
+
language?: string;
|
|
101
|
+
/** ISO 8601. A provider that cannot normalise an embedded date omits this member. */
|
|
102
|
+
createdAt?: string;
|
|
103
|
+
/** The producing application. */
|
|
104
|
+
producer?: string;
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Cheap classification inputs for a router. They are not page boundaries:
|
|
108
|
+
* `charsPerPage` must never be used to derive page-map offsets, because it is
|
|
109
|
+
* counted over text that is normalised afterwards.
|
|
110
|
+
*
|
|
111
|
+
* @provisional
|
|
112
|
+
*/
|
|
113
|
+
export interface RoutingSignals {
|
|
114
|
+
totalChars: number;
|
|
115
|
+
charsPerPage?: number[];
|
|
116
|
+
ocrCandidatePages?: number;
|
|
117
|
+
/** Share of pages with a usable text layer, from 0 to 1. */
|
|
118
|
+
textLayerCoverage?: number;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* A page map locates each physical page of a paginated source within `plainText`.
|
|
122
|
+
* Offsets are half-open UTF-16 code-unit indices. `validatePageMap` checks the
|
|
123
|
+
* structure; only sentinel verification against a fixture proves that page 3's
|
|
124
|
+
* text came from physical page 3.
|
|
125
|
+
*
|
|
126
|
+
* @provisional
|
|
127
|
+
*/
|
|
128
|
+
export interface PageMapV1 {
|
|
129
|
+
version: 1;
|
|
130
|
+
assembly: {
|
|
131
|
+
/** Inserted between consecutive ranges. */
|
|
132
|
+
pageSeparator: string;
|
|
133
|
+
/** What follows the last page. */
|
|
134
|
+
trailing?: 'embedded' | 'none';
|
|
135
|
+
};
|
|
136
|
+
pages: Array<{
|
|
137
|
+
index: number;
|
|
138
|
+
label?: string;
|
|
139
|
+
charStart: number;
|
|
140
|
+
charEnd: number;
|
|
141
|
+
}>;
|
|
142
|
+
unanchored?: Array<{
|
|
143
|
+
kind: string;
|
|
144
|
+
charStart: number;
|
|
145
|
+
charEnd: number;
|
|
146
|
+
}>;
|
|
147
|
+
blocks?: Array<{
|
|
148
|
+
page: number;
|
|
149
|
+
charStart: number;
|
|
150
|
+
charEnd: number;
|
|
151
|
+
kind: string;
|
|
152
|
+
}>;
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* The normalised output of one extraction.
|
|
156
|
+
*
|
|
157
|
+
* @provisional
|
|
158
|
+
*/
|
|
159
|
+
export interface ExtractionResult {
|
|
160
|
+
/** Equals `ExtractionProvider.name`. */
|
|
161
|
+
provider: string;
|
|
162
|
+
/**
|
|
163
|
+
* The engine version as reported at run time. Absent when the provider cannot
|
|
164
|
+
* report it. A provider that implements `resolveVersion` reports the same value.
|
|
165
|
+
*/
|
|
166
|
+
providerVersion?: string;
|
|
167
|
+
/** Always present; `''` when nothing is extractable. */
|
|
168
|
+
plainText: string;
|
|
169
|
+
/** Only when `capabilities.markdown`. */
|
|
170
|
+
markdown?: string;
|
|
171
|
+
/** Provider-native and opaque. Only when `capabilities.structure`. */
|
|
172
|
+
structure?: JsonValue;
|
|
173
|
+
/** Only when `capabilities.pageMap`. */
|
|
174
|
+
pageMap?: PageMapV1;
|
|
175
|
+
/** Physical pages, when the format has pages. */
|
|
176
|
+
pageCount?: number;
|
|
177
|
+
metadata: ExtractionMetadata;
|
|
178
|
+
signals: RoutingSignals;
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* An extraction driver.
|
|
182
|
+
*
|
|
183
|
+
* @provisional
|
|
184
|
+
*/
|
|
185
|
+
export interface ExtractionProvider {
|
|
186
|
+
/** Stable driver identifier, recorded on every result. */
|
|
187
|
+
readonly name: string;
|
|
188
|
+
/** What the driver supports. */
|
|
189
|
+
readonly capabilities: ExtractorCapabilities;
|
|
190
|
+
/**
|
|
191
|
+
* Effective output-affecting configuration, as JSON. Included in the producer
|
|
192
|
+
* configuration hash. Omit secrets and endpoints.
|
|
193
|
+
*/
|
|
194
|
+
readonly configuration?: Readonly<Record<string, JsonValue>>;
|
|
195
|
+
/** Hints from `ExtractionHints` that change this driver's output. */
|
|
196
|
+
readonly outputAffectingHints?: readonly ExtractionHintName[];
|
|
197
|
+
/**
|
|
198
|
+
* The engine version `extract` would report now, without extracting. Rejects
|
|
199
|
+
* with an `infrastructure` error when the service cannot answer.
|
|
200
|
+
*/
|
|
201
|
+
resolveVersion?(options?: {
|
|
202
|
+
signal?: AbortSignal;
|
|
203
|
+
}): Promise<string>;
|
|
204
|
+
/** Extracts one file. Rejects with `ExtractionProviderError` on failure. */
|
|
205
|
+
extract(input: ExtractionInput): Promise<ExtractionResult>;
|
|
206
|
+
/** Boot-time health check for a remote service. */
|
|
207
|
+
ping?(): Promise<void>;
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Whether a provider failure describes the input file or its infrastructure.
|
|
211
|
+
* An `infrastructure` failure must not record the file as bad and must preserve
|
|
212
|
+
* existing artifacts.
|
|
213
|
+
*
|
|
214
|
+
* @provisional
|
|
215
|
+
*/
|
|
216
|
+
export type ExtractionFailureScope = 'file' | 'infrastructure';
|
|
217
|
+
/**
|
|
218
|
+
* The persisted output of one successful extraction. Document, version, path,
|
|
219
|
+
* access, selection and verification evidence are deliberately absent: they belong
|
|
220
|
+
* to references, so one artifact can serve every authorised reference to the same
|
|
221
|
+
* bytes and producer. An artifact is immutable.
|
|
222
|
+
*
|
|
223
|
+
* A reader that meets an unknown `schemaVersion` treats the artifact as unusable
|
|
224
|
+
* for reuse, never as malformed source data.
|
|
225
|
+
*
|
|
226
|
+
* @provisional
|
|
227
|
+
*/
|
|
228
|
+
export interface ExtractionArtifact {
|
|
229
|
+
schemaVersion: 1;
|
|
230
|
+
/** SHA-256 of the source bytes; `null` only for legacy imports. */
|
|
231
|
+
contentHash: string | null;
|
|
232
|
+
/** `true` exactly when `contentHash` came from a verified read. */
|
|
233
|
+
sourceVerified: boolean;
|
|
234
|
+
provider: string;
|
|
235
|
+
providerVersion: string | null;
|
|
236
|
+
/**
|
|
237
|
+
* `null` only for a historical import whose configuration was never recorded. A
|
|
238
|
+
* new extraction always records the hash from its producer identity. A current
|
|
239
|
+
* hash is never written onto historical output.
|
|
240
|
+
*/
|
|
241
|
+
producerConfigHash: string | null;
|
|
242
|
+
plainText: string;
|
|
243
|
+
markdown?: string;
|
|
244
|
+
structure?: JsonValue;
|
|
245
|
+
pageMap?: PageMapV1;
|
|
246
|
+
pageCount?: number;
|
|
247
|
+
metadata: ExtractionMetadata;
|
|
248
|
+
signals: RoutingSignals;
|
|
249
|
+
/** ISO 8601. */
|
|
250
|
+
producedAt: string;
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* The stored object cannot be confirmed to be the file the document records.
|
|
254
|
+
*
|
|
255
|
+
* @provisional
|
|
256
|
+
*/
|
|
257
|
+
export type SourceIdentityProblem = 'missing-hash' | 'missing-object' | 'hash-mismatch';
|
|
258
|
+
/**
|
|
259
|
+
* Why a source could not be read. None of these says anything about the file.
|
|
260
|
+
*
|
|
261
|
+
* @provisional
|
|
262
|
+
*/
|
|
263
|
+
export type SourceUnavailableReason = 'download-unsupported' | 'provider-mismatch' | 'read-failed' | 'aborted';
|
|
264
|
+
/**
|
|
265
|
+
* Why a read that expected a validator fell back to one unconditional read. A
|
|
266
|
+
* changed token does not prove changed content; the hash decides.
|
|
267
|
+
*
|
|
268
|
+
* @provisional
|
|
269
|
+
*/
|
|
270
|
+
export type PreconditionFallbackReason = 'changed' | 'unsupported-kind' | 'unestablishable';
|
|
271
|
+
/**
|
|
272
|
+
* The record that binds an extraction to one verified read at one time. It is not
|
|
273
|
+
* continuous storage monitoring, and a hash copied from the saved file value is not
|
|
274
|
+
* evidence. It is persisted with the reference, never on the immutable artifact.
|
|
275
|
+
*
|
|
276
|
+
* @provisional
|
|
277
|
+
*/
|
|
278
|
+
export interface SourceVerificationEvidence {
|
|
279
|
+
/** The reference's `fileHash` at read time. */
|
|
280
|
+
expectedHash: string;
|
|
281
|
+
/** SHA-256 measured from the bytes read; equals `expectedHash`. */
|
|
282
|
+
contentHash: string;
|
|
283
|
+
/** The validator returned with those bytes. */
|
|
284
|
+
validator: StorageValidator;
|
|
285
|
+
/** ISO 8601. */
|
|
286
|
+
verifiedAt: string;
|
|
287
|
+
size?: number;
|
|
288
|
+
readBasis?: 'expected' | 'unconditional' | 'fallback';
|
|
289
|
+
preconditionFallback?: PreconditionFallbackReason;
|
|
290
|
+
}
|
|
291
|
+
/**
|
|
292
|
+
* Why extraction work is required.
|
|
293
|
+
*
|
|
294
|
+
* @provisional
|
|
295
|
+
*/
|
|
296
|
+
export type ExtractionWorkReason = 'force'
|
|
297
|
+
/** Nothing usable exists for this reference and producer. */
|
|
298
|
+
| 'no-artifact'
|
|
299
|
+
/** The last attempt failed or hit an identity problem. */
|
|
300
|
+
| 'previous-attempt-unsuccessful'
|
|
301
|
+
/** The reference's `fileHash` differs from the artifact's `contentHash`. */
|
|
302
|
+
| 'source-changed'
|
|
303
|
+
/** There is no valid evidence for the current hash. */
|
|
304
|
+
| 'unverified'
|
|
305
|
+
/** The provider, version or configuration hash differs. */
|
|
306
|
+
| 'producer-changed'
|
|
307
|
+
/** The artifact's `schemaVersion` is not the current one. */
|
|
308
|
+
| 'schema-changed';
|
|
309
|
+
/**
|
|
310
|
+
* The result of `decideExtractionWork`.
|
|
311
|
+
*
|
|
312
|
+
* `versionUnchecked` is reduced assurance: the current engine version and the
|
|
313
|
+
* deployment identifier are both unknown, so an engine upgrade is not detected.
|
|
314
|
+
* `producerUnchecked` is reduced assurance of the same kind, and is `true` only for
|
|
315
|
+
* historical output with no recorded configuration: that configuration was not
|
|
316
|
+
* compared, and a `null` recorded engine version was not compared either.
|
|
317
|
+
*
|
|
318
|
+
* @provisional
|
|
319
|
+
*/
|
|
320
|
+
export type ExtractionWorkDecision = {
|
|
321
|
+
kind: 'identity-problem';
|
|
322
|
+
problem: 'missing-hash';
|
|
323
|
+
} | {
|
|
324
|
+
kind: 'work';
|
|
325
|
+
reasons: ExtractionWorkReason[];
|
|
326
|
+
versionUnchecked: boolean;
|
|
327
|
+
} | {
|
|
328
|
+
kind: 'reuse';
|
|
329
|
+
evidence: SourceVerificationEvidence;
|
|
330
|
+
versionUnchecked: boolean;
|
|
331
|
+
producerUnchecked: boolean;
|
|
332
|
+
};
|
|
333
|
+
/**
|
|
334
|
+
* Why a promotion was refused.
|
|
335
|
+
*
|
|
336
|
+
* @provisional
|
|
337
|
+
*/
|
|
338
|
+
export type PromotionRefusalReason = 'hash-changed' | 'version-changed' | 'access-changed' | 'artifact-mismatch' | 'claim-lost';
|
|
339
|
+
/**
|
|
340
|
+
* Was the source verified, and was extraction reused, completed, unavailable,
|
|
341
|
+
* blocked or refused?
|
|
342
|
+
*
|
|
343
|
+
* @provisional
|
|
344
|
+
*/
|
|
345
|
+
export type DerivationOutcome = {
|
|
346
|
+
kind: 'reused';
|
|
347
|
+
} | {
|
|
348
|
+
kind: 'extracted';
|
|
349
|
+
} | {
|
|
350
|
+
kind: 'identity-problem';
|
|
351
|
+
problem: SourceIdentityProblem;
|
|
352
|
+
} | {
|
|
353
|
+
kind: 'source-unavailable';
|
|
354
|
+
reason: SourceUnavailableReason;
|
|
355
|
+
} | {
|
|
356
|
+
kind: 'provider-failed';
|
|
357
|
+
failureScope: ExtractionFailureScope;
|
|
358
|
+
retryable: boolean;
|
|
359
|
+
} | {
|
|
360
|
+
kind: 'promotion-refused';
|
|
361
|
+
reason: PromotionRefusalReason;
|
|
362
|
+
};
|
|
363
|
+
/**
|
|
364
|
+
* Whether making previously eligible text ineligible was durably recorded.
|
|
365
|
+
*
|
|
366
|
+
* @provisional
|
|
367
|
+
*/
|
|
368
|
+
export type EligibilityUpdateOutcome =
|
|
369
|
+
/** Nothing previously eligible became ineligible. */
|
|
370
|
+
{
|
|
371
|
+
kind: 'not-required';
|
|
372
|
+
}
|
|
373
|
+
/** Ineligibility was durably recorded. */
|
|
374
|
+
| {
|
|
375
|
+
kind: 'recorded';
|
|
376
|
+
}
|
|
377
|
+
/** Recording failed; the join still includes the old text. */
|
|
378
|
+
| {
|
|
379
|
+
kind: 'failed';
|
|
380
|
+
};
|
|
381
|
+
/**
|
|
382
|
+
* Whether search reflects the current eligible selection. `written` means the
|
|
383
|
+
* provider acknowledged a write. It is not a durable exclusion guarantee.
|
|
384
|
+
*
|
|
385
|
+
* @provisional
|
|
386
|
+
*/
|
|
387
|
+
export type ProjectionOutcome = {
|
|
388
|
+
kind: 'written';
|
|
389
|
+
} | {
|
|
390
|
+
kind: 'pending';
|
|
391
|
+
reason: 'write-failed' | 'not-attempted' | 'stale-write';
|
|
392
|
+
};
|
|
393
|
+
/**
|
|
394
|
+
* Whether ineligible text has left search. Derive it only with `exclusionState`.
|
|
395
|
+
* `excluded` means the eventual level succeeded for this write; it is not the
|
|
396
|
+
* durable read-time guarantee.
|
|
397
|
+
*
|
|
398
|
+
* @provisional
|
|
399
|
+
*/
|
|
400
|
+
export type ExclusionState = {
|
|
401
|
+
state: 'not-required';
|
|
402
|
+
} | {
|
|
403
|
+
state: 'excluded';
|
|
404
|
+
} | {
|
|
405
|
+
state: 'pending';
|
|
406
|
+
reason: 'eligibility-update-failed' | 'write-failed' | 'not-attempted' | 'stale-write';
|
|
407
|
+
};
|
|
408
|
+
/**
|
|
409
|
+
* The independent facts about one attachment after processing. A save, an extraction
|
|
410
|
+
* and a search update cannot be reported with one `success` flag.
|
|
411
|
+
*
|
|
412
|
+
* @provisional
|
|
413
|
+
*/
|
|
414
|
+
export interface AttachmentProcessingReport {
|
|
415
|
+
documentId: string;
|
|
416
|
+
documentVersionId?: string;
|
|
417
|
+
storagePath: string;
|
|
418
|
+
runId?: string;
|
|
419
|
+
derivation: DerivationOutcome;
|
|
420
|
+
eligibility?: EligibilityUpdateOutcome;
|
|
421
|
+
projection?: ProjectionOutcome;
|
|
422
|
+
exclusion?: ExclusionState;
|
|
423
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This Source Code is subject to the terms of the Mozilla Public
|
|
3
|
+
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
4
|
+
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
5
|
+
*
|
|
6
|
+
* Copyright (c) Infonomic Company Limited
|
|
7
|
+
*/
|
|
8
|
+
export {};
|
package/dist/@types/index.d.ts
CHANGED
|
@@ -9,6 +9,7 @@ export * from './admin-types.js';
|
|
|
9
9
|
export * from './collection-types.js';
|
|
10
10
|
export * from './db-types.js';
|
|
11
11
|
export * from './document-revision.js';
|
|
12
|
+
export * from './extraction-types.js';
|
|
12
13
|
export * from './field-data-types.js';
|
|
13
14
|
export * from './field-types.js';
|
|
14
15
|
export * from './populate-types.js';
|
package/dist/@types/index.js
CHANGED
|
@@ -9,6 +9,7 @@ export * from './admin-types.js';
|
|
|
9
9
|
export * from './collection-types.js';
|
|
10
10
|
export * from './db-types.js';
|
|
11
11
|
export * from './document-revision.js';
|
|
12
|
+
export * from './extraction-types.js';
|
|
12
13
|
export * from './field-data-types.js';
|
|
13
14
|
export * from './field-types.js';
|
|
14
15
|
export * from './populate-types.js';
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This Source Code is subject to the terms of the Mozilla Public
|
|
3
|
+
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
4
|
+
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
5
|
+
*
|
|
6
|
+
* Copyright (c) Infonomic Company Limited
|
|
7
|
+
*/
|
|
8
|
+
import { type PageMapIssue } from './page-map.js';
|
|
9
|
+
/** The persisted artifact shape this release reads and writes. */
|
|
10
|
+
export declare const EXTRACTION_ARTIFACT_SCHEMA_VERSION = 1;
|
|
11
|
+
/**
|
|
12
|
+
* A code for one broken artifact rule.
|
|
13
|
+
*
|
|
14
|
+
* @provisional
|
|
15
|
+
*/
|
|
16
|
+
export type ExtractionArtifactIssueCode = 'invalid-shape' | 'unsupported-schema-version' | 'invalid-content-hash' | 'source-verified-mismatch' | 'invalid-provider' | 'invalid-provider-version' | 'invalid-producer-config-hash' | 'unrecorded-producer' | 'invalid-plain-text' | 'invalid-markdown' | 'invalid-structure' | 'invalid-page-count' | 'invalid-page-map' | 'invalid-metadata' | 'invalid-signals' | 'invalid-produced-at';
|
|
17
|
+
/**
|
|
18
|
+
* One broken artifact rule.
|
|
19
|
+
*
|
|
20
|
+
* @provisional
|
|
21
|
+
*/
|
|
22
|
+
export interface ExtractionArtifactIssue {
|
|
23
|
+
code: ExtractionArtifactIssueCode;
|
|
24
|
+
message: string;
|
|
25
|
+
/** The page map's own issues, for `invalid-page-map`. */
|
|
26
|
+
pageMapIssues?: PageMapIssue[];
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Checks a persisted artifact. A `null` `producerConfigHash` fails unless
|
|
30
|
+
* `allowUnrecordedProducer` is `true`, which only code reading or importing
|
|
31
|
+
* historical records passes. A new extraction always records the real hash from its
|
|
32
|
+
* `ExtractionProducerIdentity`.
|
|
33
|
+
*
|
|
34
|
+
* An unknown `schemaVersion` is reported alone: the artifact is unusable for reuse,
|
|
35
|
+
* and its other members cannot be judged against this version's rules.
|
|
36
|
+
*
|
|
37
|
+
* @provisional
|
|
38
|
+
*/
|
|
39
|
+
export declare function validateExtractionArtifact(artifact: unknown, options?: {
|
|
40
|
+
allowUnrecordedProducer?: boolean;
|
|
41
|
+
}): {
|
|
42
|
+
ok: true;
|
|
43
|
+
} | {
|
|
44
|
+
ok: false;
|
|
45
|
+
issues: ExtractionArtifactIssue[];
|
|
46
|
+
};
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This Source Code is subject to the terms of the Mozilla Public
|
|
3
|
+
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
4
|
+
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
5
|
+
*
|
|
6
|
+
* Copyright (c) Infonomic Company Limited
|
|
7
|
+
*/
|
|
8
|
+
import { CONTENT_HASH_PATTERN } from '../storage/content-hash.js';
|
|
9
|
+
import { everySlot, isCount, isIsoDateTime, isJsonValue, isPlainObject } from './internal.js';
|
|
10
|
+
import { validatePageMap } from './page-map.js';
|
|
11
|
+
/** The persisted artifact shape this release reads and writes. */
|
|
12
|
+
export const EXTRACTION_ARTIFACT_SCHEMA_VERSION = 1;
|
|
13
|
+
const isOptional = (value, check) => value === undefined || check(value);
|
|
14
|
+
const isString = (value) => typeof value === 'string';
|
|
15
|
+
/** Recognised members only; unknown members are ignored, as in stored evidence. */
|
|
16
|
+
function isValidMetadata(metadata) {
|
|
17
|
+
if (!isPlainObject(metadata))
|
|
18
|
+
return false;
|
|
19
|
+
const { contentType, pages, title, authors, language, createdAt, producer } = metadata;
|
|
20
|
+
return ([contentType, title, language, producer].every((member) => isOptional(member, isString)) &&
|
|
21
|
+
isOptional(createdAt, isIsoDateTime) &&
|
|
22
|
+
isOptional(pages, isCount) &&
|
|
23
|
+
isOptional(authors, (value) => everySlot(value, isString)));
|
|
24
|
+
}
|
|
25
|
+
/** Recognised members only; unknown members are ignored, as in stored evidence. */
|
|
26
|
+
function isValidSignals(signals) {
|
|
27
|
+
if (!isPlainObject(signals) || !isCount(signals.totalChars))
|
|
28
|
+
return false;
|
|
29
|
+
const { charsPerPage, ocrCandidatePages, textLayerCoverage } = signals;
|
|
30
|
+
return (isOptional(charsPerPage, (value) => everySlot(value, isCount)) &&
|
|
31
|
+
isOptional(ocrCandidatePages, isCount) &&
|
|
32
|
+
isOptional(textLayerCoverage, (value) => typeof value === 'number' && value >= 0 && value <= 1));
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Checks a persisted artifact. A `null` `producerConfigHash` fails unless
|
|
36
|
+
* `allowUnrecordedProducer` is `true`, which only code reading or importing
|
|
37
|
+
* historical records passes. A new extraction always records the real hash from its
|
|
38
|
+
* `ExtractionProducerIdentity`.
|
|
39
|
+
*
|
|
40
|
+
* An unknown `schemaVersion` is reported alone: the artifact is unusable for reuse,
|
|
41
|
+
* and its other members cannot be judged against this version's rules.
|
|
42
|
+
*
|
|
43
|
+
* @provisional
|
|
44
|
+
*/
|
|
45
|
+
export function validateExtractionArtifact(artifact, options) {
|
|
46
|
+
const issues = [];
|
|
47
|
+
const report = (code, message) => {
|
|
48
|
+
issues.push({ code, message });
|
|
49
|
+
};
|
|
50
|
+
if (!isPlainObject(artifact)) {
|
|
51
|
+
return {
|
|
52
|
+
ok: false,
|
|
53
|
+
issues: [{ code: 'invalid-shape', message: 'the artifact is not a plain object' }],
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
if (artifact.schemaVersion !== EXTRACTION_ARTIFACT_SCHEMA_VERSION) {
|
|
57
|
+
return {
|
|
58
|
+
ok: false,
|
|
59
|
+
issues: [
|
|
60
|
+
{
|
|
61
|
+
code: 'unsupported-schema-version',
|
|
62
|
+
message: `schemaVersion ${String(artifact.schemaVersion)} is not supported`,
|
|
63
|
+
},
|
|
64
|
+
],
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
const { contentHash, producerConfigHash, providerVersion, plainText, pageCount } = artifact;
|
|
68
|
+
if (contentHash !== null &&
|
|
69
|
+
(typeof contentHash !== 'string' || !CONTENT_HASH_PATTERN.test(contentHash))) {
|
|
70
|
+
report('invalid-content-hash', 'contentHash must be a lower-case SHA-256 or null');
|
|
71
|
+
}
|
|
72
|
+
if (artifact.sourceVerified !== (contentHash !== null)) {
|
|
73
|
+
report('source-verified-mismatch', 'sourceVerified must be true exactly when contentHash is set');
|
|
74
|
+
}
|
|
75
|
+
if (typeof artifact.provider !== 'string' || artifact.provider === '') {
|
|
76
|
+
report('invalid-provider', 'provider must be a non-empty string');
|
|
77
|
+
}
|
|
78
|
+
if (providerVersion !== null && typeof providerVersion !== 'string') {
|
|
79
|
+
report('invalid-provider-version', 'providerVersion must be a string or null');
|
|
80
|
+
}
|
|
81
|
+
if (producerConfigHash === null) {
|
|
82
|
+
if (options?.allowUnrecordedProducer !== true) {
|
|
83
|
+
report('unrecorded-producer', 'a new artifact must record its producerConfigHash');
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
else if (typeof producerConfigHash !== 'string' ||
|
|
87
|
+
!CONTENT_HASH_PATTERN.test(producerConfigHash)) {
|
|
88
|
+
report('invalid-producer-config-hash', 'producerConfigHash must be a lower-case SHA-256');
|
|
89
|
+
}
|
|
90
|
+
if (typeof plainText !== 'string')
|
|
91
|
+
report('invalid-plain-text', 'plainText must be a string');
|
|
92
|
+
if (artifact.markdown !== undefined && typeof artifact.markdown !== 'string') {
|
|
93
|
+
report('invalid-markdown', 'markdown must be a string');
|
|
94
|
+
}
|
|
95
|
+
if (artifact.structure !== undefined && !isJsonValue(artifact.structure)) {
|
|
96
|
+
report('invalid-structure', 'structure must be a JSON value');
|
|
97
|
+
}
|
|
98
|
+
if (pageCount !== undefined && !isCount(pageCount)) {
|
|
99
|
+
report('invalid-page-count', 'pageCount must be a non-negative integer');
|
|
100
|
+
}
|
|
101
|
+
if (!isValidMetadata(artifact.metadata)) {
|
|
102
|
+
report('invalid-metadata', 'metadata must be an object whose known members have valid types');
|
|
103
|
+
}
|
|
104
|
+
if (!isValidSignals(artifact.signals)) {
|
|
105
|
+
report('invalid-signals', 'signals must be an object whose known members have valid types');
|
|
106
|
+
}
|
|
107
|
+
if (!isIsoDateTime(artifact.producedAt)) {
|
|
108
|
+
report('invalid-produced-at', 'producedAt must be an ISO 8601 date-time');
|
|
109
|
+
}
|
|
110
|
+
if (artifact.pageMap !== undefined && typeof plainText === 'string') {
|
|
111
|
+
const pageMap = validatePageMap(plainText, artifact.pageMap, isCount(pageCount) ? { pageCount: pageCount } : undefined);
|
|
112
|
+
if (!pageMap.ok) {
|
|
113
|
+
issues.push({
|
|
114
|
+
code: 'invalid-page-map',
|
|
115
|
+
message: 'pageMap breaks the page map rules',
|
|
116
|
+
pageMapIssues: pageMap.issues,
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return issues.length === 0 ? { ok: true } : { ok: false, issues };
|
|
121
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This Source Code is subject to the terms of the Mozilla Public
|
|
3
|
+
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
4
|
+
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
5
|
+
*
|
|
6
|
+
* Copyright (c) Infonomic Company Limited
|
|
7
|
+
*/
|
|
8
|
+
export {};
|