@hraness/sponge 0.5.2 → 0.6.1
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/README.md +8 -11
- package/dist/capture/NOTICES.txt +472 -0
- package/dist/capture/worker.js +553 -0
- package/dist/src/acquisition/capture.d.ts +3 -10
- package/dist/src/acquisition/capture.js +28 -202
- package/dist/src/acquisition/child-request.d.ts +40 -0
- package/dist/src/acquisition/child.d.ts +31 -0
- package/dist/src/acquisition/child.js +130 -0
- package/dist/src/acquisition/errors.d.ts +2 -2
- package/dist/src/acquisition/errors.js +15 -4
- package/dist/src/acquisition/index.d.ts +0 -3
- package/dist/src/acquisition/index.js +0 -2
- package/dist/src/acquisition/normalize.d.ts +1 -3
- package/dist/src/acquisition/normalize.js +20 -22
- package/dist/src/app/article.d.ts +1 -2
- package/dist/src/app/article.js +1 -2
- package/dist/src/cli/commands.d.ts +0 -1
- package/dist/src/cli/commands.js +10 -46
- package/dist/src/cli/config.d.ts +5 -0
- package/dist/src/cli/config.js +14 -0
- package/dist/src/cli/import.d.ts +1 -3
- package/dist/src/cli/import.js +3 -3
- package/dist/src/cli/main.js +0 -2
- package/dist/src/cli/runtimes.js +2 -3
- package/dist/src/cli/worker.d.ts +1 -4
- package/dist/src/cli/worker.js +0 -4
- package/dist/src/importers/apply.d.ts +0 -2
- package/dist/src/importers/apply.js +2 -2
- package/dist/src/importers/inspect.d.ts +2 -2
- package/dist/src/importers/inspect.js +24 -23
- package/dist/src/metadata/article.d.ts +0 -1
- package/dist/src/metadata/article.js +3 -2
- package/dist/src/pdf/capture.d.ts +7 -16
- package/dist/src/pdf/capture.js +44 -198
- package/dist/src/pdf/handler.d.ts +1 -2
- package/dist/src/pdf/handler.js +0 -1
- package/dist/src/pdf/normalize.d.ts +2 -3
- package/dist/src/pdf/normalize.js +34 -38
- package/dist/src/pdf/tools.d.ts +2 -0
- package/dist/src/pdf/tools.js +21 -0
- package/native/runtime-manifest.json +5 -5
- package/package.json +11 -2
- package/dist/src/acquisition/clip-helper.js +0 -14
- package/dist/src/acquisition/lock.d.ts +0 -1
- package/dist/src/acquisition/lock.js +0 -2
- package/dist/src/acquisition/pins.d.ts +0 -3
- package/dist/src/acquisition/pins.js +0 -21
- package/dist/src/acquisition/runtime.d.ts +0 -48
- package/dist/src/acquisition/runtime.js +0 -273
- package/dist/src/acquisition/verify-helper.d.ts +0 -1
- package/dist/src/acquisition/verify-helper.js +0 -26
- package/dist/src/importers/verify-helper.d.ts +0 -1
- package/dist/src/importers/verify-helper.js +0 -41
- package/dist/src/pdf/capture-helper.d.ts +0 -1
- package/dist/src/pdf/capture-helper.js +0 -15
- package/dist/src/pdf/errors.d.ts +0 -2
- package/dist/src/pdf/errors.js +0 -22
- package/dist/src/pdf/source-identity.d.ts +0 -4
- package/dist/src/pdf/source-identity.js +0 -13
- package/dist/src/pdf/verify-helper.d.ts +0 -1
- package/dist/src/pdf/verify-helper.js +0 -29
- /package/dist/src/acquisition/{clip-helper.d.ts → child-request.js} +0 -0
package/dist/src/cli/commands.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { isAbsolute } from 'node:path';
|
|
2
2
|
import { randomUUID } from 'node:crypto';
|
|
3
|
-
import { readConnections } from './config.js';
|
|
3
|
+
import { connectionFor, readConnections } from './config.js';
|
|
4
4
|
import { pdfSource } from '../pdf/index.js';
|
|
5
5
|
import { articleSource, inferKind, mediaSource } from '../app/source.js';
|
|
6
|
-
import {
|
|
6
|
+
import { pdfToolsAvailable } from '../pdf/tools.js';
|
|
7
7
|
import { queueAction } from './queue.js';
|
|
8
8
|
import { portabilityAction } from './portability.js';
|
|
9
9
|
import { restoreLibrary } from '../portability/backup.js';
|
|
@@ -28,15 +28,6 @@ import { SpongeError, codeOf, publicCode, exitFor, isSpongeCode } from '../error
|
|
|
28
28
|
const abs = (v) => typeof v === 'string' && isAbsolute(v);
|
|
29
29
|
const media = { language: 'value', 'force-local': 'boolean', 'allow-private-network': 'boolean' };
|
|
30
30
|
const lines = (items) => items.map((item) => `${item.itemId}\t${item.title}`);
|
|
31
|
-
/** A required capture runtime, as the categorical code of its inspection failure. */
|
|
32
|
-
async function captureRuntime(c) {
|
|
33
|
-
try {
|
|
34
|
-
return await inspectCaptureRuntime(c.captureRoot, defaultCapturePins);
|
|
35
|
-
}
|
|
36
|
-
catch (error) {
|
|
37
|
-
throw new SpongeError(publicCode(error, 'runtime-missing'), { cause: error });
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
31
|
/** The failure of a job that did not run here: its state, or its recorded reason. */
|
|
41
32
|
function jobFailure(store, jobId) {
|
|
42
33
|
const job = store.queue.list().find((j) => j.id === jobId), reason = job?.reason;
|
|
@@ -50,16 +41,7 @@ function jobFailure(store, jobId) {
|
|
|
50
41
|
? reason
|
|
51
42
|
: 'unclassified');
|
|
52
43
|
}
|
|
53
|
-
const resolveConnection = (connections) => (realm) =>
|
|
54
|
-
if (realm === 'public')
|
|
55
|
-
return undefined;
|
|
56
|
-
const connection = connections.find((c) => c.realm === realm);
|
|
57
|
-
if (!connection)
|
|
58
|
-
throw new SpongeError('repair-auth');
|
|
59
|
-
if (!connection.capture)
|
|
60
|
-
throw new SpongeError('connection-retired');
|
|
61
|
-
return connection.capture;
|
|
62
|
-
};
|
|
44
|
+
const resolveConnection = (connections) => (realm) => connectionFor(connections, realm);
|
|
63
45
|
/** add/refresh with --wait and run --drain: drain acquisition (one job when given), then queued indexes for run. */
|
|
64
46
|
async function drain(c, jobId, connections) {
|
|
65
47
|
const { store, args, signal } = c;
|
|
@@ -69,13 +51,11 @@ async function drain(c, jobId, connections) {
|
|
|
69
51
|
if (job && job.state !== 'queued')
|
|
70
52
|
throw jobFailure(store, jobId);
|
|
71
53
|
}
|
|
72
|
-
const runtime = await captureRuntime(c);
|
|
73
54
|
const results = await drainLibrary(store, {
|
|
74
55
|
asrRuntime: await optionalNative('asr', c.nativeRoot, c.version),
|
|
75
56
|
mediaRuntime: await optionalNative('media', c.nativeRoot, c.version),
|
|
76
57
|
language: args.flags.language,
|
|
77
58
|
forceLocal: args.flags['force-local'] === true,
|
|
78
|
-
runtime,
|
|
79
59
|
jobId,
|
|
80
60
|
signal,
|
|
81
61
|
allowPrivateNetwork: args.flags['allow-private-network'] === true,
|
|
@@ -175,14 +155,7 @@ export const commands = [
|
|
|
175
155
|
await installNative('media').catch((error) => {
|
|
176
156
|
media = codeOf(error);
|
|
177
157
|
});
|
|
178
|
-
|
|
179
|
-
process.stderr.write(`Capture setup: ${plan.downloadBytes} pinned artifact bytes, plus frozen dependency downloads. Temporary installation limit: ${plan.stagingBudgetBytes} bytes; retained runtime limit: ${plan.installationBudgetBytes} bytes; dependency installation deadline: ${plan.installationDeadlineMs / 60000} minutes. ${plan.requiredFreeBytes} free bytes required.\n`);
|
|
180
|
-
await provisionCapture({
|
|
181
|
-
runtimeRoot: join(c.store.root, 'runtime', 'capture'),
|
|
182
|
-
pins: defaultCapturePins,
|
|
183
|
-
signal: c.signal,
|
|
184
|
-
});
|
|
185
|
-
// Articles and PDFs run anywhere; the media helper is a native release asset and is best-effort.
|
|
158
|
+
// Articles and PDFs are built in; the media helper is a native release asset and is best-effort.
|
|
186
159
|
const gc = await setupRuntimeGc(c.store, c.version), ready = media === 'ready'
|
|
187
160
|
? 'Capture ready'
|
|
188
161
|
: media === 'unsupported'
|
|
@@ -438,13 +411,8 @@ export const commands = [
|
|
|
438
411
|
library: 'open',
|
|
439
412
|
handler: async (c) => {
|
|
440
413
|
const { store } = c;
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
await inspectCaptureRuntime(c.captureRoot, defaultCapturePins);
|
|
444
|
-
}
|
|
445
|
-
catch (error) {
|
|
446
|
-
capture = publicCode(error, 'runtime-missing');
|
|
447
|
-
}
|
|
414
|
+
// Article capture is built in; PDFs need the local Poppler tools.
|
|
415
|
+
const capture = 'ready', pdf = pdfToolsAvailable() ? 'ready' : 'runtime-missing';
|
|
448
416
|
const states = {};
|
|
449
417
|
for (const kind of ['asr', 'embedding', 'media']) {
|
|
450
418
|
try {
|
|
@@ -456,8 +424,8 @@ export const commands = [
|
|
|
456
424
|
}
|
|
457
425
|
}
|
|
458
426
|
const cleanup = store.queue.pendingCleanup(), worker = await workerDoctor(store, c.version), workerBroken = !['ready', 'not-installed', 'unsupported'].includes(worker.worker);
|
|
459
|
-
c.emit({ library: 'ready', capture, ...states, pendingCleanup: cleanup, worker }, `Library: ready\nCapture: ${capture}\nASR: ${states.asr}\nEmbedding: ${states.embedding}\nPending cleanup: ${cleanup}\nWorker: ${worker.worker}${Array.isArray(worker.drift) ? ` (${worker.drift.join(', ')})` : ''}${worker.repair ? `\n ${worker.repair}` : ''}`);
|
|
460
|
-
return cleanup ||
|
|
427
|
+
c.emit({ library: 'ready', capture, pdf, ...states, pendingCleanup: cleanup, worker }, `Library: ready\nCapture: ${capture}\nPDF: ${pdf === 'ready' ? 'ready' : 'Poppler not found (brew install poppler)'}\nASR: ${states.asr}\nEmbedding: ${states.embedding}\nPending cleanup: ${cleanup}\nWorker: ${worker.worker}${Array.isArray(worker.drift) ? ` (${worker.drift.join(', ')})` : ''}${worker.repair ? `\n ${worker.repair}` : ''}`);
|
|
428
|
+
return cleanup || workerBroken ? 4 : 0;
|
|
461
429
|
},
|
|
462
430
|
},
|
|
463
431
|
{
|
|
@@ -554,7 +522,6 @@ export const commands = [
|
|
|
554
522
|
signal: c.signal,
|
|
555
523
|
language: args.flags.language,
|
|
556
524
|
forceLocal: args.flags['force-local'] === true,
|
|
557
|
-
resolveCaptureRuntime: () => inspectCaptureRuntime(c.captureRoot, defaultCapturePins),
|
|
558
525
|
resolveConnection: resolveConnection(connections),
|
|
559
526
|
}, c.version));
|
|
560
527
|
return 0;
|
|
@@ -577,10 +544,8 @@ export const commands = [
|
|
|
577
544
|
const { store, args, signal } = c, dryRun = args.flags.apply !== true, offset = Number(args.flags.offset ?? 0);
|
|
578
545
|
if (args.positional[0] === 'jungle' && args.positional.length === 2) {
|
|
579
546
|
// A dry run reads Jungle and the library only; applying reverifies every capture with the released verifier.
|
|
580
|
-
const runtime = dryRun ? undefined : await captureRuntime(c);
|
|
581
547
|
const report = await jungleAction(store, args.positional[1], {
|
|
582
548
|
dryRun,
|
|
583
|
-
runtime,
|
|
584
549
|
limit: Number(args.flags.limit ?? 1000),
|
|
585
550
|
offset,
|
|
586
551
|
signal,
|
|
@@ -592,8 +557,7 @@ export const commands = [
|
|
|
592
557
|
? 3
|
|
593
558
|
: 0;
|
|
594
559
|
}
|
|
595
|
-
const
|
|
596
|
-
runtime,
|
|
560
|
+
const report = await importAction(store, args.positional[0], {
|
|
597
561
|
dryRun,
|
|
598
562
|
limit: Number(args.flags.limit ?? 20),
|
|
599
563
|
offset,
|
package/dist/src/cli/config.d.ts
CHANGED
|
@@ -5,5 +5,10 @@ export interface Connection {
|
|
|
5
5
|
/** Absent for a retired GhostGet account connection: it still names its items' realm, but cannot capture. */
|
|
6
6
|
capture?: CaptureConnection;
|
|
7
7
|
}
|
|
8
|
+
/**
|
|
9
|
+
* The capture connection for a source realm. Public and imported realms capture without one: an import realm
|
|
10
|
+
* records where an item came from, not an account, so refreshing an imported web page fetches it publicly.
|
|
11
|
+
*/
|
|
12
|
+
export declare function connectionFor(connections: Connection[], realm: string): CaptureConnection | undefined;
|
|
8
13
|
/** Local references only. Tokens, cookies and passwords never belong in this file. */
|
|
9
14
|
export declare function readConnections(root: string): Promise<Connection[]>;
|
package/dist/src/cli/config.js
CHANGED
|
@@ -3,6 +3,20 @@ import { readConfined } from '../acquisition/files.js';
|
|
|
3
3
|
import { join } from 'node:path';
|
|
4
4
|
import { sha256 } from '../util/hash.js';
|
|
5
5
|
import { SpongeError } from '../errors.js';
|
|
6
|
+
/**
|
|
7
|
+
* The capture connection for a source realm. Public and imported realms capture without one: an import realm
|
|
8
|
+
* records where an item came from, not an account, so refreshing an imported web page fetches it publicly.
|
|
9
|
+
*/
|
|
10
|
+
export function connectionFor(connections, realm) {
|
|
11
|
+
if (realm === 'public' || realm.startsWith('import:'))
|
|
12
|
+
return undefined;
|
|
13
|
+
const connection = connections.find((c) => c.realm === realm);
|
|
14
|
+
if (!connection)
|
|
15
|
+
throw new SpongeError('repair-auth');
|
|
16
|
+
if (!connection.capture)
|
|
17
|
+
throw new SpongeError('connection-retired');
|
|
18
|
+
return connection.capture;
|
|
19
|
+
}
|
|
6
20
|
/** Local references only. Tokens, cookies and passwords never belong in this file. */
|
|
7
21
|
export async function readConnections(root) {
|
|
8
22
|
const path = join(root, 'connections.json');
|
package/dist/src/cli/import.d.ts
CHANGED
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
import type { LibraryStore } from '../store/index.js';
|
|
2
|
-
import type { CaptureRuntime } from '../acquisition/index.js';
|
|
3
2
|
import { type ImportOptions, type ImportReport } from '../importers/apply.js';
|
|
4
3
|
import { type JungleImportReport } from '../importers/jungle.js';
|
|
5
4
|
/** A source manifest is explicit; default execution produces a private dry-run report. */
|
|
6
5
|
export declare function importAction(store: LibraryStore, path: string, options: ImportOptions): Promise<ImportReport>;
|
|
7
|
-
/** Jungle is read in place; only an applied import
|
|
6
|
+
/** Jungle is read in place; only an applied import reverifies each bundle. */
|
|
8
7
|
export declare function jungleAction(store: LibraryStore, path: string, options: {
|
|
9
8
|
dryRun: boolean;
|
|
10
|
-
runtime?: CaptureRuntime;
|
|
11
9
|
limit: number;
|
|
12
10
|
offset: number;
|
|
13
11
|
signal?: AbortSignal;
|
package/dist/src/cli/import.js
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
import { importManifest } from '../importers/apply.js';
|
|
2
|
-
import {
|
|
2
|
+
import { ownedBundleVerifier } from '../importers/inspect.js';
|
|
3
3
|
import { importJungle } from '../importers/jungle.js';
|
|
4
4
|
/** A source manifest is explicit; default execution produces a private dry-run report. */
|
|
5
5
|
export function importAction(store, path, options) {
|
|
6
6
|
return importManifest(store, path, options);
|
|
7
7
|
}
|
|
8
|
-
/** Jungle is read in place; only an applied import
|
|
8
|
+
/** Jungle is read in place; only an applied import reverifies each bundle. */
|
|
9
9
|
export function jungleAction(store, path, options) {
|
|
10
10
|
return importJungle(store, path, {
|
|
11
11
|
dryRun: options.dryRun,
|
|
12
12
|
limit: options.limit,
|
|
13
13
|
offset: options.offset,
|
|
14
14
|
...(options.signal ? { signal: options.signal } : {}),
|
|
15
|
-
...(options.
|
|
15
|
+
...(options.dryRun ? {} : { verify: ownedBundleVerifier(store.root, options.signal) }),
|
|
16
16
|
});
|
|
17
17
|
}
|
|
18
18
|
const tally = (m) => Object.entries(m)
|
package/dist/src/cli/main.js
CHANGED
|
@@ -6,7 +6,6 @@ import { terminalSafe } from './terminal.js';
|
|
|
6
6
|
import { parseArguments } from './arguments.js';
|
|
7
7
|
import { command, usageOf } from './commands.js';
|
|
8
8
|
import { openLibrary } from '../store/index.js';
|
|
9
|
-
import { defaultCapturePins } from '../acquisition/index.js';
|
|
10
9
|
import { failure } from '../acquisition/errors.js';
|
|
11
10
|
import { SpongeError, codeOf, publicCode, exitFor, hints, detailHints, debugChain, } from '../errors.js';
|
|
12
11
|
/** Failures print one categorical JSON line: the public code, its specific `reason` when narrower, and a fixed hint.
|
|
@@ -65,7 +64,6 @@ export async function main(argv, version) {
|
|
|
65
64
|
return await spec.handler({
|
|
66
65
|
...context,
|
|
67
66
|
store,
|
|
68
|
-
captureRoot: join(store.root, 'runtime', 'capture', defaultCapturePins.id),
|
|
69
67
|
nativeRoot: join(store.root, 'runtime', 'models'),
|
|
70
68
|
});
|
|
71
69
|
}
|
package/dist/src/cli/runtimes.js
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
import { defaultCapturePins } from '../acquisition/index.js';
|
|
2
1
|
import { loadNativeCatalog, nativeModelPackage } from '../models/catalog.js';
|
|
3
2
|
import { modelSetupPlan } from '../models/artifacts.js';
|
|
4
3
|
import { digestFingerprints } from '../models/digest.js';
|
|
5
4
|
import { collectRuntimes } from '../worker/runtime-gc.js';
|
|
6
5
|
import { codeOf } from '../errors.js';
|
|
7
|
-
/** References of this installed Sponge: its
|
|
6
|
+
/** References of this installed Sponge: its catalog's model packages and every digest pin. Capture is built in, so every capture runtime is superseded. */
|
|
8
7
|
async function installedReferences(version) {
|
|
9
8
|
let models = null;
|
|
10
9
|
try {
|
|
@@ -19,7 +18,7 @@ async function installedReferences(version) {
|
|
|
19
18
|
catch {
|
|
20
19
|
/* Without the catalog no model directory is provably superseded. */
|
|
21
20
|
}
|
|
22
|
-
return { captureIds: [
|
|
21
|
+
return { captureIds: [], models };
|
|
23
22
|
}
|
|
24
23
|
export async function runtimeGcAction(store, version, dryRun) {
|
|
25
24
|
return collectRuntimes(store, { ...(await installedReferences(version)), dryRun });
|
package/dist/src/cli/worker.d.ts
CHANGED
|
@@ -12,10 +12,7 @@ export declare function workerDoctor(store: LibraryStore, version: string, envir
|
|
|
12
12
|
lastExit?: unknown;
|
|
13
13
|
}>;
|
|
14
14
|
type DrainOptions = Parameters<typeof drainLibrary>[1];
|
|
15
|
-
export type ServiceDrainOptions =
|
|
16
|
-
runtime?: DrainOptions['runtime'];
|
|
17
|
-
resolveCaptureRuntime?: () => Promise<DrainOptions['runtime']>;
|
|
18
|
-
};
|
|
15
|
+
export type ServiceDrainOptions = DrainOptions;
|
|
19
16
|
/** Dispatch only work that claimNext can admit; delayed retries retain their due time. */
|
|
20
17
|
export declare function hasDueAcquisition(store: LibraryStore): boolean;
|
|
21
18
|
export declare function drainServiceQueues(store: LibraryStore, options: ServiceDrainOptions, version: string, signal: AbortSignal): Promise<readonly unknown[]>;
|
package/dist/src/cli/worker.js
CHANGED
|
@@ -60,16 +60,12 @@ export async function drainServiceQueues(store, options, version, signal) {
|
|
|
60
60
|
let acquisitionError;
|
|
61
61
|
if (hasDueAcquisition(store)) {
|
|
62
62
|
try {
|
|
63
|
-
const runtime = options.runtime ?? (await options.resolveCaptureRuntime?.());
|
|
64
|
-
if (!runtime)
|
|
65
|
-
throw new SpongeError('runtime-missing');
|
|
66
63
|
const models = join(store.root, 'runtime', 'models');
|
|
67
64
|
// Media jobs report a missing helper themselves; articles and PDFs still drain.
|
|
68
65
|
const asrRuntime = options.asrRuntime ?? (await optionalNative('asr', models, version));
|
|
69
66
|
const mediaRuntime = options.mediaRuntime ?? (await optionalNative('media', models, version));
|
|
70
67
|
results.push(...(await drainLibrary(store, {
|
|
71
68
|
...options,
|
|
72
|
-
runtime,
|
|
73
69
|
asrRuntime,
|
|
74
70
|
mediaRuntime,
|
|
75
71
|
signal,
|
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
import type { LibraryStore } from '../store/index.js';
|
|
2
2
|
import { type ImportEntry } from './manifest.js';
|
|
3
3
|
import { type BundleVerifier } from './inspect.js';
|
|
4
|
-
import type { CaptureRuntime } from '../acquisition/runtime.js';
|
|
5
4
|
export interface ImportOptions {
|
|
6
|
-
runtime: CaptureRuntime;
|
|
7
5
|
dryRun?: boolean;
|
|
8
6
|
limit?: number;
|
|
9
7
|
offset?: number;
|
|
@@ -5,7 +5,7 @@ import { localizeMarkdown } from '../app/markdown.js';
|
|
|
5
5
|
import { selectedMetadata } from '../metadata/article.js';
|
|
6
6
|
import { drainQueue } from '../worker/engine.js';
|
|
7
7
|
import { readImportManifest, importBundleRoot } from './manifest.js';
|
|
8
|
-
import { inspectImportBundle,
|
|
8
|
+
import { inspectImportBundle, ownedBundleVerifier } from './inspect.js';
|
|
9
9
|
import { SpongeError, publicCode } from '../errors.js';
|
|
10
10
|
const errorCode = (error) => {
|
|
11
11
|
const code = publicCode(error);
|
|
@@ -125,7 +125,7 @@ export async function importManifest(store, path, options) {
|
|
|
125
125
|
entries: [],
|
|
126
126
|
nextOffset: offset + limit < manifest.entries.length ? offset + limit : null,
|
|
127
127
|
};
|
|
128
|
-
const verify = options.verify ??
|
|
128
|
+
const verify = options.verify ?? ownedBundleVerifier(store.root, options.signal);
|
|
129
129
|
for (const entry of manifest.entries.slice(offset, offset + limit)) {
|
|
130
130
|
if (options.signal?.aborted)
|
|
131
131
|
throw new SpongeError('cancelled');
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import type { Asset, MetadataClaim } from '../../contracts/index.js';
|
|
2
|
-
import { type CaptureRuntime } from '../acquisition/runtime.js';
|
|
3
2
|
export interface ImportedBundle {
|
|
4
3
|
root: string;
|
|
5
4
|
kind: 'article' | 'pdf';
|
|
@@ -16,6 +15,7 @@ export interface ImportedBundle {
|
|
|
16
15
|
importHash: string;
|
|
17
16
|
}
|
|
18
17
|
export type BundleVerifier = (root: string, kind: 'article' | 'pdf') => Promise<unknown>;
|
|
19
|
-
|
|
18
|
+
/** Bundles are read by the owned child with networking denied; its report carries no document text. */
|
|
19
|
+
export declare function ownedBundleVerifier(libraryRoot: string, signal?: AbortSignal): BundleVerifier;
|
|
20
20
|
/** An import-time digest never upgrades a missing historical document digest. */
|
|
21
21
|
export declare function inspectImportBundle(root: string, verify: BundleVerifier): Promise<ImportedBundle>;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { writeFile } from 'node:fs/promises';
|
|
2
|
+
import { pathToFileURL } from 'node:url';
|
|
2
3
|
import { join } from 'node:path';
|
|
3
4
|
import { limits } from '../../contracts/index.js';
|
|
4
5
|
import { validateAsset } from '../../contracts/validate.js';
|
|
@@ -6,32 +7,32 @@ import { object, metadataFromMarkdown } from '../acquisition/normalize.js';
|
|
|
6
7
|
import { readConfined } from '../acquisition/files.js';
|
|
7
8
|
import { sha256 as hash } from '../util/hash.js';
|
|
8
9
|
import { withImportVerification } from './custody.js';
|
|
9
|
-
import {
|
|
10
|
+
import { workerArguments } from '../acquisition/child.js';
|
|
10
11
|
import { articleMetadata } from '../metadata/article.js';
|
|
11
12
|
import { bundleBytes } from '../app/bundle-files.js';
|
|
12
13
|
import { SpongeError } from '../errors.js';
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
if (result.cleanup !== 'settled')
|
|
27
|
-
throw new SpongeError('cleanup-required');
|
|
28
|
-
if (result.stopped)
|
|
29
|
-
throw new SpongeError(result.stopped);
|
|
30
|
-
if (result.code !== 0)
|
|
31
|
-
throw new SpongeError('unsupported');
|
|
32
|
-
return JSON.parse(result.stdout);
|
|
14
|
+
/** Bundles are read by the owned child with networking denied; its report carries no document text. */
|
|
15
|
+
export function ownedBundleVerifier(libraryRoot, signal) {
|
|
16
|
+
return (bundle, format) => withImportVerification(libraryRoot, async ({ scratchRoot, runProcess }) => {
|
|
17
|
+
const requestFile = join(scratchRoot, 'request.json'), request = { schemaVersion: 1, kind: 'verify', format, bundle };
|
|
18
|
+
await writeFile(requestFile, JSON.stringify(request), { mode: 0o600 });
|
|
19
|
+
const args = [...workerArguments(), requestFile];
|
|
20
|
+
const result = await runProcess(process.platform === 'darwin' ? '/usr/bin/sandbox-exec' : process.execPath, process.platform === 'darwin'
|
|
21
|
+
? ['-p', '(version 1) (allow default) (deny network*)', process.execPath, ...args]
|
|
22
|
+
: args, {
|
|
23
|
+
env: { PATH: '/usr/bin:/bin', HOME: scratchRoot, TMPDIR: scratchRoot, LANG: 'en_US.UTF-8' },
|
|
24
|
+
deadlineMs: 45000,
|
|
25
|
+
maxOutputBytes: 65536,
|
|
26
|
+
signal,
|
|
33
27
|
});
|
|
34
|
-
|
|
28
|
+
if (result.cleanup !== 'settled')
|
|
29
|
+
throw new SpongeError('cleanup-required');
|
|
30
|
+
if (result.stopped)
|
|
31
|
+
throw new SpongeError(result.stopped);
|
|
32
|
+
if (result.code !== 0)
|
|
33
|
+
throw new SpongeError('unsupported');
|
|
34
|
+
return JSON.parse(result.stdout);
|
|
35
|
+
});
|
|
35
36
|
}
|
|
36
37
|
function imageType(bytes) {
|
|
37
38
|
if (bytes[0] === 255 && bytes[1] === 216)
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import type { MetadataClaim, MetadataWarning } from '../../contracts/index.js';
|
|
2
|
-
/** Consume bounded observed Wordcell evidence without fetching linked JSON-LD contexts. */
|
|
3
2
|
export declare function articleMetadata(input: unknown, observedAt: string): MetadataClaim[];
|
|
4
3
|
export declare function selectedMetadata(claims: readonly MetadataClaim[], field: string): MetadataClaim | undefined;
|
|
5
4
|
export declare function articleMetadataWarnings(input: unknown): MetadataWarning[];
|
|
@@ -6,11 +6,12 @@ function object(v) {
|
|
|
6
6
|
throw new SpongeError('integrity');
|
|
7
7
|
return v;
|
|
8
8
|
}
|
|
9
|
-
/** Consume bounded observed
|
|
9
|
+
/** Consume bounded observed page evidence without fetching linked JSON-LD contexts. Imported Wordcell bundles keep their own version. */
|
|
10
|
+
const extractorVersions = new Set(['sponge-metadata-v1', 'wordcell-metadata-v1']);
|
|
10
11
|
export function articleMetadata(input, observedAt) {
|
|
11
12
|
const envelope = object(input);
|
|
12
13
|
articleMetadataWarnings(input);
|
|
13
|
-
if (envelope.schemaVersion !== 1 || envelope.extractorVersion
|
|
14
|
+
if (envelope.schemaVersion !== 1 || !extractorVersions.has(String(envelope.extractorVersion)))
|
|
14
15
|
throw new SpongeError('schema-newer');
|
|
15
16
|
if (!Array.isArray(envelope.claims) ||
|
|
16
17
|
envelope.claims.length > limits.metadataClaims ||
|
|
@@ -1,26 +1,17 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
type CaptureConnection = {
|
|
5
|
-
kind: 'cdp';
|
|
6
|
-
endpoint: string;
|
|
7
|
-
};
|
|
1
|
+
import type { runOwned } from '../acquisition/process.js';
|
|
2
|
+
import { type CaptureOutcome } from '../acquisition/child.js';
|
|
3
|
+
export type { CaptureOutcome } from '../acquisition/child.js';
|
|
8
4
|
export interface PdfCaptureRequest {
|
|
9
|
-
runtime: CaptureRuntime;
|
|
10
5
|
stagingRoot: string;
|
|
11
6
|
sourceUrl: string;
|
|
12
7
|
requestId: string;
|
|
13
|
-
connection?:
|
|
8
|
+
connection?: {
|
|
9
|
+
kind: 'cdp';
|
|
10
|
+
endpoint: string;
|
|
11
|
+
};
|
|
14
12
|
signal?: AbortSignal;
|
|
15
13
|
deadlineMs?: number;
|
|
16
14
|
allowPrivateNetwork?: boolean;
|
|
17
15
|
runProcess?: typeof runOwned;
|
|
18
16
|
}
|
|
19
|
-
export type CaptureOutcome = {
|
|
20
|
-
ok: true;
|
|
21
|
-
capture: CaptureResult;
|
|
22
|
-
bundleRoot: string;
|
|
23
|
-
dispose: () => Promise<void>;
|
|
24
|
-
} | ErrorEnvelope;
|
|
25
17
|
export declare function capturePdf(request: PdfCaptureRequest): Promise<CaptureOutcome>;
|
|
26
|
-
export {};
|