verifyhash 0.1.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/LICENSE +201 -0
- package/README.md +883 -0
- package/cli/abi/ContributionRegistry.json +881 -0
- package/cli/agent.js +2173 -0
- package/cli/anchor-artifact.js +853 -0
- package/cli/anchor.js +400 -0
- package/cli/claim.js +881 -0
- package/cli/core/agent-commit.js +448 -0
- package/cli/core/agent-session.js +598 -0
- package/cli/core/anchor-binding.js +663 -0
- package/cli/core/attestation.js +580 -0
- package/cli/core/evidence-plans.js +495 -0
- package/cli/core/fixtures/evidence-plans/baseline.json +19 -0
- package/cli/core/fulfill-intake.js +1082 -0
- package/cli/core/go-live-preflight.js +481 -0
- package/cli/core/license.js +534 -0
- package/cli/core/manifest.js +243 -0
- package/cli/core/packetseal.js +591 -0
- package/cli/core/registryArtifact.js +49 -0
- package/cli/core/revocation.js +539 -0
- package/cli/core/rfc3161.js +389 -0
- package/cli/core/timestamp.js +482 -0
- package/cli/core/trust-asof.js +479 -0
- package/cli/dataset.js +2950 -0
- package/cli/evidence.js +2227 -0
- package/cli/fulfill-webhook-http.js +438 -0
- package/cli/git.js +220 -0
- package/cli/hash.js +550 -0
- package/cli/identity.js +1072 -0
- package/cli/journal-cli.js +1110 -0
- package/cli/journal-log.js +454 -0
- package/cli/journal.js +334 -0
- package/cli/lineage.js +447 -0
- package/cli/list.js +287 -0
- package/cli/parcel.js +1509 -0
- package/cli/proof.js +578 -0
- package/cli/prove.js +300 -0
- package/cli/receipt.js +631 -0
- package/cli/registry.js +331 -0
- package/cli/reputation.js +344 -0
- package/cli/revocation.js +495 -0
- package/cli/serve-verify-http.js +298 -0
- package/cli/serve-verify.js +333 -0
- package/cli/show.js +339 -0
- package/cli/verify.js +383 -0
- package/cli/vh.js +3927 -0
- package/docs/ADOPT.md +183 -0
- package/docs/ADOPTION.json +11 -0
- package/docs/AGENTTRACE.md +247 -0
- package/docs/ANCHORING.md +167 -0
- package/docs/AUDIT.md +55 -0
- package/docs/CONFORMANCE.md +107 -0
- package/docs/DATALEDGER.md +638 -0
- package/docs/DECIDE.md +47 -0
- package/docs/DECISIONS-PENDING.md +27 -0
- package/docs/DEPLOY-PUBLIC-SITE.md +301 -0
- package/docs/ENGINE-LEDGER.json +12 -0
- package/docs/EVIDENCE.md +519 -0
- package/docs/GO-LIVE.md +66 -0
- package/docs/IDENTITY.md +123 -0
- package/docs/INDEPENDENT-VERIFICATION.md +377 -0
- package/docs/INTEGRITY-JOURNAL.md +337 -0
- package/docs/KEY-LIFECYCLE.md +179 -0
- package/docs/LICENSING.md +46 -0
- package/docs/LINEAGE.md +307 -0
- package/docs/LOOP-AUDIT-2026-07-03.json +580 -0
- package/docs/LOOP-HARDENING-PLAN.md +44 -0
- package/docs/MERKLE-LEAVES.md +113 -0
- package/docs/METRICS.jsonl +31 -0
- package/docs/MORNING.md +204 -0
- package/docs/PILOT.md +444 -0
- package/docs/PROOFPARCEL.md +227 -0
- package/docs/PROOFS.md +262 -0
- package/docs/RECEIPTS.md +341 -0
- package/docs/REPUTATION.md +158 -0
- package/docs/SDK.md +301 -0
- package/docs/STRATEGY-ARCHIVE.md +5055 -0
- package/docs/SUPERVISOR-RUNBOOK.md +52 -0
- package/docs/TRUST-BOUNDARIES.md +335 -0
- package/docs/TRUSTLEDGER.md +1976 -0
- package/docs/USAGE-BUDGET.json +121 -0
- package/docs/VERIFY-SERVICE.md +168 -0
- package/index.js +160 -0
- package/package.json +41 -0
- package/trustledger/build-standalone.js +796 -0
- package/trustledger/cli.js +3179 -0
- package/trustledger/close.js +391 -0
- package/trustledger/corpus.js +159 -0
- package/trustledger/dist/BUILD-PROVENANCE.json +99 -0
- package/trustledger/dist/trustledger-standalone.html +6197 -0
- package/trustledger/dist/trustledger-standalone.html.sha256 +1 -0
- package/trustledger/door-core.js +442 -0
- package/trustledger/fixtures/bank.csv +7 -0
- package/trustledger/fixtures/bank.malformed.csv +3 -0
- package/trustledger/fixtures/bank.noalias.csv +5 -0
- package/trustledger/fixtures/bank.ofx +34 -0
- package/trustledger/fixtures/bank.real.csv +5 -0
- package/trustledger/fixtures/corpus/_shared/prior-close.json +22 -0
- package/trustledger/fixtures/corpus/bank-book-mismatch--benign-twin/inputs.json +14 -0
- package/trustledger/fixtures/corpus/bank-book-mismatch--benign-twin/meta.json +7 -0
- package/trustledger/fixtures/corpus/bank-book-mismatch--out-of-trust/inputs.json +14 -0
- package/trustledger/fixtures/corpus/bank-book-mismatch--out-of-trust/meta.json +7 -0
- package/trustledger/fixtures/corpus/continuity-break--benign-twin/inputs.json +15 -0
- package/trustledger/fixtures/corpus/continuity-break--benign-twin/meta.json +7 -0
- package/trustledger/fixtures/corpus/continuity-break--out-of-trust/inputs.json +15 -0
- package/trustledger/fixtures/corpus/continuity-break--out-of-trust/meta.json +7 -0
- package/trustledger/fixtures/corpus/negative-tenant-ledger--benign-twin/inputs.json +13 -0
- package/trustledger/fixtures/corpus/negative-tenant-ledger--benign-twin/meta.json +7 -0
- package/trustledger/fixtures/corpus/negative-tenant-ledger--out-of-trust/inputs.json +13 -0
- package/trustledger/fixtures/corpus/negative-tenant-ledger--out-of-trust/meta.json +7 -0
- package/trustledger/fixtures/corpus/owner-overdraw--benign-twin/inputs.json +15 -0
- package/trustledger/fixtures/corpus/owner-overdraw--benign-twin/meta.json +7 -0
- package/trustledger/fixtures/corpus/owner-overdraw--out-of-trust/inputs.json +15 -0
- package/trustledger/fixtures/corpus/owner-overdraw--out-of-trust/meta.json +7 -0
- package/trustledger/fixtures/corpus/security-deposit-segregation--benign-twin/inputs.json +16 -0
- package/trustledger/fixtures/corpus/security-deposit-segregation--benign-twin/meta.json +7 -0
- package/trustledger/fixtures/corpus/security-deposit-segregation--out-of-trust/inputs.json +13 -0
- package/trustledger/fixtures/corpus/security-deposit-segregation--out-of-trust/meta.json +7 -0
- package/trustledger/fixtures/corpus/subledger-out-of-balance--benign-twin/inputs.json +13 -0
- package/trustledger/fixtures/corpus/subledger-out-of-balance--benign-twin/meta.json +7 -0
- package/trustledger/fixtures/corpus/subledger-out-of-balance--out-of-trust/inputs.json +13 -0
- package/trustledger/fixtures/corpus/subledger-out-of-balance--out-of-trust/meta.json +7 -0
- package/trustledger/fixtures/e2e/bank.aliased.csv +4 -0
- package/trustledger/fixtures/e2e/bank.csv +4 -0
- package/trustledger/fixtures/e2e/bank.nsf.csv +4 -0
- package/trustledger/fixtures/e2e/quickbooks.csv +6 -0
- package/trustledger/fixtures/e2e/quickbooks.nsf.csv +8 -0
- package/trustledger/fixtures/e2e/rentroll.csv +6 -0
- package/trustledger/fixtures/e2e/rentroll.nsf.csv +8 -0
- package/trustledger/fixtures/e2e/rentroll.short.csv +5 -0
- package/trustledger/fixtures/plans/baseline.json +25 -0
- package/trustledger/fixtures/plans/price-binding.example.json +27 -0
- package/trustledger/fixtures/policy/ambiguous-deposit-example.json +12 -0
- package/trustledger/fixtures/policy/baseline.json +19 -0
- package/trustledger/fixtures/policy/ca-example.json +12 -0
- package/trustledger/fixtures/policy/negative-tenant-ledger-example.json +12 -0
- package/trustledger/fixtures/policy/owner-overdraw-example.json +12 -0
- package/trustledger/fixtures/quickbooks.csv +7 -0
- package/trustledger/fixtures/quickbooks.real.csv +5 -0
- package/trustledger/fixtures/rentroll.csv +6 -0
- package/trustledger/fixtures/rentroll.real.csv +4 -0
- package/trustledger/ingest.js +1163 -0
- package/trustledger/lib/policy-bundled-loader.js +44 -0
- package/trustledger/lib/sha256-vendored.js +227 -0
- package/trustledger/license.js +563 -0
- package/trustledger/match.js +551 -0
- package/trustledger/plans.js +551 -0
- package/trustledger/policy.js +398 -0
- package/trustledger/public/index.html +512 -0
- package/trustledger/reconcile.js +1486 -0
- package/trustledger/report.js +887 -0
- package/trustledger/seal.js +854 -0
- package/trustledger/server.js +391 -0
- package/trustledger/valueproof.js +350 -0
package/cli/list.js
ADDED
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
// `vh list [--contract a] [--rpc u] [filters]` — enumerate the registry, read-only.
|
|
4
|
+
//
|
|
5
|
+
// This is the discovery + audit counterpart to `vh verify`: where verify answers "is THIS content
|
|
6
|
+
// anchored?", list answers "WHAT is in the registry?". It reads `total()` and pages through the
|
|
7
|
+
// contract's `getRecords(start, count)` view, printing one block per record (or a JSON array under
|
|
8
|
+
// `--json`).
|
|
9
|
+
//
|
|
10
|
+
// Read-only by construction: it takes a PROVIDER only, never a signer and never a key. Enumerating a
|
|
11
|
+
// public, immutable registry must never require the ability to write to it.
|
|
12
|
+
//
|
|
13
|
+
// Trust posture (mirrors docs/TRUST-BOUNDARIES.md): the per-record `uri` is an UNTRUSTED hint the
|
|
14
|
+
// contract never validated, and `contributor` only means "proven first claimant" when `authorBound`
|
|
15
|
+
// is true — otherwise it is merely "first anchorer". Human output always leads with that caveat so a
|
|
16
|
+
// browser of the list does not over-trust either field.
|
|
17
|
+
|
|
18
|
+
const ARTIFACT = require("./core/registryArtifact");
|
|
19
|
+
const ABI = ARTIFACT.abi;
|
|
20
|
+
|
|
21
|
+
// Reuse the lineage-root predicate from `show` so `list` and `show` never disagree about what a
|
|
22
|
+
// "root" (parent == bytes32(0)) is. T-10.1.
|
|
23
|
+
const { isRoot } = require("./show");
|
|
24
|
+
const {
|
|
25
|
+
assertRegistry,
|
|
26
|
+
formatRegistryLine,
|
|
27
|
+
formatSkippedLine,
|
|
28
|
+
jsonRegistryBlock,
|
|
29
|
+
jsonSkippedBlock,
|
|
30
|
+
} = require("./registry");
|
|
31
|
+
|
|
32
|
+
// Default page size for walking getRecords(). The contract clamps a window to what exists, so this is
|
|
33
|
+
// purely a request-batching knob; it never affects which records come back.
|
|
34
|
+
const DEFAULT_PAGE = 100;
|
|
35
|
+
|
|
36
|
+
// The one-line trust caveat that leads every human-readable run. Kept consistent with the
|
|
37
|
+
// `uri`/`contributor` rows in docs/TRUST-BOUNDARIES.md so a reader of the list does not over-trust
|
|
38
|
+
// the off-chain pointer or the recorded address.
|
|
39
|
+
const TRUST_CAVEAT =
|
|
40
|
+
"NOTE: `uri` is an UNTRUSTED hint (never fetched/validated — re-fetch + re-hash yourself); " +
|
|
41
|
+
"`contributor` only means proven authorship when authorBound is true (commit-reveal), " +
|
|
42
|
+
"otherwise it is merely the first anchorer.";
|
|
43
|
+
|
|
44
|
+
// The two attribution strings, reused verbatim from cli/verify.js so list and verify never drift.
|
|
45
|
+
const ATTRIBUTION_PROVEN = "proven first claimant (commit-reveal)";
|
|
46
|
+
const ATTRIBUTION_ANCHOR_ONLY = "first anchorer only — NOT authorship";
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Read every record from the registry in insertion order by paging through getRecords().
|
|
50
|
+
* Read-only: uses only the provider. Returns a plain array of normalized record objects with their
|
|
51
|
+
* insertion `index`, so callers can filter/slice client-side without re-reading the chain.
|
|
52
|
+
*
|
|
53
|
+
* @param {object} contract an ethers v6 Contract bound to a provider
|
|
54
|
+
* @param {number} [pageSize]
|
|
55
|
+
* @returns {Promise<Array<{
|
|
56
|
+
* index:number, contentHash:string, contributor:string, authorBound:boolean,
|
|
57
|
+
* timestamp:bigint, blockNumber:bigint, uri:string, parent:string
|
|
58
|
+
* }>>}
|
|
59
|
+
*/
|
|
60
|
+
async function readAllRecords(contract, pageSize = DEFAULT_PAGE) {
|
|
61
|
+
const total = Number(await contract.total());
|
|
62
|
+
const out = [];
|
|
63
|
+
for (let start = 0; start < total; start += pageSize) {
|
|
64
|
+
const [contentHashes, records] = await contract.getRecords(start, pageSize);
|
|
65
|
+
// Defensive: the contract clamps, so a window past the end returns empty and we stop.
|
|
66
|
+
if (contentHashes.length === 0) break;
|
|
67
|
+
for (let i = 0; i < contentHashes.length; i++) {
|
|
68
|
+
const r = records[i];
|
|
69
|
+
out.push({
|
|
70
|
+
index: start + i,
|
|
71
|
+
contentHash: contentHashes[i],
|
|
72
|
+
contributor: r.contributor,
|
|
73
|
+
authorBound: Boolean(r.authorBound),
|
|
74
|
+
timestamp: BigInt(r.timestamp),
|
|
75
|
+
blockNumber: BigInt(r.blockNumber),
|
|
76
|
+
uri: r.uri,
|
|
77
|
+
// The immutable lineage edge (T-10.1). Normalized to a lowercase 0x string; a root reads back
|
|
78
|
+
// as the 32-byte zero hash (isRoot() flags it for the JSON/human shapes below).
|
|
79
|
+
parent: String(r.parent).toLowerCase(),
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return out;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Apply the client-side filters to an in-order record array. All filters are optional and combine
|
|
88
|
+
* (logical AND); offset/limit page the *filtered* result so `vh list --contributor X --limit 5`
|
|
89
|
+
* means "the first 5 of X's records", which is what a reader expects.
|
|
90
|
+
*
|
|
91
|
+
* @param {Array} records normalized records from readAllRecords (insertion order preserved)
|
|
92
|
+
* @param {object} filters
|
|
93
|
+
* @param {string} [filters.contributor] lowercase-compared address; keep only this contributor
|
|
94
|
+
* @param {boolean} [filters.authorBound] if true, keep only authorBound (commit-reveal) records
|
|
95
|
+
* @param {number} [filters.offset] skip this many of the filtered records (default 0)
|
|
96
|
+
* @param {number} [filters.limit] keep at most this many after the offset (default: all)
|
|
97
|
+
* @returns {Array} the filtered + windowed records (insertion order preserved)
|
|
98
|
+
*/
|
|
99
|
+
function applyFilters(records, filters = {}) {
|
|
100
|
+
let rows = records;
|
|
101
|
+
if (filters.contributor) {
|
|
102
|
+
const want = String(filters.contributor).toLowerCase();
|
|
103
|
+
rows = rows.filter((r) => String(r.contributor).toLowerCase() === want);
|
|
104
|
+
}
|
|
105
|
+
if (filters.authorBound) {
|
|
106
|
+
rows = rows.filter((r) => r.authorBound === true);
|
|
107
|
+
}
|
|
108
|
+
const offset = filters.offset || 0;
|
|
109
|
+
const start = offset;
|
|
110
|
+
const end = filters.limit == null ? rows.length : start + filters.limit;
|
|
111
|
+
return rows.slice(start, end);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/** The human attribution phrase for a record, reusing verify.js wording exactly. */
|
|
115
|
+
function attributionFor(authorBound) {
|
|
116
|
+
return authorBound ? ATTRIBUTION_PROVEN : ATTRIBUTION_ANCHOR_ONLY;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/** Format a unix-seconds bigint as an ISO-8601 UTC string for human display. */
|
|
120
|
+
function isoFromUnix(unixSeconds) {
|
|
121
|
+
try {
|
|
122
|
+
return new Date(Number(unixSeconds) * 1000).toISOString();
|
|
123
|
+
} catch (_) {
|
|
124
|
+
return "(unparseable)";
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Render one record as the human-readable block printed per row. Includes the acceptance fields:
|
|
130
|
+
* index, contentHash, contributor, attribution strength, timestamp (+ISO), blockNumber, uri.
|
|
131
|
+
*/
|
|
132
|
+
function formatRecord(r) {
|
|
133
|
+
// `parent` (T-10.1): a root (0x0) shows "(none) — lineage root"; a parented record shows the
|
|
134
|
+
// predecessor hash. The edge is only a CLAIM (see the trust caveat), so it is reported, not trusted.
|
|
135
|
+
const parentLine = isRoot(r.parent)
|
|
136
|
+
? " parent: (none) — lineage root"
|
|
137
|
+
: ` parent: ${r.parent}`;
|
|
138
|
+
return [
|
|
139
|
+
`[${r.index}] ${r.contentHash}`,
|
|
140
|
+
` contributor: ${r.contributor}`,
|
|
141
|
+
` attribution: ${attributionFor(r.authorBound)}`,
|
|
142
|
+
` timestamp: ${r.timestamp} (${isoFromUnix(r.timestamp)})`,
|
|
143
|
+
` blockNumber: ${r.blockNumber}`,
|
|
144
|
+
` uri: ${r.uri ? r.uri : "(none)"}`,
|
|
145
|
+
parentLine,
|
|
146
|
+
].join("\n");
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Shape a record for `--json`: BigInts become Numbers (unix seconds / block heights fit safely) and
|
|
151
|
+
* the attribution phrase is included so a machine consumer gets the same semantics as the human block.
|
|
152
|
+
*/
|
|
153
|
+
function jsonRecord(r) {
|
|
154
|
+
const root = isRoot(r.parent);
|
|
155
|
+
return {
|
|
156
|
+
index: r.index,
|
|
157
|
+
contentHash: r.contentHash,
|
|
158
|
+
contributor: r.contributor,
|
|
159
|
+
authorBound: r.authorBound,
|
|
160
|
+
attribution: attributionFor(r.authorBound),
|
|
161
|
+
timestamp: Number(r.timestamp),
|
|
162
|
+
timestampISO: isoFromUnix(r.timestamp),
|
|
163
|
+
blockNumber: Number(r.blockNumber),
|
|
164
|
+
uri: r.uri ? r.uri : null,
|
|
165
|
+
// Lineage edge (T-10.1): a root serializes parent:null + isRoot:true (distinguishable from a
|
|
166
|
+
// missing key); a parented record carries the predecessor hash + isRoot:false. So an indexer can
|
|
167
|
+
// reconstruct the full edge set from `vh list --json` alone, mirroring the on-chain Linked logs.
|
|
168
|
+
parent: root ? null : r.parent,
|
|
169
|
+
isRoot: root,
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Enumerate the registry, read-only. Reads `total`, pages through `getRecords`, applies the
|
|
175
|
+
* client-side filters, then emits either a human block (led by the trust caveat) per record or a
|
|
176
|
+
* machine-readable JSON array (`--json`). Requires a provider; NEVER a signer or key.
|
|
177
|
+
*
|
|
178
|
+
* @param {object} opts
|
|
179
|
+
* @param {string} opts.contractAddress deployed ContributionRegistry address to read from
|
|
180
|
+
* @param {object} opts.provider ethers v6 Provider (read-only RPC connection)
|
|
181
|
+
* @param {object} [opts.filters] { contributor, authorBound, offset, limit } (see applyFilters)
|
|
182
|
+
* @param {boolean} [opts.json] emit a JSON array instead of the human block
|
|
183
|
+
* @param {object} [opts.ethers] ethers v6 module (defaults to the bundled one)
|
|
184
|
+
* @param {(s:string)=>void} [opts.log] sink for output (defaults to process.stdout)
|
|
185
|
+
* @returns {Promise<{records: Array, total: number, shown: number, json: boolean}>}
|
|
186
|
+
* `records` is the normalized + filtered list actually emitted (the JSON-shaped objects).
|
|
187
|
+
*/
|
|
188
|
+
async function runList(opts) {
|
|
189
|
+
const ethersLib = opts.ethers || require("ethers");
|
|
190
|
+
const log = opts.log || ((s) => process.stdout.write(s));
|
|
191
|
+
|
|
192
|
+
const { contractAddress, provider } = opts;
|
|
193
|
+
if (!contractAddress) {
|
|
194
|
+
throw new Error(
|
|
195
|
+
"no contract address: pass --contract <address> or set VH_CONTRACT in the environment"
|
|
196
|
+
);
|
|
197
|
+
}
|
|
198
|
+
if (!ethersLib.isAddress(contractAddress)) {
|
|
199
|
+
throw new Error(`invalid contract address: ${contractAddress}`);
|
|
200
|
+
}
|
|
201
|
+
if (!provider) {
|
|
202
|
+
throw new Error("no provider: pass --rpc <url> or set VH_RPC_URL / AMOY_RPC_URL");
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
const filters = opts.filters || {};
|
|
206
|
+
// Validate a --contributor address up front so a typo'd address hard-errors rather than silently
|
|
207
|
+
// matching nothing (which would look like an empty registry).
|
|
208
|
+
if (filters.contributor && !ethersLib.isAddress(filters.contributor)) {
|
|
209
|
+
throw new Error(`invalid --contributor address: ${filters.contributor}`);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// T-11.2: authenticate the registry BEFORE enumerating it — no records are reported until we have
|
|
213
|
+
// confirmed there is a real verifyhash ContributionRegistry at this address (unless the caller
|
|
214
|
+
// explicitly, loudly opts out with skipIdentityCheck for a known not-yet-deployed/local-dev target).
|
|
215
|
+
let registryAuth = null;
|
|
216
|
+
if (!opts.skipIdentityCheck) {
|
|
217
|
+
registryAuth = await assertRegistry({ provider, contractAddress, ethers: ethersLib });
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
const contract = new ethersLib.Contract(
|
|
221
|
+
ethersLib.getAddress(contractAddress),
|
|
222
|
+
ABI,
|
|
223
|
+
provider
|
|
224
|
+
);
|
|
225
|
+
|
|
226
|
+
const all = await readAllRecords(contract);
|
|
227
|
+
const filtered = applyFilters(all, {
|
|
228
|
+
contributor: filters.contributor,
|
|
229
|
+
authorBound: filters.authorBound,
|
|
230
|
+
offset: filters.offset,
|
|
231
|
+
limit: filters.limit,
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
const jsonRows = filtered.map(jsonRecord);
|
|
235
|
+
|
|
236
|
+
// T-11.2: the machine-readable registry block — the same identity a UI/indexer can depend on to know
|
|
237
|
+
// the list was read from an authenticated registry (or that the check was skipped).
|
|
238
|
+
const registryBlock = opts.skipIdentityCheck
|
|
239
|
+
? jsonSkippedBlock()
|
|
240
|
+
: registryAuth
|
|
241
|
+
? jsonRegistryBlock(registryAuth)
|
|
242
|
+
: null;
|
|
243
|
+
|
|
244
|
+
if (opts.json) {
|
|
245
|
+
// Machine-readable ENVELOPE (T-11.2): `{ registry, records }`. The `registry` block proves the
|
|
246
|
+
// records came from an authenticated registry; `records` is the array a consumer iterates. (Before
|
|
247
|
+
// T-11.2 this was a bare top-level array; the envelope is the documented contract now.)
|
|
248
|
+
log(JSON.stringify({ registry: registryBlock, records: jsonRows }, null, 2) + "\n");
|
|
249
|
+
} else {
|
|
250
|
+
// Human-readable: lead with the trust caveat, then the registry-authentication confirmation (or the
|
|
251
|
+
// loud skip warning), then one block per record.
|
|
252
|
+
log(TRUST_CAVEAT + "\n\n");
|
|
253
|
+
if (opts.skipIdentityCheck) {
|
|
254
|
+
log(formatSkippedLine() + "\n\n");
|
|
255
|
+
} else if (registryAuth) {
|
|
256
|
+
log(formatRegistryLine(registryAuth) + "\n\n");
|
|
257
|
+
}
|
|
258
|
+
if (filtered.length === 0) {
|
|
259
|
+
log("no records\n");
|
|
260
|
+
} else {
|
|
261
|
+
log(filtered.map(formatRecord).join("\n\n") + "\n");
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
return {
|
|
266
|
+
records: jsonRows,
|
|
267
|
+
registry: registryBlock,
|
|
268
|
+
total: all.length,
|
|
269
|
+
shown: filtered.length,
|
|
270
|
+
json: Boolean(opts.json),
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
module.exports = {
|
|
275
|
+
runList,
|
|
276
|
+
readAllRecords,
|
|
277
|
+
applyFilters,
|
|
278
|
+
formatRecord,
|
|
279
|
+
jsonRecord,
|
|
280
|
+
attributionFor,
|
|
281
|
+
isoFromUnix,
|
|
282
|
+
TRUST_CAVEAT,
|
|
283
|
+
ATTRIBUTION_PROVEN,
|
|
284
|
+
ATTRIBUTION_ANCHOR_ONLY,
|
|
285
|
+
DEFAULT_PAGE,
|
|
286
|
+
ABI,
|
|
287
|
+
};
|