@lapxo/bound 0.1.5 → 0.1.7
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 -8
- package/TARGET.bound +19 -18
- package/dist/cli/accept.d.ts +5 -0
- package/dist/cli/accept.js +57 -8
- package/dist/cli/act-result.d.ts +3 -0
- package/dist/cli/act-result.js +48 -0
- package/dist/cli/owner.js +3 -2
- package/dist/cli/pass.js +6 -4
- package/dist/cli/place.d.ts +1 -1
- package/dist/cli/place.js +1 -1
- package/dist/cli/release-result.d.ts +5 -0
- package/dist/cli/release-result.js +80 -0
- package/dist/cli/standing-result.d.ts +5 -0
- package/dist/cli/standing-result.js +39 -0
- package/dist/cli/verb.js +33 -2
- package/dist/fold/admission-history.d.ts +6 -0
- package/dist/fold/admission-history.js +22 -0
- package/dist/fold/closed.js +9 -3
- package/dist/fold/digests.js +13 -9
- package/dist/fold/kept.d.ts +1 -1
- package/dist/fold/kept.js +21 -6
- package/dist/fold/keys.js +16 -9
- package/dist/fold/observed.js +8 -7
- package/dist/fold/places.d.ts +5 -1
- package/dist/fold/places.js +19 -3
- package/dist/fold/readers.js +2 -1
- package/dist/fold/release-policy.js +0 -2
- package/dist/fold/signed.d.ts +5 -0
- package/dist/fold/signed.js +19 -15
- package/dist/fold/zoom.js +5 -4
- package/dist/host/capsule.d.ts +2 -1
- package/dist/host/capsule.js +8 -6
- package/dist/host/cell-receipts.js +7 -3
- package/dist/host/io.d.ts +1 -1
- package/dist/host/io.js +1 -1
- package/dist/host/ports/ingress.js +4 -28
- package/dist/host/ports/local-act.d.ts +34 -0
- package/dist/host/ports/local-act.js +100 -0
- package/dist/host/ports/publication.d.ts +10 -0
- package/dist/host/ports/publication.js +56 -0
- package/dist/host/reading-closures.d.ts +3 -0
- package/dist/host/reading-closures.js +5 -4
- package/dist/host/selected-capsules.js +4 -4
- package/dist/host/selected-topos.d.ts +4 -1
- package/dist/host/selected-topos.js +16 -1
- package/dist/land/ledger.d.ts +1 -0
- package/dist/land/ledger.js +21 -2
- package/dist/land/owned.d.ts +1 -1
- package/dist/land/owned.js +5 -4
- package/dist/land/standing-pin.d.ts +3 -0
- package/dist/land/standing-pin.js +49 -0
- package/dist/observe/files.js +12 -2
- package/docs/contracts/cli.md +16 -0
- package/docs/contracts/evidence-view.md +4 -0
- package/package.json +2 -2
- package/receipts.bound +593 -314
package/dist/cli/accept.js
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
|
+
import { commitLocalAct, intactLocalAct, committedLocalActs } from "../host/ports/local-act.js";
|
|
1
2
|
import { withoutEffects } from "../host/read-only.js";
|
|
2
|
-
import {
|
|
3
|
+
import { admittedLine } from "../host/selected-topos.js";
|
|
3
4
|
import { timing } from "../host/timing.js";
|
|
4
5
|
import { admittedObjects } from "../host/objects.js";
|
|
5
6
|
import { existsSync, rmdirSync, rmSync } from 'node:fs';
|
|
6
7
|
import { dirname, join, basename } from 'node:path';
|
|
7
8
|
import { closeReceipts, instrumentKeep, instrumentKept } from "../fold/closed.js";
|
|
8
9
|
import { bytesDigest, fullDigest, instrumentOf, signaturesOf } from "../fold/digests.js";
|
|
9
|
-
import { canonical,
|
|
10
|
+
import { canonical, LOCK, parse, actIdentity, actResultProfile } from '@lapxo/topos/wire';
|
|
10
11
|
import { fieldOf, isConfig, foldClaims, sayingOf, selfName } from "../fold/claims.js";
|
|
11
12
|
import { isReaderLock } from "../fold/observed.js";
|
|
12
13
|
import { authorityFor, publicKeyOf, rootSigner, writerFor } from "../fold/signers.js";
|
|
@@ -35,6 +36,7 @@ import { outsideTheirCapsule } from "../fold/own.js";
|
|
|
35
36
|
import { lateFacts } from "../judge/late.js";
|
|
36
37
|
import { paidOf } from "../fold/paid.js";
|
|
37
38
|
import { aliasPinned } from "../land/alias.js";
|
|
39
|
+
import { admitStandingPins } from "../land/standing-pin.js";
|
|
38
40
|
import { coordinatesWithdrawn } from "../land/leaving.js";
|
|
39
41
|
import { ownerOf, epochOf, localActLines, forksItself, forksJoinedBy, renderInto, sideOf, signedDemands, signedOwnerLines } from "./owner.js";
|
|
40
42
|
import { claimLinesIn, entryOf, namedPlaces, placesOfFile, positionalsOf, valueOf } from "./args.js";
|
|
@@ -110,6 +112,16 @@ function refusesAdmission(root, signed, standing) {
|
|
|
110
112
|
}
|
|
111
113
|
return false;
|
|
112
114
|
}
|
|
115
|
+
function refusesStandingPins(root, lines) {
|
|
116
|
+
try {
|
|
117
|
+
admitStandingPins(root, lines);
|
|
118
|
+
return false;
|
|
119
|
+
}
|
|
120
|
+
catch (error) {
|
|
121
|
+
process.stderr.write(`${selfName()}: ${error instanceof Error ? error.message : String(error)}\n`);
|
|
122
|
+
return true;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
113
125
|
/** A batch signed as accept signs it: at the next epoch, by the key it names, each line read by the wire it would land under. */
|
|
114
126
|
export async function signedBatch(root, args, files) {
|
|
115
127
|
const byFile = files.map((file) => [placesOfFile(file), claimLinesIn(file)]);
|
|
@@ -134,7 +146,9 @@ export async function signedBatch(root, args, files) {
|
|
|
134
146
|
process.stderr.write(`${selfName()}: REFUSE·place nothing landed · ${stray.join(', ')} is no place of the tree, and no line of the root founds it: --place names the own lock a line lands in\n`);
|
|
135
147
|
return { kind: 'exit', code: 2 };
|
|
136
148
|
}
|
|
137
|
-
const
|
|
149
|
+
const local = localActLines(root, destinations);
|
|
150
|
+
const base = { digest: fullDigest(store, local), places: destinations };
|
|
151
|
+
const epoch = epochOf(local) + 1;
|
|
138
152
|
const stale = proposed.filter((line) => fieldOf(line, 'sig')).flatMap((line) => ((by, key) => (key === undefined || !verifiesOwnerLine(line, key, signaturesOf(store).admitted)
|
|
139
153
|
? [`${fieldOf(line, 'scope')} · the signature does not hold for ${by || '∅'}`]
|
|
140
154
|
: []))(fieldOf(line, 'by'), publicKeyOf(root, fieldOf(line, 'by'))));
|
|
@@ -203,12 +217,14 @@ export async function signedBatch(root, args, files) {
|
|
|
203
217
|
return got.kind === 'fact' ? [got.value.fields] : [];
|
|
204
218
|
});
|
|
205
219
|
for (const line of signed) {
|
|
206
|
-
const got =
|
|
220
|
+
const got = admittedLine(line, wireLines, Number(fieldOf(line, 'epoch')), foldClaims(signedOwnerLines(store)).standing.filter(one => fieldOf(one, 'scope').startsWith('uses/') && fieldOf(one, 'value') !== 'withdraw'));
|
|
207
221
|
if (got.kind !== 'fact') {
|
|
208
222
|
process.stderr.write(`${selfName()}: REFUSE·wire ${fieldOf(line, 'scope')} · ${got.why} · nothing landed\n`);
|
|
209
223
|
return { kind: 'exit', code: 1 };
|
|
210
224
|
}
|
|
211
225
|
}
|
|
226
|
+
if (refusesStandingPins(root, signed))
|
|
227
|
+
return { kind: 'exit', code: 1 };
|
|
212
228
|
const taken = retractedTogether(signed);
|
|
213
229
|
if (taken.length) {
|
|
214
230
|
for (const pair of taken) {
|
|
@@ -249,7 +265,7 @@ export async function signedBatch(root, args, files) {
|
|
|
249
265
|
process.stderr.write(`${selfName()}: ${error instanceof Error ? error.message : String(error)} · nothing landed\n`);
|
|
250
266
|
return { kind: 'exit', code: 1 };
|
|
251
267
|
}
|
|
252
|
-
return { kind: 'signed', signed, epoch, ...(drafted.length ? { keyId } : {}) };
|
|
268
|
+
return { kind: 'signed', signed, epoch, base, ...(drafted.length ? { keyId } : {}) };
|
|
253
269
|
}
|
|
254
270
|
/**
|
|
255
271
|
* accept is one act under the descent of its lines. Every place, then the root where they join, only when those lines
|
|
@@ -285,7 +301,7 @@ export async function accept(root, args) {
|
|
|
285
301
|
const where = every ? 'every place · then the root, where the places join' : regions.length ? regions.map((one) => `${one}/`).join(' · ') : 'the root alone, where the places join';
|
|
286
302
|
process.stdout.write(`ACTS ${where} · ${owned.ledger.length} lines to the ledger${[...owned.own].map(([place, lines]) => ` · ${lines.length} to ${place}'s own lock`).join('')} · before the lock\n`);
|
|
287
303
|
const asked = Date.now();
|
|
288
|
-
return underTheRegions(store, every ? [...regions, ''] : regions, 'land', () => acceptNow(root, { ...batch, signed: owned.ledger }, started, owned, asked, regions));
|
|
304
|
+
return underTheRegions(store, every ? [...regions, ''] : regions, 'land', () => acceptNow(root, { ...batch, signed: owned.ledger, records: batch.signed }, started, owned, asked, regions));
|
|
289
305
|
}
|
|
290
306
|
async function acceptNow(root, batch, started, owned, asked = started, descent = []) {
|
|
291
307
|
const waited = Date.now() - asked;
|
|
@@ -294,7 +310,14 @@ async function acceptNow(root, batch, started, owned, asked = started, descent =
|
|
|
294
310
|
const spent = [`sign ${asked - started}`, `wait ${waited}`];
|
|
295
311
|
const took = timing(spent);
|
|
296
312
|
const { signed, epoch } = batch;
|
|
313
|
+
if (refusesStandingPins(root, batch.records ?? signed))
|
|
314
|
+
return 1;
|
|
297
315
|
const store = storeOf(root);
|
|
316
|
+
const currentBase = fullDigest(store, localActLines(root, batch.base.places));
|
|
317
|
+
if (currentBase !== batch.base.digest) {
|
|
318
|
+
process.stderr.write(`${selfName()}: REFUSE·base moved ${batch.base.digest} → ${currentBase} · nothing landed\n`);
|
|
319
|
+
return 1;
|
|
320
|
+
}
|
|
298
321
|
if (signed.some(line => !isConfig(line))) {
|
|
299
322
|
try {
|
|
300
323
|
await admittedObjects(root, [...signedOwnerLines(store), ...signed]);
|
|
@@ -348,9 +371,31 @@ async function acceptNow(root, batch, started, owned, asked = started, descent =
|
|
|
348
371
|
process.stderr.write(`OPEN ${name}\n`);
|
|
349
372
|
if (owned.own.size)
|
|
350
373
|
layReleases(store, root, told.standing, store);
|
|
351
|
-
const
|
|
374
|
+
const resultProfile = actResultProfile(snapshot.standing);
|
|
375
|
+
let landed = [];
|
|
376
|
+
if (resultProfile) {
|
|
377
|
+
const records = batch.records ?? signed;
|
|
378
|
+
const placements = [...(signed.length ? [{ place: '.', records: signed }] : []), ...[...owned.own].map(([place, records]) => ({ place: `${place}/${LOCK}`, records }))];
|
|
379
|
+
const digest = (bytes) => bytesDigest(store, new TextEncoder().encode(bytes));
|
|
380
|
+
const identity = actIdentity(records, { ...resultProfile, digest, placements, localEpoch: epoch });
|
|
381
|
+
const folder = writerFor(snapshot.standing, 'fold');
|
|
382
|
+
if (!folder)
|
|
383
|
+
throw Error('REFUSE·act native result requires a declared fold identity');
|
|
384
|
+
const receipt = canonical({ scope: resultProfile.scope, at: resultProfile.context, role: 'writes', form: 'alphabet', measure: 'digest', value: identity, by: folder, epoch: String(epoch) });
|
|
385
|
+
const bundle = { records: records.join('\n') + '\n', receipt: receipt + '\n', placements: placements.map(p => ({ place: p.place, records: p.records.join('\n') + '\n' })) };
|
|
386
|
+
await commitLocalAct(store, identity, descent, async () => bundle, intactLocalAct);
|
|
387
|
+
// Commitment precedes projections and conformance work. If either fails,
|
|
388
|
+
// this public identity still names the admitted act; it is never rollback.
|
|
389
|
+
process.stdout.write(`ACT ${identity} · epoch ${epoch} · admitted · conformance not asserted\n`);
|
|
390
|
+
for (const line of records)
|
|
391
|
+
process.stdout.write(line + '\n');
|
|
392
|
+
process.stdout.write(receipt + '\n');
|
|
393
|
+
landed = [{ at: 'committed act ' + identity, appended: records.length }];
|
|
394
|
+
}
|
|
395
|
+
else
|
|
396
|
+
landed = took('land', () => [...new Set(signed.map(line => fieldOf(line, 'by')))].map(by => land(store, by, signed.filter(line => fieldOf(line, 'by') === by))));
|
|
352
397
|
for (const [place, lines] of owned.own)
|
|
353
|
-
landOwned(root, place, lines);
|
|
398
|
+
landOwned(root, place, lines, resultProfile !== undefined);
|
|
354
399
|
landSaid(store, writerFor(told.standing, 'fold'), []);
|
|
355
400
|
for (const line of signed) {
|
|
356
401
|
const value = fieldOf(line, 'value');
|
|
@@ -419,6 +464,10 @@ async function acceptNow(root, batch, started, owned, asked = started, descent =
|
|
|
419
464
|
*/
|
|
420
465
|
function repaid(root, proposed, started) {
|
|
421
466
|
const store = storeOf(root);
|
|
467
|
+
const prior = committedLocalActs(store).find(act => { const lines = act.bundle.records.trimEnd().split('\n'); return lines.length === proposed.length && lines.every((line, i) => line === proposed[i]); });
|
|
468
|
+
if (prior) {
|
|
469
|
+
process.stdout.write(`ACT ${prior.identity} · epoch ${fieldOf(prior.bundle.receipt.trim(), 'epoch')} · admitted · replay\n` + prior.bundle.records + prior.bundle.receipt);
|
|
470
|
+
}
|
|
422
471
|
const asked = new Set(proposed.filter((line) => fieldOf(line, 'role') === 'demands').map((line) => fieldOf(line, 'scope')));
|
|
423
472
|
const view = `${wordOf(foldClaims(signedOwnerLines(store)).standing, 'families', 'view')}/`;
|
|
424
473
|
const standingAsked = signedDemands(store).filter((line) => asked.has(fieldOf(line, 'scope')) && !fieldOf(line, 'scope').startsWith(view));
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { actResultOf, actResultProfile } from '@lapxo/topos/wire';
|
|
2
|
+
import { actLines, intactLocalAct, localActResult } from "../host/ports/local-act.js";
|
|
3
|
+
import { storeOf } from "../land/ledger.js";
|
|
4
|
+
import { lockStanding } from "../fold/keys.js";
|
|
5
|
+
import { authorityFor, writerFor } from "../fold/signers.js";
|
|
6
|
+
import { eraOf } from "../fold/digests.js";
|
|
7
|
+
import { fieldOf, isConfig, foldClaims } from "../fold/claims.js";
|
|
8
|
+
import { ownRecordAdmitted } from "../fold/signed.js";
|
|
9
|
+
import { admissionAnchor, admissionHistoryAt } from "../fold/admission-history.js";
|
|
10
|
+
import { fullDigest } from "../host/digest.js";
|
|
11
|
+
import { wordOf, wireLine } from "../fold/wire.js";
|
|
12
|
+
import { viewsOf } from "../fold/views.js";
|
|
13
|
+
/** Public query of a committed identity, using the same admission authority.
|
|
14
|
+
* Native fold evidence has store provenance; it never grants coverage. */
|
|
15
|
+
export function printActResult(root, identity) {
|
|
16
|
+
const store = storeOf(root), standing = lockStanding(store);
|
|
17
|
+
const view = viewsOf(standing).get('act');
|
|
18
|
+
if (!view || view.shape || !view.regions.some(region => region.name === 'act'))
|
|
19
|
+
throw Error('REFUSE·view act requires an admitted result profile and act view');
|
|
20
|
+
if (!identity)
|
|
21
|
+
throw Error('REFUSE·act name one committed identity');
|
|
22
|
+
const bundle = localActResult(store, identity, intactLocalAct);
|
|
23
|
+
if (!bundle)
|
|
24
|
+
throw Error('REFUSE·act no committed result ' + identity);
|
|
25
|
+
const records = actLines(bundle.records), epoch = Number(fieldOf(bundle.receipt.trim(), 'epoch'));
|
|
26
|
+
const history = admissionHistoryAt(root, epoch), before = history.filter(line => !isConfig(line) || Number(fieldOf(line, 'epoch')) < epoch);
|
|
27
|
+
const algorithms = eraOf(foldClaims(before).standing).admitted;
|
|
28
|
+
const authority = authorityFor(history, admissionAnchor(root), algorithms);
|
|
29
|
+
const admittedBefore = foldClaims(before.filter(line => authority.of(line).kind === 'admitted')).standing;
|
|
30
|
+
const profile = actResultProfile(admittedBefore);
|
|
31
|
+
if (!profile)
|
|
32
|
+
throw Error('REFUSE·act no result profile at admission');
|
|
33
|
+
const family = authority.admitted.some(signer => !signer.coverage.includes('*')) ? wordOf(admittedBefore, 'families', 'region') : '';
|
|
34
|
+
const algorithm = identity.slice(0, identity.indexOf(':'));
|
|
35
|
+
const named = fieldOf(wireLine(admittedBefore, 'digest-algorithms') ?? '', 'value').split('|').map(value => value.split(':')[0]);
|
|
36
|
+
if (!named.includes(algorithm))
|
|
37
|
+
throw Error('REFUSE·act digest algorithm was not admitted at commitment');
|
|
38
|
+
const result = actResultOf(records, bundle.receipt.trim(), { ...profile, localEpoch: epoch,
|
|
39
|
+
digest: bytes => fullDigest(bytes, algorithm),
|
|
40
|
+
placements: bundle.placements.map(p => ({ place: p.place, records: actLines(p.records) })),
|
|
41
|
+
admitsRecord: line => {
|
|
42
|
+
if (isConfig(line) && Number(fieldOf(line, 'epoch')) !== epoch)
|
|
43
|
+
return false;
|
|
44
|
+
return bundle.placements.some(p => actLines(p.records).includes(line) && (p.place === '.' ? authority.of(line).kind === 'admitted' : ownRecordAdmitted(line, p.place.slice(0, p.place.lastIndexOf('/')), family, authority.admitted, algorithms)));
|
|
45
|
+
},
|
|
46
|
+
verifiesReceipt: line => fieldOf(line, 'by') === writerFor(admittedBefore, 'fold') && intactLocalAct(identity, bundle), });
|
|
47
|
+
process.stdout.write(`ACT ${result.identity} · epoch ${result.epoch} · admitted · conformance not asserted\n` + bundle.records + bundle.receipt);
|
|
48
|
+
}
|
package/dist/cli/owner.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { committedLocalActs } from "../host/ports/local-act.js";
|
|
1
2
|
import { fileURLToPath } from 'node:url';
|
|
2
3
|
import { ingressReceipts } from "../host/ports/ingress.js";
|
|
3
4
|
import { readOnly } from "../host/read-only.js";
|
|
@@ -12,7 +13,7 @@ import { authorityFor, releaseSigner, rootSigner } from "../fold/signers.js";
|
|
|
12
13
|
import { storeRoot } from "../host/ports/store.js";
|
|
13
14
|
import { blobAt, landBlob, ledgerLines, storeOf } from "../land/ledger.js";
|
|
14
15
|
import { observeText } from "../observe/files.js";
|
|
15
|
-
import {
|
|
16
|
+
import { appliedHistoryIn } from "../fold/signed.js";
|
|
16
17
|
import { everLanded, inside, locationsOf } from "../land/vouched.js";
|
|
17
18
|
import { renderTarget } from "../render/target.js";
|
|
18
19
|
export const ownerOf = (store, told = []) => keyFor(store, 'authorize', told);
|
|
@@ -60,7 +61,7 @@ export function localActLines(root, placed = []) {
|
|
|
60
61
|
throw Error('REFUSE·ingress local import receipt is not admitted');
|
|
61
62
|
}
|
|
62
63
|
}
|
|
63
|
-
return [...local, ...receipts, ...placed.flatMap(place =>
|
|
64
|
+
return [...local, ...receipts, ...committedLocalActs(store).map(act => act.bundle.receipt.trim()), ...placed.flatMap(place => appliedHistoryIn(root, place))];
|
|
64
65
|
}
|
|
65
66
|
export function epochOf(lines) {
|
|
66
67
|
return lines.reduce((top, line) => Math.max(top, Number(fieldOf(line, 'epoch')) || 0), 0);
|
package/dist/cli/pass.js
CHANGED
|
@@ -13,12 +13,12 @@ import { algorithmCost, instrumentOf, instrumentDigest } from "../fold/digests.j
|
|
|
13
13
|
import { writerFor } from "../fold/signers.js";
|
|
14
14
|
import { defaulted, placesOf, viewsOf } from "../fold/views.js";
|
|
15
15
|
import { ownLockOf } from "../fold/signed.js";
|
|
16
|
-
import { worldAt, worldsIn } from "../fold/places.js";
|
|
16
|
+
import { localDirectoriesIn, worldAt, worldsIn } from "../fold/places.js";
|
|
17
17
|
import { wordsOf } from "../fold/wire.js";
|
|
18
18
|
import { land, ledgerLines, replaceWhole, storeOf } from "../land/ledger.js";
|
|
19
19
|
import { holdRoot, holds, underTheLock } from "../land/act.js";
|
|
20
20
|
import { emptyLedger, sayStranger } from "../fold/stranger.js";
|
|
21
|
-
import {
|
|
21
|
+
import { observeFile, observeText } from "../observe/files.js";
|
|
22
22
|
import { render, renderedOf, unrendered, viewOf } from "../render/view.js";
|
|
23
23
|
import { releasePolicyLines } from "../fold/release-policy.js";
|
|
24
24
|
import { carriedFrom } from "../fold/resolved.js";
|
|
@@ -90,7 +90,9 @@ export async function pass(root, entry, under, check) {
|
|
|
90
90
|
if (!swept.written.length)
|
|
91
91
|
await closeReceipts(root, storeOf(root));
|
|
92
92
|
const closed = meets(root, storeOf(root), under ?? '') && !swept.written.length;
|
|
93
|
-
|
|
93
|
+
// Sealed files do not imply an empty reading. If a retained fold is stale,
|
|
94
|
+
// foldPlace must reload its evidence; a valid retained fold still costs no read.
|
|
95
|
+
const fold = foldPlace(root, entry, under, false, { standing: swept.standingAt.get(under ?? '') ?? standingOf(root, under), ...(swept.settled ? {} : { observed: swept.observed.length ? swept.observed : observedClaims(root, storeOf(root), { wait: false }) }), free: !swept.settled });
|
|
94
96
|
for (const line of releasePolicyLines(fold.release))
|
|
95
97
|
process.stdout.write(`${line}\n`);
|
|
96
98
|
if (closed) {
|
|
@@ -166,7 +168,7 @@ async function sweep(root, entry, under, check, told, journal, given, keep, reac
|
|
|
166
168
|
return picked.length ? picked : [...worldPlaces];
|
|
167
169
|
};
|
|
168
170
|
const placeless = views.filter((view) => !worldList.includes(view.name) && !placesOf(lock.standing, view).length);
|
|
169
|
-
const holding =
|
|
171
|
+
const holding = localDirectoriesIn(root).filter((name) => placeless.some((view) => observeText(join(root, name, view.shape)) !== undefined)).map((name) => `${name}/`);
|
|
170
172
|
const descended = (place) => !limited || descent.some((one) => one === '' || place === `${one}/` || place.startsWith(`${one}/`));
|
|
171
173
|
const reached = limited ? descent.filter((one) => one !== '').map((one) => (one.endsWith('/') ? one : `${one}/`)) : [];
|
|
172
174
|
const places = [...new Set([...views.flatMap(resolved), ...holding, ...reached])]
|
package/dist/cli/place.d.ts
CHANGED
|
@@ -69,6 +69,6 @@ export declare function coneOf(root: string, entry: string, ceilings: readonly s
|
|
|
69
69
|
export declare const ownCeilings: (under?: string) => readonly string[];
|
|
70
70
|
export declare function foldPlace(root: string, entry: string, under?: string, look?: boolean, given?: {
|
|
71
71
|
readonly standing: ReturnType<typeof standingOf>;
|
|
72
|
-
readonly observed
|
|
72
|
+
readonly observed?: readonly string[];
|
|
73
73
|
readonly free?: boolean;
|
|
74
74
|
}): PlaceFold;
|
package/dist/cli/place.js
CHANGED
|
@@ -195,7 +195,7 @@ export function foldPlace(root, entry, under, look = true, given) {
|
|
|
195
195
|
const closed = meets(root, store, under ?? '');
|
|
196
196
|
const digestMs = Date.now() - digestAt;
|
|
197
197
|
if (closed && given?.free !== true) {
|
|
198
|
-
const prior = keptPlace(store, under);
|
|
198
|
+
const prior = keptPlace(store, under, instrumentOf(store, entry, standingOf(root).standing, root, ownLock()));
|
|
199
199
|
if (prior !== undefined) {
|
|
200
200
|
process.stderr.write(`CLOSED ${under || '.'} · digest ${digestMs} ms\n`);
|
|
201
201
|
return { ...prior, kept: true };
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/** Judge an external signed approval without landing it, fetching its source,
|
|
2
|
+
* executing a provider, or changing the release-policy verdict of the place.
|
|
3
|
+
* The anchor is the place's key, never a key offered by the approval. */
|
|
4
|
+
export declare function releaseApproval(lock: readonly string[], lines: readonly string[]): string;
|
|
5
|
+
export declare function printReleaseApproval(root: string, file: string): void;
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { parse } from '@lapxo/topos/wire';
|
|
2
|
+
import { authorityFor, writerFor, releaseSigner } from "../fold/signers.js";
|
|
3
|
+
import { eraOf } from "../fold/digests.js";
|
|
4
|
+
import { fieldOf, foldClaims } from "../fold/claims.js";
|
|
5
|
+
import { observeText } from "../observe/files.js";
|
|
6
|
+
import { claimLinesIn } from "./args.js";
|
|
7
|
+
import { LOCK } from '@lapxo/topos/wire';
|
|
8
|
+
import { join } from 'node:path';
|
|
9
|
+
/** Judge an external signed approval without landing it, fetching its source,
|
|
10
|
+
* executing a provider, or changing the release-policy verdict of the place.
|
|
11
|
+
* The anchor is the place's key, never a key offered by the approval. */
|
|
12
|
+
export function releaseApproval(lock, lines) {
|
|
13
|
+
const standing = foldClaims(lock).standing;
|
|
14
|
+
const id = writerFor(standing, 'authorize');
|
|
15
|
+
if (!id)
|
|
16
|
+
throw Error('REFUSE·release place names no authorizer');
|
|
17
|
+
const one = (rows, scope, measure) => {
|
|
18
|
+
const selected = rows.filter(line => fieldOf(line, 'scope') === scope && (!measure || fieldOf(line, 'measure') === measure) && fieldOf(line, 'value') !== 'withdraw');
|
|
19
|
+
if (selected.length !== 1)
|
|
20
|
+
throw Error(`REFUSE·release ${scope} needs one declaration`);
|
|
21
|
+
return fieldOf(selected[0], 'value');
|
|
22
|
+
};
|
|
23
|
+
const anchor = { ...releaseSigner(standing), id, publicKey: one(standing, `keys/${id}`, 'public-key'), coverage: one(standing, `keys/${id}`, 'coverage').split('|') };
|
|
24
|
+
if (!lines.length)
|
|
25
|
+
throw Error('REFUSE·release empty approval');
|
|
26
|
+
for (const line of lines) {
|
|
27
|
+
const got = parse(line);
|
|
28
|
+
if (got.kind !== 'fact')
|
|
29
|
+
throw Error('REFUSE·release malformed approval');
|
|
30
|
+
if (got.value.fields['by'] !== id || !got.value.fields['sig'])
|
|
31
|
+
throw Error(`REFUSE·release approval must be signed by ${id}`);
|
|
32
|
+
}
|
|
33
|
+
// A separate release place may declare an era; its configuration must itself
|
|
34
|
+
// authenticate against the existing anchor. No packet key or coverage is used.
|
|
35
|
+
const wire = lines.filter(line => ['wire/signature-algorithms', 'audit/wire/signature-algorithms', 'wire/era', 'audit/wire/era'].includes(fieldOf(line, 'scope')));
|
|
36
|
+
let era;
|
|
37
|
+
try {
|
|
38
|
+
era = eraOf(wire.length ? foldClaims(wire).standing : standing);
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
throw Error('REFUSE·release signature era is absent or conflicting');
|
|
42
|
+
}
|
|
43
|
+
const authority = authorityFor([], anchor, era.admitted);
|
|
44
|
+
for (const line of lines) {
|
|
45
|
+
const verdict = authority.of(line);
|
|
46
|
+
if (verdict.kind !== 'admitted')
|
|
47
|
+
throw Error(`REFUSE·release ${fieldOf(line, 'scope')}: ${verdict.why ?? verdict.kind}`);
|
|
48
|
+
}
|
|
49
|
+
const released = lines.filter(line => /^release\/[^/]+$/.test(fieldOf(line, 'scope')) && fieldOf(line, 'measure') === 'digest' && fieldOf(line, 'role') === 'writes');
|
|
50
|
+
if (released.length !== 1)
|
|
51
|
+
throw Error('REFUSE·release needs one signed release/<tag> digest');
|
|
52
|
+
const required = (scope, measure) => {
|
|
53
|
+
const selected = lines.filter(line => fieldOf(line, 'scope') === scope);
|
|
54
|
+
if (selected.some(line => fieldOf(line, 'role') !== 'writes' || fieldOf(line, 'form') !== 'alphabet' || fieldOf(line, 'measure') !== measure))
|
|
55
|
+
throw Error(`REFUSE·release ${scope} is not an approval declaration`);
|
|
56
|
+
return one(selected, scope, measure);
|
|
57
|
+
};
|
|
58
|
+
const digest = required(fieldOf(released[0], 'scope'), 'digest'), name = required('release/artifact', 'id'), source = required('sources/archive', 'id');
|
|
59
|
+
if (!/^sha256:[a-f0-9]{64}$/.test(digest))
|
|
60
|
+
throw Error('REFUSE·release archive needs sha256');
|
|
61
|
+
if (!/^[A-Za-z0-9][A-Za-z0-9._-]*$/.test(name))
|
|
62
|
+
throw Error('REFUSE·release artifact needs one safe filename');
|
|
63
|
+
let url;
|
|
64
|
+
try {
|
|
65
|
+
url = new URL(source);
|
|
66
|
+
}
|
|
67
|
+
catch {
|
|
68
|
+
throw Error('REFUSE·release archive source needs an absolute URL');
|
|
69
|
+
}
|
|
70
|
+
if (url.username || url.password || /[\s\x00-\x1f\x7f]/.test(source))
|
|
71
|
+
throw Error('REFUSE·release archive source needs a credential-free absolute URL without whitespace');
|
|
72
|
+
return `READY ${digest} ${name} ${source}`;
|
|
73
|
+
}
|
|
74
|
+
export function printReleaseApproval(root, file) {
|
|
75
|
+
const text = observeText(file);
|
|
76
|
+
if (text === undefined)
|
|
77
|
+
throw Error('REFUSE·release approval file is absent');
|
|
78
|
+
const lines = text.split('\n').filter(line => line.trim().length > 0);
|
|
79
|
+
process.stdout.write(releaseApproval(claimLinesIn(join(root, LOCK)), lines) + '\n');
|
|
80
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/** An evidence projection, never a new authority lock. Authentication uses
|
|
2
|
+
* the admission snapshot at each local epoch; the existing fold applies
|
|
3
|
+
* supersessions and withdrawals across all declared classes. Storage layout
|
|
4
|
+
* and delivery mode do not enter this public boundary. */
|
|
5
|
+
export declare function printStandingResult(root: string): void;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { viewsOf } from "../fold/views.js";
|
|
2
|
+
import { lockStanding } from "../fold/keys.js";
|
|
3
|
+
import { admissionAnchor, admissionHistoryAt } from "../fold/admission-history.js";
|
|
4
|
+
import { authorityFor } from "../fold/signers.js";
|
|
5
|
+
import { eraOf } from "../fold/digests.js";
|
|
6
|
+
import { fieldOf, foldClaims } from "../fold/claims.js";
|
|
7
|
+
import { storeOf } from "../land/ledger.js";
|
|
8
|
+
import { foldedEvidence } from '@lapxo/topos/contract';
|
|
9
|
+
/** An evidence projection, never a new authority lock. Authentication uses
|
|
10
|
+
* the admission snapshot at each local epoch; the existing fold applies
|
|
11
|
+
* supersessions and withdrawals across all declared classes. Storage layout
|
|
12
|
+
* and delivery mode do not enter this public boundary. */
|
|
13
|
+
export function printStandingResult(root) {
|
|
14
|
+
const store = storeOf(root), view = viewsOf(lockStanding(store)).get('standing');
|
|
15
|
+
if (!view || view.shape || !view.regions.some(region => region.name === 'standing'))
|
|
16
|
+
throw Error('REFUSE·view standing requires a declared standing view');
|
|
17
|
+
const history = admissionHistoryAt(root, Number.MAX_SAFE_INTEGER), anchor = admissionAnchor(root);
|
|
18
|
+
const snapshots = new Map();
|
|
19
|
+
const admitted = history.filter(line => {
|
|
20
|
+
const epoch = Number(fieldOf(line, 'epoch')) || 0;
|
|
21
|
+
let authority = snapshots.get(epoch);
|
|
22
|
+
if (!authority) {
|
|
23
|
+
const snapshot = admissionHistoryAt(root, epoch);
|
|
24
|
+
// The signature vocabulary is configuration, not the as-yet
|
|
25
|
+
// unauthenticated evidence whose values are about to be folded.
|
|
26
|
+
const wire = snapshot.filter(record => fieldOf(record, 'scope').startsWith('wire/'));
|
|
27
|
+
authority = authorityFor(snapshot, anchor, eraOf(foldClaims(wire).standing).admitted);
|
|
28
|
+
snapshots.set(epoch, authority);
|
|
29
|
+
}
|
|
30
|
+
return authority.of(line).kind === 'admitted';
|
|
31
|
+
});
|
|
32
|
+
// The host authenticates against historical authority; Topos projects the
|
|
33
|
+
// authenticated history using the instrument's existing fold.
|
|
34
|
+
const { standing } = foldedEvidence(admitted, {
|
|
35
|
+
classification: () => 'local',
|
|
36
|
+
fold: lines => foldClaims(lines).standing,
|
|
37
|
+
});
|
|
38
|
+
process.stdout.write(standing.length ? standing.join('\n') + '\n' : '');
|
|
39
|
+
}
|
package/dist/cli/verb.js
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import { printReleaseApproval } from "./release-result.js";
|
|
3
|
+
import { printActResult } from "./act-result.js";
|
|
4
|
+
import { printStandingResult } from "./standing-result.js";
|
|
2
5
|
import { withContentStore } from "../host/content-store.js";
|
|
3
|
-
import { withoutEffects } from "../host/read-only.js";
|
|
6
|
+
import { readOnly, withoutEffects } from "../host/read-only.js";
|
|
7
|
+
import { layReleases } from "../host/release.js";
|
|
4
8
|
import { resolveSources, resolveRequested } from "../host/source-content.js";
|
|
5
9
|
import { cellsWithReceipts } from "../host/cell-receipts.js";
|
|
6
10
|
import { renderCells } from '@lapxo/topos/cells-view';
|
|
@@ -188,6 +192,18 @@ async function view(root, under, asked, told = [], coord, key, includePolicy = t
|
|
|
188
192
|
process.stderr.write(`${selfName()}: the owner ledger is empty; land it before rendering a view\n`);
|
|
189
193
|
return EXIT.refuse;
|
|
190
194
|
}
|
|
195
|
+
if (name === 'act') {
|
|
196
|
+
if (under || told.length)
|
|
197
|
+
throw Error('REFUSE·act committed identity query only');
|
|
198
|
+
printActResult(root, coord);
|
|
199
|
+
return EXIT.closed;
|
|
200
|
+
}
|
|
201
|
+
if (name === 'standing') {
|
|
202
|
+
if (under || told.length || coord || depth !== undefined)
|
|
203
|
+
throw Error('REFUSE·standing local committed projection only');
|
|
204
|
+
printStandingResult(root);
|
|
205
|
+
return EXIT.closed;
|
|
206
|
+
}
|
|
191
207
|
if (name === 'evidence' && !under && !told.length && ingressBundles(storeOf(root)).length) {
|
|
192
208
|
const declared = viewsOf(lockStanding(storeOf(root))).get(name);
|
|
193
209
|
if (!declared || declared.shape || !declared.regions.some(region => region.name === 'evidence'))
|
|
@@ -224,6 +240,10 @@ async function view(root, under, asked, told = [], coord, key, includePolicy = t
|
|
|
224
240
|
}
|
|
225
241
|
}
|
|
226
242
|
const own = place ? rest.filter((line) => admittedIn(root, place, line)) : [];
|
|
243
|
+
// Executed views use the place's declared mounts before observation, as pass
|
|
244
|
+
// does. Prospective folds and public standing queries do not lay releases.
|
|
245
|
+
if (!told.length && !readOnly())
|
|
246
|
+
layReleases(storeOf(root), root, standingOf(root, under).standing, storeOf(root));
|
|
227
247
|
const fold = told.length
|
|
228
248
|
? foldPlace(root, entryOf(root), under, false, { standing: standingOf(root, under, ledger), observed: observedClaims(root, storeOf(root), { wait: false }), free: true })
|
|
229
249
|
: foldPlace(root, entryOf(root), under, true);
|
|
@@ -440,12 +460,18 @@ async function foldAt(root, under, argv) {
|
|
|
440
460
|
async function foldRequested(root, under, argv) {
|
|
441
461
|
const as = valueOf(argv, FLAGS.as);
|
|
442
462
|
const prospective = as !== undefined && positionalsOf(argv).some(one => existsSync(resolve(one)) && !lstatSync(resolve(one)).isDirectory());
|
|
443
|
-
if (as === NAMED_VIEWS.help || prospective || argv.includes(FLAGS.check))
|
|
463
|
+
if (as === 'release' || as?.split('@')[0] === 'act' || as === NAMED_VIEWS.help || prospective || argv.includes(FLAGS.check))
|
|
444
464
|
return foldResolved(root, under, argv);
|
|
445
465
|
return resolveRequested(digest => resolveSources(standingOf(root, under).standing, [digest]), () => foldResolved(root, under, argv));
|
|
446
466
|
}
|
|
447
467
|
async function foldResolved(root, under, argv) {
|
|
448
468
|
const as = valueOf(argv, FLAGS.as);
|
|
469
|
+
if (as === 'release') {
|
|
470
|
+
const approvals = positionalsOf(argv).filter(one => !(existsSync(resolve(one)) && lstatSync(resolve(one)).isDirectory()));
|
|
471
|
+
if (approvals.length !== 1 || argv.includes(FLAGS.check))
|
|
472
|
+
throw Error('REFUSE·release name one approval file; check does not authorize delivery');
|
|
473
|
+
return withoutEffects(() => { printReleaseApproval(under ? resolve(root, under) : root, resolve(approvals[0])); return EXIT.closed; });
|
|
474
|
+
}
|
|
449
475
|
const inputs = positionalsOf(argv).filter(one => existsSync(resolve(one)) && !lstatSync(resolve(one)).isDirectory());
|
|
450
476
|
// Public content is authenticated by its digest, independently of owner history.
|
|
451
477
|
// A named view does not request mounting every release of the instrument.
|
|
@@ -497,6 +523,11 @@ async function foldResolved(root, under, argv) {
|
|
|
497
523
|
}
|
|
498
524
|
const named = inputs;
|
|
499
525
|
const coord = positionalsOf(argv).find((one) => one !== under?.replace(/\/$/, '') && !one.startsWith('-') && !(existsSync(resolve(one)) && lstatSync(resolve(one)).isDirectory()));
|
|
526
|
+
if (as === 'act') {
|
|
527
|
+
if (positionalsOf(argv).length !== 1)
|
|
528
|
+
throw Error('REFUSE·act name one committed identity');
|
|
529
|
+
return view(root, under, as, [], coord);
|
|
530
|
+
}
|
|
500
531
|
if ((as === 'why' || as === NAMED_VIEWS.because || as.split('@')[0] === 'why') && coord !== undefined)
|
|
501
532
|
return view(root, under, as === NAMED_VIEWS.because ? 'why' : as, [], coord, key);
|
|
502
533
|
if (!named.length)
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { Signer } from '@lapxo/topos/wire';
|
|
2
|
+
/** Local admission history only. Foreign ingress is evidence, never coverage.
|
|
3
|
+
* The clock cutoff is meaningful for configuration; object clocks stay intact. */
|
|
4
|
+
export declare function admissionHistoryAt(root: string, epoch: number): readonly string[];
|
|
5
|
+
/** The existing bootstrap's public root of trust, not today's key selection. */
|
|
6
|
+
export declare function admissionAnchor(root: string): Signer;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { join } from 'node:path';
|
|
2
|
+
import { readFileSync } from 'node:fs';
|
|
3
|
+
import { ledgerLines, ledgerWriters, storeOf } from "../land/ledger.js";
|
|
4
|
+
import { fieldOf, isConfig, selfName } from "./claims.js";
|
|
5
|
+
/** Local admission history only. Foreign ingress is evidence, never coverage.
|
|
6
|
+
* The clock cutoff is meaningful for configuration; object clocks stay intact. */
|
|
7
|
+
export function admissionHistoryAt(root, epoch) {
|
|
8
|
+
const store = storeOf(root);
|
|
9
|
+
return [...new Set(ledgerWriters(store).flatMap(writer => ledgerLines(store, writer)))]
|
|
10
|
+
.filter(line => fieldOf(line, 'sig') !== '' && (!isConfig(line) || Number(fieldOf(line, 'epoch')) <= epoch));
|
|
11
|
+
}
|
|
12
|
+
/** The existing bootstrap's public root of trust, not today's key selection. */
|
|
13
|
+
export function admissionAnchor(root) {
|
|
14
|
+
const at = join(storeOf(root), `${selfName()}.keys`);
|
|
15
|
+
const keys = JSON.parse(readFileSync(at, 'utf8'));
|
|
16
|
+
const anchors = Object.values(keys);
|
|
17
|
+
if (anchors.length !== 1 || !anchors[0]?.publicKey || !anchors[0].coverage.length)
|
|
18
|
+
throw Error('REFUSE·act historical admission requires one bootstrap anchor');
|
|
19
|
+
const anchor = anchors[0];
|
|
20
|
+
return { id: anchor.id, keyClass: 'authorize', publicKey: anchor.publicKey, coverage: anchor.coverage,
|
|
21
|
+
depth: { lo: 1, hi: 16 }, admittedBy: [] };
|
|
22
|
+
}
|
package/dist/fold/closed.js
CHANGED
|
@@ -4,7 +4,7 @@ import { dirname, existsSync, join, mkdirSync, relative, resolve, statSync, writ
|
|
|
4
4
|
import { byBytes, canonical, LOCK, parse, publicationOf, roleAt, roleClaimsOf } from '@lapxo/topos/wire';
|
|
5
5
|
import { RECEIPTS, fieldOf, foldClaims, isConfig, isWire } from "./claims.js";
|
|
6
6
|
import { bytesDigest as hashBytes, fullDigest as hashFull } from "../host/digest.js";
|
|
7
|
-
import { worldAt } from "./places.js";
|
|
7
|
+
import { mountedCoordinatesIn, worldAt } from "./places.js";
|
|
8
8
|
import { ownLockOf } from "./signed.js";
|
|
9
9
|
import { viewsOf } from "./views.js";
|
|
10
10
|
import { storeAt, storeOf } from "../land/ledger.js";
|
|
@@ -31,6 +31,7 @@ const lockStamp = (root, place) => {
|
|
|
31
31
|
return 'none';
|
|
32
32
|
}
|
|
33
33
|
};
|
|
34
|
+
const publishedMounts = (root) => mountedCoordinatesIn(root, (observeText(join(root, LOCK)) ?? '').split('\n').filter(isWire));
|
|
34
35
|
function filesOf(root, place, region, inputs) {
|
|
35
36
|
const step = region.replace(/^receipts\//, '').replace(/\/$/, '');
|
|
36
37
|
const here = join(root, place);
|
|
@@ -41,7 +42,10 @@ function filesOf(root, place, region, inputs) {
|
|
|
41
42
|
const record = parse(line);
|
|
42
43
|
return record.kind === 'fact' ? [record.value.fields] : [];
|
|
43
44
|
}));
|
|
44
|
-
const
|
|
45
|
+
const localMounts = [...mountedCoordinatesIn(here), ...publishedMounts(here)];
|
|
46
|
+
const mounts = [...new Set([...mountedCoordinatesIn(root), ...publishedMounts(root), ...localMounts.map(coordinate => relative(root, join(here, coordinate)))])];
|
|
47
|
+
const owned = coordinatesUnder(join(here, step), root).filter(rel => roleAt(relative(here, join(root, rel)), claims) !== 'foreign'
|
|
48
|
+
&& !mounts.some(mount => rel === mount || rel.startsWith(`${mount}/`)));
|
|
45
49
|
// Foreign worlds are not the place's tree. Retain the external files actually handed to its readers.
|
|
46
50
|
const observed = inputs.filter(line => fieldOf(line, 'measure') === 'observed').flatMap(line => {
|
|
47
51
|
const coordinate = fieldOf(line, 'scope');
|
|
@@ -101,11 +105,13 @@ function namedReceipts(root, store) {
|
|
|
101
105
|
const text = observeText(join(root, RECEIPTS));
|
|
102
106
|
const carried = carriedFrom(store, (text ?? '').split('\n').filter(isWire));
|
|
103
107
|
const out = new Map();
|
|
108
|
+
const mounted = new Set(mountedCoordinatesIn(root));
|
|
104
109
|
for (const line of carried ?? []) {
|
|
105
110
|
if (!isWire(line) || fieldOf(line, 'measure') !== 'digest')
|
|
106
111
|
continue;
|
|
107
112
|
const place = /^write\/([^/]+)\/receipts\.bound$/.exec(fieldOf(line, 'scope'))?.[1];
|
|
108
|
-
|
|
113
|
+
// A retained dependency receipt is input evidence, not a child-place closure.
|
|
114
|
+
if (place && !mounted.has(place))
|
|
109
115
|
out.set(place, fieldOf(line, 'value'));
|
|
110
116
|
}
|
|
111
117
|
return out;
|
package/dist/fold/digests.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
import { ledgerLines, ledgerWriters } from "../land/ledger.js";
|
|
1
2
|
import { readOnly } from "../host/read-only.js";
|
|
2
|
-
import { appendFileSync, dirname, join, mkdirSync, readdirSync, relative, resolve } from "../host/io.js";
|
|
3
|
+
import { appendFileSync, dirname, join, mkdirSync, readdirSync, realpathSync, relative, resolve } from "../host/io.js";
|
|
3
4
|
import { bytesDigest as bytesWith, fullDigest as fullWith } from "../host/digest.js";
|
|
4
5
|
import { coordinatesUnder, observeFile, observeText, stampOf } from "../observe/files.js";
|
|
5
6
|
import { releaseOf, signedLockLines } from "./keys.js";
|
|
@@ -32,10 +33,8 @@ function namedAlgorithm(store) {
|
|
|
32
33
|
const contract = published === undefined ? undefined : wireLine(published, 'digest-algorithms');
|
|
33
34
|
return contract === undefined ? undefined : fieldOf(contract, 'value').split('|')[0]?.split(':')[0];
|
|
34
35
|
}
|
|
35
|
-
for (const
|
|
36
|
-
|
|
37
|
-
continue;
|
|
38
|
-
const text = observeText(join(store, 'ledger', entry.name)) ?? '';
|
|
36
|
+
for (const writer of ledgerWriters(store)) {
|
|
37
|
+
const text = ledgerLines(store, writer).join('\n');
|
|
39
38
|
let from = 0;
|
|
40
39
|
while (from < text.length) {
|
|
41
40
|
const at = (() => {
|
|
@@ -176,14 +175,19 @@ export function instrumentCoordinates(store, entry, standing = [], root = dirnam
|
|
|
176
175
|
const digest = coordinateDigest(store, at);
|
|
177
176
|
if (digest === undefined)
|
|
178
177
|
return;
|
|
179
|
-
|
|
180
|
-
|
|
178
|
+
// The host loader resolves links before loading a module. Count that module
|
|
179
|
+
// once, while retaining distinct modules even when their bytes are equal.
|
|
180
|
+
const module = realpathSync(at);
|
|
181
|
+
if (seen.has(module))
|
|
182
|
+
return;
|
|
183
|
+
seen.set(module, digest);
|
|
184
|
+
for (const one of importsOf(store, module, digest)) {
|
|
181
185
|
if (one.startsWith('node:') || builtinModules.includes(one))
|
|
182
186
|
continue;
|
|
183
187
|
if (one.startsWith('.'))
|
|
184
|
-
walk(resolve(dirname(
|
|
188
|
+
walk(resolve(dirname(module), one));
|
|
185
189
|
else
|
|
186
|
-
walk(createRequire(
|
|
190
|
+
walk(createRequire(module).resolve(one));
|
|
187
191
|
}
|
|
188
192
|
};
|
|
189
193
|
walk(resolve(entry));
|
package/dist/fold/kept.d.ts
CHANGED
|
@@ -23,7 +23,7 @@ export declare function movedPoints(store: string, now: readonly string[], key:
|
|
|
23
23
|
readonly came: readonly string[];
|
|
24
24
|
} | undefined;
|
|
25
25
|
export declare const foldsElsewhere: () => number;
|
|
26
|
-
export declare function keptPlace(store: string, under?: string): PlaceFold | undefined;
|
|
26
|
+
export declare function keptPlace(store: string, under?: string, currentInstrument?: string): PlaceFold | undefined;
|
|
27
27
|
export declare const keptRender: (store: string, key: string, name: string) => string | undefined;
|
|
28
28
|
export declare const keepRender: (store: string, _signer: string, key: string, name: string, _epoch: number, text_: string) => void;
|
|
29
29
|
export declare function keepFold(store: string, key: string, fold: PlaceFold, points: readonly string[], instrument?: string): void;
|