@semiont/content 0.5.30 → 0.5.32
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/index.d.ts +134 -151
- package/dist/index.js +168 -114
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.d.ts
CHANGED
|
@@ -1,155 +1,102 @@
|
|
|
1
1
|
import { Readable } from 'stream';
|
|
2
2
|
import { SemiontProject, ArchivistAddressConfig } from '@semiont/core/node';
|
|
3
|
-
import { Logger, ExtractionOutcome, PdfTextItem,
|
|
3
|
+
import { Logger, StoredResource, ExtractionOutcome, PdfTextItem, IContentTransport, AnchoredText } from '@semiont/core';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
* Two write paths:
|
|
14
|
-
* - store(content, storageUri): Write bytes to disk (API/GUI/AI path).
|
|
15
|
-
* Used when the file does not yet exist and the caller provides content.
|
|
16
|
-
* - register(storageUri, expectedChecksum?): Adopt a file already on disk and
|
|
17
|
-
* return its metadata. The CLI path (the file arrived by other means) and
|
|
18
|
-
* the event-apply path (the Stower staging bytes an event names) both use
|
|
19
|
-
* it. Streams the file to hash it — never holds it. If expectedChecksum is
|
|
20
|
-
* provided, throws on mismatch.
|
|
21
|
-
*
|
|
22
|
-
* Storage layout:
|
|
23
|
-
* {projectRoot}/{path-from-uri}
|
|
24
|
-
*
|
|
25
|
-
* For example, storageUri "file://docs/overview.md" resolves to
|
|
26
|
-
* {projectRoot}/docs/overview.md
|
|
27
|
-
*/
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* Result of store() or register()
|
|
6
|
+
* Deferred, deduped `git add`. The index is for humans who commit by hand, so
|
|
7
|
+
* it must be current within seconds, not after every change.
|
|
8
|
+
*
|
|
9
|
+
* Serialized per repo — git's index is single-writer, and concurrent `git add`
|
|
10
|
+
* fails on `index.lock` rather than retrying. Created on first use, never at
|
|
11
|
+
* import: consumers of this package may never stage anything.
|
|
31
12
|
*/
|
|
32
|
-
interface
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
13
|
+
interface StagerOptions {
|
|
14
|
+
/** Quiet period after the last change before staging. */
|
|
15
|
+
flushMs?: number;
|
|
16
|
+
/** Ceiling on staleness: stage this long after the OLDEST pending path even
|
|
17
|
+
* if changes keep arriving. Without it a continuous append stream resets
|
|
18
|
+
* the debounce forever and the index never updates. */
|
|
19
|
+
maxWaitMs?: number;
|
|
20
|
+
}
|
|
21
|
+
interface Stager {
|
|
22
|
+
/** Queue a path. Returns immediately; deduped against what is pending. */
|
|
23
|
+
add(path: string): void;
|
|
24
|
+
/** Run an order-sensitive command (`mv`, `rm`): pending adds flush first,
|
|
25
|
+
* then this runs alone — it must not overtake the adds it depends on. */
|
|
26
|
+
run(args: string[]): Promise<void>;
|
|
27
|
+
/** Stage everything pending now. */
|
|
28
|
+
flush(): Promise<void>;
|
|
29
|
+
/** Paths queued and not yet staged. */
|
|
30
|
+
pending(): number;
|
|
31
|
+
/** Drain and stop. A stopped process must leave nothing unstaged. */
|
|
32
|
+
dispose(): Promise<void>;
|
|
37
33
|
}
|
|
34
|
+
declare function createStager(cwd: string, options?: StagerOptions): Stager;
|
|
35
|
+
|
|
38
36
|
/**
|
|
39
|
-
*
|
|
37
|
+
* Files in the project working tree, addressed by file:// URI —
|
|
38
|
+
* "file://docs/overview.md" is {projectRoot}/docs/overview.md.
|
|
39
|
+
*
|
|
40
|
+
* `store` writes bytes the caller supplies; `register` adopts a file already
|
|
41
|
+
* on disk. Both stream to hash, neither holds a representation in memory.
|
|
40
42
|
*/
|
|
43
|
+
|
|
41
44
|
declare class WorkingTreeStore {
|
|
42
45
|
private projectRoot;
|
|
43
46
|
private gitSync;
|
|
44
47
|
private logger?;
|
|
45
|
-
|
|
48
|
+
private _stager?;
|
|
49
|
+
private readonly staging;
|
|
50
|
+
/** `staging` is policy — how stale the index may get is the caller's call. */
|
|
51
|
+
constructor(project: SemiontProject, logger?: Logger, staging?: StagerOptions);
|
|
52
|
+
/** Created on first use — importers of this package may never stage. */
|
|
53
|
+
private stager;
|
|
54
|
+
/** Stage everything pending now — for a caller that wants the index current. */
|
|
55
|
+
flushStaging(): Promise<void>;
|
|
56
|
+
/** Drain and stop. A stopped process must leave nothing unstaged. */
|
|
57
|
+
dispose(): Promise<void>;
|
|
46
58
|
private shouldRunGit;
|
|
47
59
|
/**
|
|
48
|
-
* Write
|
|
49
|
-
*
|
|
50
|
-
* API/GUI/AI path: caller provides bytes — as a Buffer it already holds, or
|
|
51
|
-
* as a stream (the Archivist's write endpoint hands the request body
|
|
52
|
-
* straight through, SINGLE-KB-MOUNT P2/D7: memory stays bounded by the
|
|
53
|
-
* chunk, never the representation).
|
|
60
|
+
* Write bytes to the path storageUri names, whole or streamed.
|
|
54
61
|
*
|
|
55
|
-
* Atomic
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
* overwritten survives) and no temp file behind, so the Stower's `register`
|
|
60
|
-
* can never find partial bytes an event names.
|
|
62
|
+
* Atomic: bytes land in a temp file and are renamed into place only once
|
|
63
|
+
* complete and once `expectedChecksum`, when given, agrees. A mismatch or a
|
|
64
|
+
* torn stream leaves the target untouched, so `register` can never find
|
|
65
|
+
* partial bytes an event names.
|
|
61
66
|
*
|
|
62
|
-
* @param content - Raw bytes to write, whole or streamed
|
|
63
|
-
* @param storageUri - file:// URI (e.g. "file://docs/overview.md")
|
|
64
67
|
* @throws ChecksumMismatchError when expectedChecksum disagrees with the body
|
|
65
|
-
* @returns Stored resource metadata
|
|
66
68
|
*/
|
|
67
69
|
store(content: Buffer | Readable, storageUri: string, options?: {
|
|
68
70
|
noGit?: boolean;
|
|
69
71
|
expectedChecksum?: string;
|
|
70
72
|
}): Promise<StoredResource>;
|
|
71
73
|
/**
|
|
72
|
-
*
|
|
74
|
+
* Adopt a file already on disk: stream it to hash it, then stage it.
|
|
73
75
|
*
|
|
74
|
-
*
|
|
75
|
-
* it is, then stages it. If expectedChecksum is provided, throws
|
|
76
|
-
* ChecksumMismatchError on mismatch.
|
|
77
|
-
*
|
|
78
|
-
* @param storageUri - file:// URI (e.g. "file://docs/overview.md")
|
|
79
|
-
* @param expectedChecksum - Optional SHA-256 to verify against
|
|
80
|
-
* @returns Stored resource metadata
|
|
81
|
-
* @throws ChecksumMismatchError if expectedChecksum is provided and does not match
|
|
82
|
-
* @throws Error if file does not exist
|
|
76
|
+
* @throws ChecksumMismatchError if expectedChecksum is given and disagrees
|
|
83
77
|
*/
|
|
84
78
|
register(storageUri: string, expectedChecksum?: string, options?: {
|
|
85
79
|
noGit?: boolean;
|
|
86
80
|
}): Promise<StoredResource>;
|
|
87
81
|
/**
|
|
88
|
-
*
|
|
89
|
-
*
|
|
90
|
-
*
|
|
91
|
-
* @returns Raw bytes
|
|
92
|
-
*/
|
|
93
|
-
/**
|
|
94
|
-
* The same bytes as `retrieve`, streamed — for the byte paths that must not
|
|
95
|
-
* hold a whole representation in memory (SINGLE-KB-MOUNT D7: the Archivist
|
|
96
|
-
* serves content for every reader now, so its memory cannot be bounded by
|
|
97
|
-
* the largest file anyone asks for).
|
|
98
|
-
*
|
|
99
|
-
* Lazy by construction: the stream is created here but nothing is read
|
|
100
|
-
* until the caller iterates, so a missing file surfaces as an `error` event
|
|
101
|
-
* on the stream rather than a rejected promise. Callers that need the
|
|
102
|
-
* distinction up front should resolve the descriptor first — which is what
|
|
103
|
-
* `resolveRepresentation` does.
|
|
82
|
+
* The same bytes as `retrieve`, streamed. Lazy: a missing file surfaces as
|
|
83
|
+
* an `error` event on the stream, not a rejected promise — callers needing
|
|
84
|
+
* that up front should resolve the descriptor first.
|
|
104
85
|
*/
|
|
105
86
|
retrieveStream(storageUri: string): Readable;
|
|
106
87
|
retrieve(storageUri: string): Promise<Buffer>;
|
|
107
|
-
/**
|
|
108
|
-
* Move a file from one URI to another.
|
|
109
|
-
*
|
|
110
|
-
* If .git/ exists in the project root and noGit is not set, runs `git mv`.
|
|
111
|
-
* Otherwise (no .git/ or noGit: true), runs fs.rename.
|
|
112
|
-
*
|
|
113
|
-
* @param fromUri - Current file:// URI
|
|
114
|
-
* @param toUri - New file:// URI
|
|
115
|
-
* @param options.noGit - Skip git mv even if .git/ is present
|
|
116
|
-
*/
|
|
88
|
+
/** `git mv` when the project syncs git, `fs.rename` otherwise. */
|
|
117
89
|
move(fromUri: string, toUri: string, options?: {
|
|
118
90
|
noGit?: boolean;
|
|
119
91
|
}): Promise<void>;
|
|
120
|
-
/**
|
|
121
|
-
* Remove a file from the working tree.
|
|
122
|
-
*
|
|
123
|
-
* If .git/ exists and noGit is not set:
|
|
124
|
-
* - keepFile false (default): runs `git rm` (removes from index and disk)
|
|
125
|
-
* - keepFile true: runs `git rm --cached` (removes from index only, file stays on disk)
|
|
126
|
-
* If no .git/ or noGit: true:
|
|
127
|
-
* - keepFile false: runs fs.unlink
|
|
128
|
-
* - keepFile true: no-op on filesystem
|
|
129
|
-
*
|
|
130
|
-
* @param storageUri - file:// URI
|
|
131
|
-
* @param options.noGit - Skip git rm even if .git/ is present
|
|
132
|
-
* @param options.keepFile - Remove from git index only; leave file on disk
|
|
133
|
-
*/
|
|
92
|
+
/** @param options.keepFile - Drop from the index only; leave the file on disk. */
|
|
134
93
|
remove(storageUri: string, options?: {
|
|
135
94
|
noGit?: boolean;
|
|
136
95
|
keepFile?: boolean;
|
|
137
96
|
}): Promise<void>;
|
|
138
|
-
/**
|
|
139
|
-
* Convert a file:// URI to an absolute filesystem path.
|
|
140
|
-
*
|
|
141
|
-
* "file://docs/overview.md" → "{projectRoot}/docs/overview.md"
|
|
142
|
-
*
|
|
143
|
-
* @param storageUri - file:// URI
|
|
144
|
-
* @returns Absolute path
|
|
145
|
-
*/
|
|
146
97
|
resolveUri(storageUri: string): string;
|
|
147
98
|
}
|
|
148
|
-
/**
|
|
149
|
-
* Thrown when a registered file's checksum does not match the expected value.
|
|
150
|
-
* This indicates the file on disk differs from what was recorded (e.g. modified
|
|
151
|
-
* after staging, or wrong file path provided).
|
|
152
|
-
*/
|
|
99
|
+
/** The file on disk is not the file the checksum names. */
|
|
153
100
|
declare class ChecksumMismatchError extends Error {
|
|
154
101
|
readonly storageUri: string;
|
|
155
102
|
readonly expected: string;
|
|
@@ -308,19 +255,23 @@ interface AnchoredTextStore {
|
|
|
308
255
|
declare function createAnchoredTextStore(dir: string, logger?: Logger): AnchoredTextStore;
|
|
309
256
|
|
|
310
257
|
/**
|
|
311
|
-
*
|
|
312
|
-
*
|
|
313
|
-
*
|
|
314
|
-
*
|
|
315
|
-
*
|
|
316
|
-
*
|
|
317
|
-
*
|
|
318
|
-
*
|
|
319
|
-
*
|
|
320
|
-
*
|
|
321
|
-
*
|
|
322
|
-
*
|
|
323
|
-
*
|
|
258
|
+
* TextExtractor — DERIVING text from bytes that carry none of their own.
|
|
259
|
+
*
|
|
260
|
+
* Scope note (READ-VS-EXTRACT P2/P3): this file used to hold a registry covering
|
|
261
|
+
* both ways a resource yields text — decoding (charset-aware `Buffer → string`)
|
|
262
|
+
* and deriving (parse a PDF, OCR it when there is no text layer). Those shared a
|
|
263
|
+
* name and almost nothing else: microseconds vs. minutes,
|
|
264
|
+
* total determinism vs. none across engine versions, no canonical artifact vs.
|
|
265
|
+
* exactly one, and anyone-with-bytes vs. the Smelter alone. The registry made
|
|
266
|
+
* them interchangeable at every call site.
|
|
267
|
+
*
|
|
268
|
+
* Decoding left: it is `decodeRepresentation` in `@semiont/core`, called
|
|
269
|
+
* directly. What remains here is the deriving half, reached through
|
|
270
|
+
* `derivingExtractorFor` and callable only with the store that persists its
|
|
271
|
+
* output.
|
|
272
|
+
*
|
|
273
|
+
* Anchoring is unaffected: annotations anchor to native geometry (`items`),
|
|
274
|
+
* never to extracted-text offsets, so re-derivation can never break an anchor.
|
|
324
275
|
*/
|
|
325
276
|
|
|
326
277
|
interface ExtractedText {
|
|
@@ -375,7 +326,7 @@ interface ExtractionDecline {
|
|
|
375
326
|
declined: 'no-text-layer' | 'encrypted' | 'corrupt' | 'too-large';
|
|
376
327
|
}
|
|
377
328
|
/**
|
|
378
|
-
* Where a
|
|
329
|
+
* Where a derivation may reuse an earlier recognition, and under what key.
|
|
379
330
|
*
|
|
380
331
|
* The caller supplies the key, and derives it from the bytes it actually
|
|
381
332
|
* holds — `calculateChecksum` over the same Buffer it passes to `extract()` —
|
|
@@ -386,40 +337,72 @@ interface ExtractionDecline {
|
|
|
386
337
|
* (PERSIST-ANCHORS P1b); readers mirror it (P1c). One SHA-256 over bytes
|
|
387
338
|
* already in memory is noise against the engine pass a hit avoids.
|
|
388
339
|
*
|
|
389
|
-
*
|
|
390
|
-
*
|
|
391
|
-
*
|
|
392
|
-
*
|
|
393
|
-
*
|
|
394
|
-
*
|
|
340
|
+
* REQUIRED, and that is the ownership rule (READ-VS-EXTRACT P2). It carries an
|
|
341
|
+
* `AnchoredTextStore`, and only the Smelter holds one — so deriving is reachable
|
|
342
|
+
* exactly to the process that can persist what it derived. The restriction is a
|
|
343
|
+
* capability the caller must already hold, not a convention it must remember:
|
|
344
|
+
* a would-be second producer cannot construct the argument, so it cannot compile.
|
|
345
|
+
*
|
|
346
|
+
* The seam is `extract()` itself (PERSIST-ANCHORS D1/P2b): a hit returns the
|
|
347
|
+
* FINISHED outcome — classification, geometry, provenance, or a named decline —
|
|
348
|
+
* so neither the native parse nor the engine runs.
|
|
395
349
|
*/
|
|
396
350
|
interface ExtractionCache {
|
|
397
351
|
key: string;
|
|
398
352
|
store: AnchoredTextStore;
|
|
399
353
|
}
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
354
|
+
/**
|
|
355
|
+
* Deriving text from bytes that carry none of their own.
|
|
356
|
+
*
|
|
357
|
+
* Named `ContentExtractor` until READ-VS-EXTRACT P3: the `Content` prefix named
|
|
358
|
+
* the INPUT, when what distinguishes this type is that it produces TEXT — by
|
|
359
|
+
* deriving, which since P3 is the only thing "extraction" means here. WHERE a
|
|
360
|
+
* media type's text comes from at all is core's `TextSource`, which spans both
|
|
361
|
+
* routes and is therefore not called extraction.
|
|
362
|
+
*
|
|
363
|
+
* Whether a text source yields positioned runs lives in `@semiont/core`'s
|
|
364
|
+
* `yieldsGeometryOf`, NOT here (P1). It is a property of the source, and that
|
|
365
|
+
* vocabulary is core's — declaring it per-implementation made it two facts that
|
|
366
|
+
* could disagree, and forced consumers asking about a media type to resolve an
|
|
367
|
+
* implementation to find out. `text-extractor.test.ts` gates core's answer
|
|
368
|
+
* against what these extractors actually produce.
|
|
369
|
+
*/
|
|
370
|
+
interface TextExtractor {
|
|
411
371
|
/**
|
|
412
|
-
*
|
|
413
|
-
* (scanned-without-OCR, encrypted, corrupt).
|
|
414
|
-
* and settles skipped with that reason.
|
|
372
|
+
* Derive text WITH geometry from bytes that carry no text of their own, or
|
|
373
|
+
* decline with the class reason (scanned-without-OCR, encrypted, corrupt).
|
|
374
|
+
* The caller skips embedding and settles skipped with that reason.
|
|
375
|
+
*
|
|
376
|
+
* Expensive, non-deterministic across engine versions, and the sole producer
|
|
377
|
+
* of a canonical artifact — which is why `cache` is required rather than
|
|
378
|
+
* optional (see `ExtractionCache`).
|
|
415
379
|
*/
|
|
416
|
-
extract(content: Buffer, mediaType: string, cache
|
|
380
|
+
extract(content: Buffer, mediaType: string, cache: ExtractionCache): Promise<ExtractedText | ExtractionDecline>;
|
|
417
381
|
}
|
|
418
382
|
/**
|
|
419
|
-
*
|
|
420
|
-
*
|
|
383
|
+
* The deriving extractor for a media type, or `null` when its text needs no
|
|
384
|
+
* deriving.
|
|
385
|
+
*
|
|
386
|
+
* **This replaced a `Record<TextSource, TextExtractor | null>` keyed by
|
|
387
|
+
* strategy (READ-VS-EXTRACT P2), and the deletion is the point.** That map held
|
|
388
|
+
* one real extractor, a `null`, and — under 'decode' — a one-line wrapper around
|
|
389
|
+
* core's `decodeRepresentation`, which five sites in `@semiont/make-meaning`
|
|
390
|
+
* already called directly. Resolving "give me an extractor for this media type"
|
|
391
|
+
* therefore returned, half the time, a trivial function dressed as the same
|
|
392
|
+
* capability as OCR: identical at the call site, wildly different in cost,
|
|
393
|
+
* determinism, and who is allowed to run it. That symmetry is what let a
|
|
394
|
+
* detection worker OCR scanned PDFs for four months without anyone reading it as
|
|
395
|
+
* a category error (#739).
|
|
396
|
+
*
|
|
397
|
+
* Decoding is now a direct `decodeRepresentation()` call at the two sites that
|
|
398
|
+
* need it. There is no registry to resolve, so there is no way to reach OCR by
|
|
399
|
+
* asking a generic question — and a caller that gets a non-null answer here still
|
|
400
|
+
* cannot run it without an `AnchoredTextStore`.
|
|
401
|
+
*
|
|
402
|
+
* Keyed by P1's `yieldsGeometryOf`, so this and the Smelter's publish gate cannot
|
|
403
|
+
* disagree about which media types have a canonical artifact.
|
|
421
404
|
*/
|
|
422
|
-
declare
|
|
405
|
+
declare function derivingExtractorFor(mediaType: string): TextExtractor | null;
|
|
423
406
|
|
|
424
407
|
/**
|
|
425
408
|
* PDF extractor — the 'pdf-text-layer' strategy (SMELTER-MEDIA-TYPES).
|
|
@@ -583,5 +566,5 @@ interface PdfTextLayer extends AnchoredText {
|
|
|
583
566
|
|
|
584
567
|
declare function extractPdfTextLayer(bytes: Uint8Array | Buffer): Promise<PdfTextLayer | null>;
|
|
585
568
|
|
|
586
|
-
export { ChecksumMismatchError,
|
|
587
|
-
export type { AnchoredTextStore, CachedAnchoredText, CachedLine,
|
|
569
|
+
export { ChecksumMismatchError, MAX_PDF_BYTES, RepresentationMissing, WorkingTreeStore, archivistContentReads, calculateChecksum, createAnchoredTextStore, createStager, derivingExtractorFor, extractPdfTextLayer, verifyChecksum, withinByteBudget };
|
|
570
|
+
export type { AnchoredTextStore, CachedAnchoredText, CachedLine, ContentReads, ExtractedText, ExtractionCache, ExtractionDecline, MissingReason, PdfFormField, PdfPageInfo, PdfTextLayer, Stager, StagerOptions, TextExtractor };
|