@lapxo/bound 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/CONTRIBUTING.md +44 -0
- package/LICENSE +22 -0
- package/README.md +97 -0
- package/TARGET.bound +361 -0
- package/dist/cli/accept.d.ts +23 -0
- package/dist/cli/accept.js +423 -0
- package/dist/cli/args.d.ts +10 -0
- package/dist/cli/args.js +34 -0
- package/dist/cli/host.d.ts +1 -0
- package/dist/cli/host.js +32 -0
- package/dist/cli/names.d.ts +25 -0
- package/dist/cli/names.js +12 -0
- package/dist/cli/observe.d.ts +1 -0
- package/dist/cli/observe.js +6 -0
- package/dist/cli/owner.d.ts +35 -0
- package/dist/cli/owner.js +119 -0
- package/dist/cli/pass.d.ts +6 -0
- package/dist/cli/pass.js +309 -0
- package/dist/cli/place.d.ts +74 -0
- package/dist/cli/place.js +290 -0
- package/dist/cli/takes.d.ts +30 -0
- package/dist/cli/takes.js +108 -0
- package/dist/cli/verb.d.ts +2 -0
- package/dist/cli/verb.js +478 -0
- package/dist/fold/ceilings.d.ts +24 -0
- package/dist/fold/ceilings.js +129 -0
- package/dist/fold/claims.d.ts +25 -0
- package/dist/fold/claims.js +107 -0
- package/dist/fold/closed.d.ts +15 -0
- package/dist/fold/closed.js +168 -0
- package/dist/fold/configures.d.ts +4 -0
- package/dist/fold/configures.js +22 -0
- package/dist/fold/digests.d.ts +29 -0
- package/dist/fold/digests.js +211 -0
- package/dist/fold/evidence.d.ts +32 -0
- package/dist/fold/evidence.js +22 -0
- package/dist/fold/fold-worker.d.ts +1 -0
- package/dist/fold/fold-worker.js +13 -0
- package/dist/fold/kept.d.ts +29 -0
- package/dist/fold/kept.js +142 -0
- package/dist/fold/keys.d.ts +17 -0
- package/dist/fold/keys.js +84 -0
- package/dist/fold/laid.d.ts +17 -0
- package/dist/fold/laid.js +90 -0
- package/dist/fold/nos.d.ts +8 -0
- package/dist/fold/nos.js +48 -0
- package/dist/fold/object.d.ts +17 -0
- package/dist/fold/object.js +38 -0
- package/dist/fold/observed.d.ts +33 -0
- package/dist/fold/observed.js +444 -0
- package/dist/fold/opened.d.ts +5 -0
- package/dist/fold/opened.js +33 -0
- package/dist/fold/own.d.ts +24 -0
- package/dist/fold/own.js +215 -0
- package/dist/fold/paid.d.ts +11 -0
- package/dist/fold/paid.js +92 -0
- package/dist/fold/place-inputs.d.ts +17 -0
- package/dist/fold/place-inputs.js +70 -0
- package/dist/fold/places.d.ts +20 -0
- package/dist/fold/places.js +84 -0
- package/dist/fold/rank.d.ts +3 -0
- package/dist/fold/rank.js +9 -0
- package/dist/fold/reach.d.ts +13 -0
- package/dist/fold/reach.js +65 -0
- package/dist/fold/readers.d.ts +44 -0
- package/dist/fold/readers.js +132 -0
- package/dist/fold/receipts.d.ts +8 -0
- package/dist/fold/receipts.js +47 -0
- package/dist/fold/region.d.ts +72 -0
- package/dist/fold/region.js +286 -0
- package/dist/fold/release-policy.d.ts +67 -0
- package/dist/fold/release-policy.js +103 -0
- package/dist/fold/resolved.d.ts +18 -0
- package/dist/fold/resolved.js +123 -0
- package/dist/fold/roles.d.ts +17 -0
- package/dist/fold/roles.js +59 -0
- package/dist/fold/second.d.ts +18 -0
- package/dist/fold/second.js +68 -0
- package/dist/fold/shards.d.ts +29 -0
- package/dist/fold/shards.js +100 -0
- package/dist/fold/signed.d.ts +18 -0
- package/dist/fold/signed.js +75 -0
- package/dist/fold/signers.d.ts +24 -0
- package/dist/fold/signers.js +58 -0
- package/dist/fold/stranger.d.ts +3 -0
- package/dist/fold/stranger.js +24 -0
- package/dist/fold/views.d.ts +27 -0
- package/dist/fold/views.js +66 -0
- package/dist/fold/wire.d.ts +19 -0
- package/dist/fold/wire.js +41 -0
- package/dist/fold/zoom.d.ts +16 -0
- package/dist/fold/zoom.js +80 -0
- package/dist/host/adapters/keys/ed25519.d.ts +7 -0
- package/dist/host/adapters/keys/ed25519.js +30 -0
- package/dist/host/archive.d.ts +11 -0
- package/dist/host/archive.js +152 -0
- package/dist/host/capsule-process.d.ts +20 -0
- package/dist/host/capsule-process.js +54 -0
- package/dist/host/capsule.d.ts +13 -0
- package/dist/host/capsule.js +149 -0
- package/dist/host/cell-receipts.d.ts +2 -0
- package/dist/host/cell-receipts.js +124 -0
- package/dist/host/digest.d.ts +3 -0
- package/dist/host/digest.js +15 -0
- package/dist/host/hash.d.ts +2 -0
- package/dist/host/hash.js +3 -0
- package/dist/host/io.d.ts +9 -0
- package/dist/host/io.js +9 -0
- package/dist/host/located.d.ts +4 -0
- package/dist/host/located.js +105 -0
- package/dist/host/objects.d.ts +5 -0
- package/dist/host/objects.js +18 -0
- package/dist/host/ports/store.d.ts +2 -0
- package/dist/host/ports/store.js +18 -0
- package/dist/host/provide.d.ts +23 -0
- package/dist/host/provide.js +81 -0
- package/dist/host/release.d.ts +12 -0
- package/dist/host/release.js +213 -0
- package/dist/host/selected-capsules.d.ts +8 -0
- package/dist/host/selected-capsules.js +19 -0
- package/dist/host/selected-topos.d.ts +17 -0
- package/dist/host/selected-topos.js +61 -0
- package/dist/host/source-content.d.ts +10 -0
- package/dist/host/source-content.js +94 -0
- package/dist/host/timing.d.ts +2 -0
- package/dist/host/timing.js +7 -0
- package/dist/judge/late.d.ts +7 -0
- package/dist/judge/late.js +33 -0
- package/dist/judge/take.d.ts +57 -0
- package/dist/judge/take.js +75 -0
- package/dist/judge/widening.d.ts +1 -0
- package/dist/judge/widening.js +25 -0
- package/dist/land/act.d.ts +4 -0
- package/dist/land/act.js +209 -0
- package/dist/land/alias.d.ts +10 -0
- package/dist/land/alias.js +41 -0
- package/dist/land/held.d.ts +7 -0
- package/dist/land/held.js +23 -0
- package/dist/land/inert.d.ts +26 -0
- package/dist/land/inert.js +33 -0
- package/dist/land/leaving.d.ts +14 -0
- package/dist/land/leaving.js +51 -0
- package/dist/land/ledger.d.ts +28 -0
- package/dist/land/ledger.js +148 -0
- package/dist/land/owned.d.ts +23 -0
- package/dist/land/owned.js +65 -0
- package/dist/land/sign.d.ts +16 -0
- package/dist/land/sign.js +30 -0
- package/dist/land/vouched.d.ts +21 -0
- package/dist/land/vouched.js +112 -0
- package/dist/land/withdraw.d.ts +14 -0
- package/dist/land/withdraw.js +24 -0
- package/dist/observe/files.d.ts +14 -0
- package/dist/observe/files.js +68 -0
- package/dist/observe/identity.d.ts +19 -0
- package/dist/observe/identity.js +61 -0
- package/dist/observe/match.d.ts +1 -0
- package/dist/observe/match.js +8 -0
- package/dist/observe/regions.d.ts +29 -0
- package/dist/observe/regions.js +200 -0
- package/dist/observe/run.d.ts +30 -0
- package/dist/observe/run.js +34 -0
- package/dist/observe/runner.d.ts +16 -0
- package/dist/observe/runner.js +83 -0
- package/dist/render/capsule.d.ts +24 -0
- package/dist/render/capsule.js +30 -0
- package/dist/render/field.d.ts +9 -0
- package/dist/render/field.js +46 -0
- package/dist/render/names.d.ts +3 -0
- package/dist/render/names.js +4 -0
- package/dist/render/open.d.ts +27 -0
- package/dist/render/open.js +61 -0
- package/dist/render/page.d.ts +3 -0
- package/dist/render/page.js +7 -0
- package/dist/render/receipts.d.ts +3 -0
- package/dist/render/receipts.js +46 -0
- package/dist/render/shape.d.ts +16 -0
- package/dist/render/shape.js +74 -0
- package/dist/render/target.d.ts +18 -0
- package/dist/render/target.js +52 -0
- package/dist/render/view.d.ts +22 -0
- package/dist/render/view.js +191 -0
- package/docs/readme-assets/atlas.svg +169 -0
- package/docs/usage.md +53 -0
- package/package.json +48 -0
- package/receipts.bound +59 -0
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { keyFor, lockLines, releaseOf, signedLockLines } from "../fold/keys.js";
|
|
2
|
+
import { existsSync, renameSync, writeFileSync } from 'node:fs';
|
|
3
|
+
import { join, resolve } from 'node:path';
|
|
4
|
+
import { bytesDigest, fullDigest, memoized, signaturesOf } from "../fold/digests.js";
|
|
5
|
+
import { claimKey, isConfig, fieldOf, foldClaims, LOCK, selfName } from "../fold/claims.js";
|
|
6
|
+
import { asksThePlace } from "../fold/configures.js";
|
|
7
|
+
import { receiptsIn } from "../fold/receipts.js";
|
|
8
|
+
import { authorityFor, releaseSigner, rootSigner } from "../fold/signers.js";
|
|
9
|
+
import { storeRoot } from "../host/ports/store.js";
|
|
10
|
+
import { blobAt, landBlob, ledgerLines, storeOf } from "../land/ledger.js";
|
|
11
|
+
import { observeText } from "../observe/files.js";
|
|
12
|
+
import { everLanded, inside, locationsOf } from "../land/vouched.js";
|
|
13
|
+
import { renderTarget } from "../render/target.js";
|
|
14
|
+
export const ownerOf = (store, told = []) => keyFor(store, 'authorize', told);
|
|
15
|
+
/** The signed lines standing in the lock: the owner's, and those of every key it admits to authorize, each in its own ledger. */
|
|
16
|
+
export function signedOwnerLines(store) {
|
|
17
|
+
return signedLockLines(store);
|
|
18
|
+
}
|
|
19
|
+
function forkReceipts(store, lines) {
|
|
20
|
+
const scopes = [...new Set(foldClaims(lines.filter(line => fieldOf(line, 'sig') !== '')).forks.map((f) => f.key.split(' ')[0] ?? ''))].filter(Boolean);
|
|
21
|
+
return scopes.length ? receiptsIn(store, scopes) : [];
|
|
22
|
+
}
|
|
23
|
+
export function standingDemands(store) {
|
|
24
|
+
const lines = signedOwnerLines(store);
|
|
25
|
+
return foldClaims(lines, forkReceipts(store, lines)).standing
|
|
26
|
+
.filter((line) => asksThePlace(line) && fieldOf(line, 'value') !== 'withdraw');
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* A demand is history when it asks about a place that is gone: one the tree once held and holds no more. It paid, or
|
|
30
|
+
* did not, in its own time; whoever wants it again signs it against the region as it is. A place never held is not
|
|
31
|
+
* gone — it is still to be made, and its demand stays live.
|
|
32
|
+
*/
|
|
33
|
+
export function historyOf(root, store, demands) {
|
|
34
|
+
const held = [...everLanded(store)];
|
|
35
|
+
const gone = (need) => {
|
|
36
|
+
const at = need.replace(/\/+$/, '');
|
|
37
|
+
return !existsSync(join(root, at)) && held.some((coordinate) => coordinate === at || inside(coordinate, at));
|
|
38
|
+
};
|
|
39
|
+
const history = demands.filter((line) => locationsOf(fieldOf(line, 'needs')).locations.some(gone));
|
|
40
|
+
return { live: demands.filter((line) => !history.includes(line)), history };
|
|
41
|
+
}
|
|
42
|
+
export function signedDemands(store, root = resolve('.')) {
|
|
43
|
+
return historyOf(root, store, standingDemands(store)).live;
|
|
44
|
+
}
|
|
45
|
+
export function forkedScopes(store) {
|
|
46
|
+
const lines = signedOwnerLines(store);
|
|
47
|
+
return new Set(foldClaims(lines, forkReceipts(store, lines)).forks.flatMap((f) => f.lines.map((line) => fieldOf(line, 'scope'))));
|
|
48
|
+
}
|
|
49
|
+
export function epochOf(lines) {
|
|
50
|
+
return lines.reduce((top, line) => Math.max(top, Number(fieldOf(line, 'epoch')) || 0), 0);
|
|
51
|
+
}
|
|
52
|
+
export function targetFile(root) {
|
|
53
|
+
return join(storeRoot(root), LOCK);
|
|
54
|
+
}
|
|
55
|
+
export function renderInto(root, store) {
|
|
56
|
+
if (!lockLines(store).length)
|
|
57
|
+
throw new Error(`${selfName()}: the owner ledger is empty; land it before rendering TARGET`);
|
|
58
|
+
const rendered = renderedLock(root, store);
|
|
59
|
+
if (releaseOf(store))
|
|
60
|
+
return rendered;
|
|
61
|
+
const at = targetFile(root);
|
|
62
|
+
writeFileSync(`${at}.render`, rendered.text);
|
|
63
|
+
renameSync(`${at}.render`, at);
|
|
64
|
+
return rendered;
|
|
65
|
+
}
|
|
66
|
+
const rendered = new WeakMap();
|
|
67
|
+
/**
|
|
68
|
+
* A release's lock is read as it was laid and never rendered over. Its target lines are true only where a pin names the
|
|
69
|
+
* release, so here they stand grey; a line signed by a key the release does not carry is refused and does not stand.
|
|
70
|
+
*/
|
|
71
|
+
function readRelease(lines) {
|
|
72
|
+
const of = authorityFor(lines, releaseSigner(lines), [], { digest: '', pins: [] }).of;
|
|
73
|
+
const refused = lines.filter((line) => of(line).kind === 'refuse');
|
|
74
|
+
const standing = lines.filter((line) => !refused.includes(line));
|
|
75
|
+
return {
|
|
76
|
+
text: `${standing.join('\n')}\n`, standing: standing.length, superseded: [], retracted: [], forks: [],
|
|
77
|
+
grey: standing.filter((line) => of(line).kind === 'grey'), refused, vacuous: [],
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
export function renderedLock(root, store, told = []) {
|
|
81
|
+
const released = releaseOf(store);
|
|
82
|
+
if (released && !told.length)
|
|
83
|
+
return readRelease(released);
|
|
84
|
+
const owned = [...lockLines(store), ...told];
|
|
85
|
+
const render = () => renderTarget(owned, undefined, authorityFor(owned, rootSigner(root), signaturesOf(store, told).admitted).of, forkReceipts(store, owned));
|
|
86
|
+
if (told.length) {
|
|
87
|
+
const byLock = rendered.get(lockLines(store)) ?? rendered.set(lockLines(store), new Map()).get(lockLines(store));
|
|
88
|
+
return byLock.get(told) ?? byLock.set(told, render()).get(told);
|
|
89
|
+
}
|
|
90
|
+
const anchor = [join(storeRoot(root), `${selfName()}.keys`), join(storeOf(root), `${selfName()}.keys`)].map((at) => observeText(at) ?? '').join('\n');
|
|
91
|
+
const judged = ledgerLines(store, 'judge');
|
|
92
|
+
const key = `lock ${fullDigest(store, [new Date().toISOString().slice(0, 10), anchor, `${judged.length} ${judged.at(-1) ?? ''}`, ...owned])}`;
|
|
93
|
+
const held = memoized(store, key, () => {
|
|
94
|
+
const bytes = new TextEncoder().encode(JSON.stringify(render()));
|
|
95
|
+
const digest = bytesDigest(store, bytes);
|
|
96
|
+
landBlob(store, digest, bytes);
|
|
97
|
+
return digest;
|
|
98
|
+
});
|
|
99
|
+
const text = observeText(blobAt(store, held));
|
|
100
|
+
return text === undefined ? render() : JSON.parse(text);
|
|
101
|
+
}
|
|
102
|
+
export function forksJoinedBy(store, signed) {
|
|
103
|
+
signed = signed.filter(isConfig);
|
|
104
|
+
const keys = new Set(signed.map(claimKey));
|
|
105
|
+
const held = signedOwnerLines(store).filter((line) => isConfig(line) && keys.has(claimKey(line)));
|
|
106
|
+
const vacuous = new Set(foldClaims(held, forkReceipts(store, held)).vacuous);
|
|
107
|
+
const live = held.filter((line) => !vacuous.has(line));
|
|
108
|
+
return foldClaims([...live, ...signed]).forks.filter((fork) => fork.lines.some((line) => signed.includes(line)));
|
|
109
|
+
}
|
|
110
|
+
export function forksItself(lines) {
|
|
111
|
+
const bySigner = new Map();
|
|
112
|
+
for (const line of lines)
|
|
113
|
+
bySigner.set(fieldOf(line, 'by'), (bySigner.get(fieldOf(line, 'by')) ?? 0) + 1);
|
|
114
|
+
return [...bySigner.values()].some((n) => n > 1);
|
|
115
|
+
}
|
|
116
|
+
export function sideOf(line, landed) {
|
|
117
|
+
const at = landed.indexOf(line);
|
|
118
|
+
return ['value', 'at', 'needs', 'restsOn'].map((k) => `${k}=${fieldOf(line, k) || '∅'}`).join(' ') + ` landed=${at < 0 ? 'new' : `#${at + 1}`}`;
|
|
119
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export declare function pass(root: string, entry: string, under: string | undefined, check: boolean): Promise<number>;
|
|
2
|
+
export type Journal = Map<string, {
|
|
3
|
+
readonly held: Uint8Array | undefined;
|
|
4
|
+
readonly made: string | undefined;
|
|
5
|
+
}>;
|
|
6
|
+
export declare function landSaid(store: string, folder: string | undefined, lines: readonly string[]): void;
|
package/dist/cli/pass.js
ADDED
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
import { mkdirSync, rmSync } from 'node:fs';
|
|
2
|
+
import { dirname, join } from 'node:path';
|
|
3
|
+
import { Worker } from 'node:worker_threads';
|
|
4
|
+
import { ENVELOPE, canonical, parse } from '@lapxo/topos/wire';
|
|
5
|
+
import { foldPlace, standingOf } from "./place.js";
|
|
6
|
+
import { fieldOf, selfName } from "../fold/claims.js";
|
|
7
|
+
import { observedClaims } from "../fold/observed.js";
|
|
8
|
+
import { layReleases, resolvedOf } from "../host/release.js";
|
|
9
|
+
import { ownLock, ownRoot, ownStore, processOf } from "../observe/runner.js";
|
|
10
|
+
import { landLaid } from "../fold/laid.js";
|
|
11
|
+
import { actSeconds, idleCount, instrumentKeep, instrumentKept, meets, noteIdle, receiptPlaces, receiptsSeen } from "../fold/closed.js";
|
|
12
|
+
import { algorithmCost, instrumentOf, instrumentDigest } from "../fold/digests.js";
|
|
13
|
+
import { writerFor } from "../fold/signers.js";
|
|
14
|
+
import { defaulted, placesOf, viewsOf } from "../fold/views.js";
|
|
15
|
+
import { ownLockOf } from "../fold/signed.js";
|
|
16
|
+
import { worldAt, worldsIn } from "../fold/places.js";
|
|
17
|
+
import { wordsOf } from "../fold/wire.js";
|
|
18
|
+
import { land, ledgerLines, replaceWhole, storeOf } from "../land/ledger.js";
|
|
19
|
+
import { holdRoot, holds, underTheLock } from "../land/act.js";
|
|
20
|
+
import { emptyLedger, sayStranger } from "../fold/stranger.js";
|
|
21
|
+
import { entriesIn, observeFile, observeText } from "../observe/files.js";
|
|
22
|
+
import { render, renderedOf, unrendered, viewOf } from "../render/view.js";
|
|
23
|
+
import { releasePolicyLines } from "../fold/release-policy.js";
|
|
24
|
+
import { carriedFrom } from "../fold/resolved.js";
|
|
25
|
+
import { wireLine } from "../fold/wire.js";
|
|
26
|
+
import { isWire, RECEIPTS } from "../fold/claims.js";
|
|
27
|
+
/**
|
|
28
|
+
* The pass of a place, in one process: the releases its instrument names are laid out first, its readers observe
|
|
29
|
+
* once, each place a view writes in is folded once, the places before the root whose door reads them, and every coordinate a
|
|
30
|
+
* view writes there is its render or is written again.
|
|
31
|
+
* A round that wrote nothing ends the pass; one that made a coordinate or wrote one a region reads folds again, since what a
|
|
32
|
+
* place shows may rest on what another renders — a coordinate rewritten that no region reads moves no page — and a third round
|
|
33
|
+
* that still writes is named, never looped. With `--check` nothing is written; without it the pass holds the store,
|
|
34
|
+
* and what it saw answer and lay lands by the key that writes folds: which capsule rendered each region of a view and
|
|
35
|
+
* which release each dependency was laid from, each line once. What an act's gates saw answer lands only once its batch
|
|
36
|
+
* has landed, and never when it is refused.
|
|
37
|
+
*/
|
|
38
|
+
/**
|
|
39
|
+
* A check verifies existing receipts without running readers or refreshing hashes. Open regions or a different
|
|
40
|
+
* instrument refuse; an admitted fold must establish closure before it can be checked.
|
|
41
|
+
*/
|
|
42
|
+
async function checked(root, entry, under) {
|
|
43
|
+
const started = Date.now();
|
|
44
|
+
const store = storeOf(root);
|
|
45
|
+
if (!meets(root, store, under ?? '')) {
|
|
46
|
+
process.stderr.write(`${selfName()}: REFUSE·check receipts are open; a fold must close them\n`);
|
|
47
|
+
return 1;
|
|
48
|
+
}
|
|
49
|
+
const contract = wireLine(ownLock(), 'receipt-instrument');
|
|
50
|
+
const evidence = carriedFrom(store, (observeText(join(root, under ?? '', RECEIPTS)) ?? '').split('\n').filter(isWire));
|
|
51
|
+
const sealed = contract === undefined ? undefined : evidence?.find(line => fieldOf(line, 'scope') === fieldOf(contract, 'value') && fieldOf(line, 'measure') === 'digest');
|
|
52
|
+
const expectedInstrument = sealed === undefined ? instrumentKept(store) : fieldOf(sealed, 'value');
|
|
53
|
+
if (!expectedInstrument) {
|
|
54
|
+
process.stderr.write(`${selfName()}: REFUSE·check no verified instrument identity is available for these receipts\n`);
|
|
55
|
+
return 1;
|
|
56
|
+
}
|
|
57
|
+
const standing = emptyLedger(store) ? ownLockOf(root, under?.replace(/\/$/, '') ?? '') : standingOf(root).standing;
|
|
58
|
+
const instrument = sealed === undefined ? instrumentOf(store, entry, standing, root, ownLock()) : instrumentDigest(store, entry, [], ownRoot(), ownLock());
|
|
59
|
+
if (expectedInstrument !== instrument) {
|
|
60
|
+
process.stderr.write(`${selfName()}: REFUSE·check receipt instrument differs; a fold must close it\n`);
|
|
61
|
+
return 1;
|
|
62
|
+
}
|
|
63
|
+
const read = receiptsSeen();
|
|
64
|
+
const ms = Date.now() - started;
|
|
65
|
+
if (under === undefined)
|
|
66
|
+
process.stderr.write(`RECEIPTS ${read.count || receiptPlaces(root, store).length} read · 0 opened · ${ms} ms\n`);
|
|
67
|
+
else
|
|
68
|
+
process.stderr.write(`CLOSED ${under} · ${ms} ms\n`);
|
|
69
|
+
const seconds = actSeconds();
|
|
70
|
+
process.stderr.write(`COST act ${seconds} s\n`);
|
|
71
|
+
process.stdout.write(`PASS closed · ${under ? 1 : receiptPlaces(root, store).length} places · ${ms} ms\n`);
|
|
72
|
+
return 0;
|
|
73
|
+
}
|
|
74
|
+
export async function pass(root, entry, under, check) {
|
|
75
|
+
if (check) {
|
|
76
|
+
// Verification reads existing evidence; it grants no admission authority.
|
|
77
|
+
return emptyLedger(storeOf(root)) ? checked(root, entry, under) : underTheLock(storeOf(root), 'check', () => checked(root, entry, under));
|
|
78
|
+
}
|
|
79
|
+
if (!under && emptyLedger(storeOf(root))) {
|
|
80
|
+
sayStranger(root);
|
|
81
|
+
return 0;
|
|
82
|
+
}
|
|
83
|
+
const started = Date.now();
|
|
84
|
+
const run = async () => {
|
|
85
|
+
const swept = await sweep(root, entry, under, check, []);
|
|
86
|
+
for (const coordinate of check ? swept.written : [])
|
|
87
|
+
process.stdout.write(`DIFFERS ${coordinate}\n`);
|
|
88
|
+
const closed = meets(root, storeOf(root), under ?? '') && !swept.written.length;
|
|
89
|
+
const fold = foldPlace(root, entry, under, false, { standing: swept.standingAt.get(under ?? '') ?? standingOf(root, under), observed: swept.observed.length ? swept.observed : observedClaims(root, storeOf(root), { wait: false }), free: true });
|
|
90
|
+
for (const line of releasePolicyLines(fold.release))
|
|
91
|
+
process.stdout.write(`${line}\n`);
|
|
92
|
+
if (closed) {
|
|
93
|
+
instrumentKeep(storeOf(root), instrumentOf(storeOf(root), entry, standingOf(root).standing, root, ownLock()));
|
|
94
|
+
process.stdout.write(`PASS closed · ${swept.places} places · ${Date.now() - started} ms\n`);
|
|
95
|
+
return 0;
|
|
96
|
+
}
|
|
97
|
+
process.stdout.write(`PASS ${swept.same + swept.written.length} renders · ${swept.same} same${swept.written.length ? ` · ${swept.written.length} ${check ? 'differ' : 'still written after three rounds'}` : ''} · ${swept.places} places · ${Date.now() - started} ms\n`);
|
|
98
|
+
process.stdout.write(render(fold, [{ name: 'programs', at: 1 }]));
|
|
99
|
+
return swept.written.length ? 1 : 0;
|
|
100
|
+
};
|
|
101
|
+
return underTheLock(storeOf(root), 'pass', run);
|
|
102
|
+
}
|
|
103
|
+
export function landSaid(store, folder, lines) {
|
|
104
|
+
if (folder === undefined || !lines.length)
|
|
105
|
+
return;
|
|
106
|
+
const bare = (line) => ((got) => (got.kind === 'fact' ? canonical(Object.fromEntries(Object.entries(got.value.fields).filter(([name]) => !ENVELOPE.has(name) && name !== 'by'))) : line))(parse(line));
|
|
107
|
+
const held = new Set(ledgerLines(store, folder).filter((line) => /(?:^|\s)scope=re(?:ndered|solved)\//.test(line)).map(bare));
|
|
108
|
+
const fresh = [...new Set(lines.filter((line) => !held.has(bare(line))).map((line) => ((got) => (got.kind === 'fact' ? canonical({ ...got.value.fields, by: folder }) : line))(parse(line))))];
|
|
109
|
+
if (fresh.length)
|
|
110
|
+
land(store, folder, fresh);
|
|
111
|
+
}
|
|
112
|
+
async function sweep(root, entry, under, check, told, journal, given, keep, reach) {
|
|
113
|
+
const store = storeOf(root);
|
|
114
|
+
const digestAt = Date.now();
|
|
115
|
+
const met = told.length === 0 && reach === undefined && meets(root, store, under ?? '');
|
|
116
|
+
const sameInstrument = instrumentKept(store) === instrumentOf(store, entry, standingOf(root).standing, root, ownLock());
|
|
117
|
+
const missingView = () => {
|
|
118
|
+
if (!under || !worldAt(root, under.replace(/\/$/, '')))
|
|
119
|
+
return false;
|
|
120
|
+
const held = standingOf(root, under);
|
|
121
|
+
const views = viewsOf(held.standing);
|
|
122
|
+
return wordsOf(held.standing, 'views/world').some((name) => {
|
|
123
|
+
const view = views.get(name);
|
|
124
|
+
return view?.shape !== undefined && view.shape !== '' && observeText(join(root, under, view.shape)) === undefined;
|
|
125
|
+
});
|
|
126
|
+
};
|
|
127
|
+
const settled = met && sameInstrument && !missingView();
|
|
128
|
+
if (settled) {
|
|
129
|
+
const ms = Date.now() - digestAt;
|
|
130
|
+
const read = receiptsSeen();
|
|
131
|
+
const lock = algorithmCost();
|
|
132
|
+
const hash = Math.max(0, ms - lock);
|
|
133
|
+
if (lock > 0) {
|
|
134
|
+
noteIdle();
|
|
135
|
+
process.stderr.write(`RED fold/idle · ${idleCount()} · lock ${lock} ms · hash ${hash} ms · a fold over a closed region\n`);
|
|
136
|
+
}
|
|
137
|
+
if (under === undefined)
|
|
138
|
+
process.stderr.write(`RECEIPTS ${read.count} read · 0 opened · ${ms} ms\n`);
|
|
139
|
+
else
|
|
140
|
+
process.stderr.write(`CLOSED ${under} · lock ${lock} ms · hash ${hash} ms\n`);
|
|
141
|
+
const seconds = actSeconds();
|
|
142
|
+
process.stderr.write(`COST act ${seconds} s\n`);
|
|
143
|
+
return { same: 0, written: [], places: under === undefined ? read.count : 1, observed: [], standingAt: new Map(), folds: new Map(), said: [] };
|
|
144
|
+
}
|
|
145
|
+
layReleases(store, ownRoot(), ownLock(), ownStore());
|
|
146
|
+
const descent = reach ?? (under ? [under.replace(/\/$/, '')] : undefined);
|
|
147
|
+
const limited = descent !== undefined;
|
|
148
|
+
const observed = given ?? observedClaims(root, store, { wait: true, ...(descent !== undefined ? { reach: descent } : {}) });
|
|
149
|
+
const lock = standingOf(root, undefined, told);
|
|
150
|
+
landLaid(store, ownRoot(), ownLock(), writerFor(lock.standing, 'fold'));
|
|
151
|
+
const shaped = (standing) => [...viewsOf(standing).values()].flatMap((view) => ('regions' in view && view.shape ? [view] : []));
|
|
152
|
+
const views = shaped(lock.standing);
|
|
153
|
+
const tree = viewsOf(lock.lock);
|
|
154
|
+
const worldList = wordsOf(lock.standing, 'views/world');
|
|
155
|
+
const worldPlaces = new Set(worldsIn(root).map((name) => `${name}/`));
|
|
156
|
+
const resolved = (view) => {
|
|
157
|
+
const named = placesOf(lock.standing, view);
|
|
158
|
+
if (!worldList.includes(view.name))
|
|
159
|
+
return named;
|
|
160
|
+
const picked = named.filter((place) => worldPlaces.has(place));
|
|
161
|
+
return picked.length ? picked : [...worldPlaces];
|
|
162
|
+
};
|
|
163
|
+
const placeless = views.filter((view) => !worldList.includes(view.name) && !placesOf(lock.standing, view).length);
|
|
164
|
+
const holding = entriesIn(root).filter((entry) => entry.dir && placeless.some((view) => observeText(join(root, entry.name, view.shape)) !== undefined)).map((entry) => `${entry.name}/`);
|
|
165
|
+
const descended = (place) => !limited || descent.some((one) => one === '' || place === `${one}/` || place.startsWith(`${one}/`));
|
|
166
|
+
const reached = limited ? descent.filter((one) => one !== '').map((one) => (one.endsWith('/') ? one : `${one}/`)) : [];
|
|
167
|
+
const places = [...new Set([...views.flatMap(resolved), ...holding, ...reached])]
|
|
168
|
+
.filter((place) => place !== '' && (!under || place.startsWith(under)) && descended(place)).sort();
|
|
169
|
+
const standingAt = new Map([...places, ''].map((place) => [place, place ? standingOf(root, place, told) : lock]));
|
|
170
|
+
const writes = (place) => {
|
|
171
|
+
const { standing } = standingAt.get(place) ?? lock;
|
|
172
|
+
const mine = shaped(standing);
|
|
173
|
+
const own = worldAt(root, place.slice(0, -1)) ? ownLockOf(root, place.slice(0, -1)) : [];
|
|
174
|
+
const namedOf = (view) => placesOf(standing, view);
|
|
175
|
+
return mine.filter((view) => namedOf(view).includes(place) || (own.length > 0 && defaulted(standing, view, own, tree))
|
|
176
|
+
|| (!worldList.includes(view.name) && !namedOf(view).length && observeText(join(root, place, view.shape)) !== undefined && !mine.some((other) => other.shape === view.shape && namedOf(other).includes(place))));
|
|
177
|
+
};
|
|
178
|
+
let same = 0;
|
|
179
|
+
let written = [];
|
|
180
|
+
const read = new Set(ownLock().filter((line) => fieldOf(line, 'scope').startsWith('region/') && fieldOf(line, 'measure') === 'lines').flatMap((line) => fieldOf(line, 'value').split('|')));
|
|
181
|
+
const free = (place) => limited ? descended(place) : !sameInstrument || !meets(root, store, place);
|
|
182
|
+
const folds = new Map();
|
|
183
|
+
const answered = new Map();
|
|
184
|
+
const paint = (place, drawn, moved) => {
|
|
185
|
+
for (const view of drawn) {
|
|
186
|
+
if (view.missing.length) {
|
|
187
|
+
throw new Error(`REFUSE·view at ${place || '.'} · asks regions nothing renders: ${view.missing.join(' ')}`);
|
|
188
|
+
}
|
|
189
|
+
if (!check && view.said.length)
|
|
190
|
+
answered.set(`${place}${view.shape}`, view.said);
|
|
191
|
+
const coordinate = join(root, place, view.shape);
|
|
192
|
+
const leaf = ['s', 'v', 'g'].join('');
|
|
193
|
+
if (view.shape.endsWith(`.${leaf}`) && !view.text.includes(`<${leaf}`)) {
|
|
194
|
+
if (!check && observeText(coordinate) !== undefined) {
|
|
195
|
+
rmSync(coordinate, { force: true });
|
|
196
|
+
written.push(`${place}${view.shape}`);
|
|
197
|
+
}
|
|
198
|
+
continue;
|
|
199
|
+
}
|
|
200
|
+
if (observeText(coordinate) === view.text)
|
|
201
|
+
same += 1;
|
|
202
|
+
else if (check)
|
|
203
|
+
written.push(`${place}${view.shape}`);
|
|
204
|
+
else {
|
|
205
|
+
moved.at || (moved.at = observeText(coordinate) === undefined || read.has(view.shape));
|
|
206
|
+
const held = journal !== undefined && !journal.has(coordinate) ? observeFile(coordinate) : undefined;
|
|
207
|
+
const made = mkdirSync(dirname(coordinate), { recursive: true });
|
|
208
|
+
if (journal !== undefined && !journal.has(coordinate))
|
|
209
|
+
journal.set(coordinate, { held, made });
|
|
210
|
+
replaceWhole(coordinate, view.text);
|
|
211
|
+
written.push(`${place}${view.shape}`);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
};
|
|
215
|
+
for (let round = 1; round <= 3; round += 1) {
|
|
216
|
+
written = [];
|
|
217
|
+
same = 0;
|
|
218
|
+
const moved = { at: false };
|
|
219
|
+
const asked = [...places, ...(under || (limited && descent.length > 0 && !descent.includes('')) ? [] : [''])];
|
|
220
|
+
const open = asked.filter((place) => free(place));
|
|
221
|
+
for (const place of asked)
|
|
222
|
+
if (!open.includes(place))
|
|
223
|
+
same += writes(place).length;
|
|
224
|
+
if (!under && round === 1) {
|
|
225
|
+
if (!receiptsSeen().count)
|
|
226
|
+
meets(root, store, '');
|
|
227
|
+
const read = receiptsSeen();
|
|
228
|
+
process.stderr.write(`RECEIPTS ${read.count} read · ${open.length} opened · ${read.ms} ms\n`);
|
|
229
|
+
}
|
|
230
|
+
if (open.length)
|
|
231
|
+
process.stderr.write(`OPEN ${open.map((place) => place || '.').join(' ')}\n`);
|
|
232
|
+
const aside = open.filter((place) => place !== '' && !keep?.has(place) && !meets(root, store, place));
|
|
233
|
+
const drawn = aside.length > 1 ? await drawParallel(aside.map((place) => ({ root, entry, place, told, observed, views: writes(place) }))) : new Map();
|
|
234
|
+
for (const place of open) {
|
|
235
|
+
if (!check && place === '')
|
|
236
|
+
await holdRoot(store, 'join');
|
|
237
|
+
if (!check && !holds(store, place)) {
|
|
238
|
+
process.stderr.write(`OUTSIDE ${place} · not settled, and no region this act holds: the next pass renders it\n`);
|
|
239
|
+
continue;
|
|
240
|
+
}
|
|
241
|
+
const ready = drawn.get(place);
|
|
242
|
+
if (ready !== undefined) {
|
|
243
|
+
paint(place, ready, moved);
|
|
244
|
+
continue;
|
|
245
|
+
}
|
|
246
|
+
const standing = standingAt.get(place) ?? lock;
|
|
247
|
+
const fold = foldPlace(root, entry, place || undefined, false, { standing, observed, free: true });
|
|
248
|
+
if (keep?.has(place))
|
|
249
|
+
folds.set(place, fold);
|
|
250
|
+
paint(place, writes(place).map((view) => {
|
|
251
|
+
const missing = unrendered(fold, view);
|
|
252
|
+
return { shape: view.shape, missing, text: missing.length ? '' : viewOf(fold, view), said: missing.length ? [] : renderedOf(fold, view) };
|
|
253
|
+
}), moved);
|
|
254
|
+
}
|
|
255
|
+
if (check || !written.length)
|
|
256
|
+
break;
|
|
257
|
+
for (const coordinate of written)
|
|
258
|
+
process.stdout.write(`RENDERED ${coordinate} · round ${round}\n`);
|
|
259
|
+
if (!moved.at) {
|
|
260
|
+
same += written.length;
|
|
261
|
+
written = [];
|
|
262
|
+
break;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
const said = [...answered.values()].flat();
|
|
266
|
+
if (!check && journal === undefined)
|
|
267
|
+
landSaid(store, writerFor(lock.standing, 'fold'), [...resolvedOf(store, ownRoot(), ownLock(), ownStore()), ...said]);
|
|
268
|
+
return { same, written, places: places.length + (under ? 0 : 1), observed, standingAt, folds, said: journal === undefined ? [] : said };
|
|
269
|
+
}
|
|
270
|
+
function drawParallel(jobs) {
|
|
271
|
+
const out = new Map();
|
|
272
|
+
if (jobs.length < 2)
|
|
273
|
+
return Promise.resolve(out);
|
|
274
|
+
return new Promise((resolve, reject) => {
|
|
275
|
+
const workers = [];
|
|
276
|
+
let left = jobs.length;
|
|
277
|
+
let failed = false;
|
|
278
|
+
const refuse = (error) => {
|
|
279
|
+
if (failed)
|
|
280
|
+
return;
|
|
281
|
+
failed = true;
|
|
282
|
+
reject(error);
|
|
283
|
+
for (const worker of workers)
|
|
284
|
+
void worker.terminate();
|
|
285
|
+
};
|
|
286
|
+
try {
|
|
287
|
+
for (const job of jobs) {
|
|
288
|
+
const worker = new Worker(processOf('fold-worker', 'fold'), { execArgv: process.execArgv, workerData: job });
|
|
289
|
+
workers.push(worker);
|
|
290
|
+
worker.on('message', (msg) => out.set(msg.place, msg.texts));
|
|
291
|
+
worker.on('error', refuse);
|
|
292
|
+
worker.on('exit', (code) => {
|
|
293
|
+
if (failed)
|
|
294
|
+
return;
|
|
295
|
+
if (code !== 0 || !out.has(job.place)) {
|
|
296
|
+
refuse(new Error(`REFUSE·worker ${job.place} · exit ${code} · no completed render`));
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
299
|
+
left -= 1;
|
|
300
|
+
if (left === 0)
|
|
301
|
+
resolve(out);
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
catch (error) {
|
|
306
|
+
refuse(error instanceof Error ? error : new Error(String(error)));
|
|
307
|
+
}
|
|
308
|
+
});
|
|
309
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import type { ReleasePolicyResult } from '../fold/release-policy.ts';
|
|
2
|
+
import type { Fork } from '../fold/claims.ts';
|
|
3
|
+
import type { Packed } from '../fold/region.ts';
|
|
4
|
+
import type { OpenCell } from '../render/open.ts';
|
|
5
|
+
import type { SecondHead } from '../fold/second.ts';
|
|
6
|
+
import { renderTarget } from '../render/target.ts';
|
|
7
|
+
/**
|
|
8
|
+
* Everything the fold knows about one place of the tree, or about the whole tree: what every region is rendered from,
|
|
9
|
+
* whether it was read back from a key already answered rather than taken again, and the middle of the last ten folds
|
|
10
|
+
* of this place, those that had to fold apart from those that read. A region whose points are ledger lines that move
|
|
11
|
+
* at every beat is judged when it is read and never kept: a kept fold answers for the regions whose digest is in its
|
|
12
|
+
* key and for no others.
|
|
13
|
+
*/
|
|
14
|
+
export interface PlaceFold {
|
|
15
|
+
readonly release?: ReleasePolicyResult;
|
|
16
|
+
readonly root: string;
|
|
17
|
+
readonly store: string;
|
|
18
|
+
readonly under?: string;
|
|
19
|
+
readonly epoch: number;
|
|
20
|
+
readonly standing: readonly string[];
|
|
21
|
+
readonly claims: number;
|
|
22
|
+
readonly forks: readonly Fork[];
|
|
23
|
+
readonly grey: readonly string[];
|
|
24
|
+
readonly refused: readonly string[];
|
|
25
|
+
readonly vacuous: number;
|
|
26
|
+
readonly demands: readonly string[];
|
|
27
|
+
readonly paid: readonly string[];
|
|
28
|
+
readonly missing: readonly string[];
|
|
29
|
+
readonly history: readonly string[];
|
|
30
|
+
readonly observed: readonly string[];
|
|
31
|
+
readonly ceilings: {
|
|
32
|
+
readonly read: number;
|
|
33
|
+
readonly total: number;
|
|
34
|
+
readonly unread: readonly string[];
|
|
35
|
+
readonly vacuous: readonly string[];
|
|
36
|
+
readonly short: readonly string[];
|
|
37
|
+
readonly over: readonly string[];
|
|
38
|
+
};
|
|
39
|
+
readonly orphans: readonly string[];
|
|
40
|
+
readonly lock: number;
|
|
41
|
+
readonly signed: ReadonlyMap<string, number>;
|
|
42
|
+
readonly second: SecondHead;
|
|
43
|
+
readonly effects: string;
|
|
44
|
+
readonly costs: readonly number[];
|
|
45
|
+
readonly landed: number;
|
|
46
|
+
readonly kept?: boolean;
|
|
47
|
+
readonly key: string;
|
|
48
|
+
readonly instrument: string;
|
|
49
|
+
readonly own: Readonly<Record<string, number>>;
|
|
50
|
+
readonly open: readonly OpenCell[];
|
|
51
|
+
readonly beats: {
|
|
52
|
+
readonly kept: number;
|
|
53
|
+
readonly folded: number;
|
|
54
|
+
};
|
|
55
|
+
readonly regions?: Packed;
|
|
56
|
+
}
|
|
57
|
+
export declare function standingOf(root: string, under?: string, told?: readonly string[]): {
|
|
58
|
+
readonly standing: readonly string[];
|
|
59
|
+
readonly lock: readonly string[];
|
|
60
|
+
readonly rendered: ReturnType<typeof renderTarget>;
|
|
61
|
+
readonly owned: readonly string[];
|
|
62
|
+
};
|
|
63
|
+
export declare function coneOf(root: string, entry: string, ceilings: readonly string[], descent?: readonly string[]): {
|
|
64
|
+
readonly standing: readonly string[];
|
|
65
|
+
readonly observed: readonly string[];
|
|
66
|
+
readonly own: Readonly<Record<string, number>>;
|
|
67
|
+
readonly kept: boolean;
|
|
68
|
+
};
|
|
69
|
+
export declare const ownCeilings: (under?: string) => readonly string[];
|
|
70
|
+
export declare function foldPlace(root: string, entry: string, under?: string, look?: boolean, given?: {
|
|
71
|
+
readonly standing: ReturnType<typeof standingOf>;
|
|
72
|
+
readonly observed: readonly string[];
|
|
73
|
+
readonly free?: boolean;
|
|
74
|
+
}): PlaceFold;
|