@lapxo/bound 0.2.3 → 0.2.5
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/CITATION.cff +19 -0
- package/TARGET.bound +95 -39
- package/dist/cli/accept.js +72 -558
- package/dist/cli/act-result.js +3 -103
- package/dist/cli/args.js +3 -44
- package/dist/cli/custodian.js +2 -29
- package/dist/cli/host.js +4 -32
- package/dist/cli/names.js +1 -12
- package/dist/cli/native-descendant.js +1 -19
- package/dist/cli/native.js +3 -60
- package/dist/cli/observe.js +1 -13
- package/dist/cli/owner.js +3 -139
- package/dist/cli/pass.js +21 -321
- package/dist/cli/place.js +6 -281
- package/dist/cli/release-result.js +3 -80
- package/dist/cli/retained-judgment.js +1 -25
- package/dist/cli/standing-result.js +4 -21
- package/dist/cli/takes.js +10 -111
- package/dist/cli/verb.d.ts +1 -2
- package/dist/cli/verb.js +83 -683
- package/dist/engine/computation.js +2 -0
- package/dist/engine/conformance.js +4 -0
- package/dist/engine/encoding.js +1 -2
- package/dist/engine/index.d.ts +1 -13
- package/dist/engine/index.js +2 -11
- package/dist/engine/object.js +1 -38
- package/dist/engine/ports.js +2 -103
- package/dist/fold/admission-history.js +1 -17
- package/dist/fold/bootstrap-anchor.js +1 -28
- package/dist/fold/ceilings.js +1 -142
- package/dist/fold/claims.js +2 -131
- package/dist/fold/closed.js +24 -234
- package/dist/fold/configures.js +1 -25
- package/dist/fold/digests.js +10 -226
- package/dist/fold/evidence.js +1 -22
- package/dist/fold/fold-worker.js +1 -13
- package/dist/fold/kept.js +12 -173
- package/dist/fold/keys.js +3 -91
- package/dist/fold/laid.js +3 -90
- package/dist/fold/nos.js +1 -48
- package/dist/fold/object.js +1 -2
- package/dist/fold/observed.js +27 -534
- package/dist/fold/opened.js +1 -33
- package/dist/fold/own.js +3 -217
- package/dist/fold/paid.js +1 -92
- package/dist/fold/place-inputs.js +1 -70
- package/dist/fold/places.js +3 -100
- package/dist/fold/rank.js +1 -9
- package/dist/fold/reach.js +1 -83
- package/dist/fold/readers.js +6 -142
- package/dist/fold/receipts.js +1 -47
- package/dist/fold/region-declarations.js +1 -13
- package/dist/fold/region.js +8 -286
- package/dist/fold/release-policy.js +1 -101
- package/dist/fold/resolved.js +6 -145
- package/dist/fold/roles.js +2 -59
- package/dist/fold/second.js +4 -71
- package/dist/fold/shards.js +12 -100
- package/dist/fold/signed.js +3 -81
- package/dist/fold/signers.js +1 -54
- package/dist/fold/stranger.js +6 -24
- package/dist/fold/views.js +1 -66
- package/dist/fold/wire.js +1 -46
- package/dist/fold/zoom.js +3 -81
- package/dist/host/act-cost.js +1 -25
- package/dist/host/adapters/keys/ed25519.js +1 -30
- package/dist/host/adapters/modules/javascript.js +1 -50
- package/dist/host/adapters/transport/http.js +1 -28
- package/dist/host/admitted-place.js +1 -63
- package/dist/host/archive.js +1 -152
- package/dist/host/call-context.js +1 -0
- package/dist/host/capsule-process.js +2 -61
- package/dist/host/capsule.js +9 -168
- package/dist/host/cell-receipts.js +12 -128
- package/dist/host/commit-result.js +1 -0
- package/dist/host/content-needed.js +1 -8
- package/dist/host/content-store.js +1 -31
- package/dist/host/contextual-place.js +2 -77
- package/dist/host/digest.js +2 -15
- package/dist/host/hash.js +1 -9
- package/dist/host/host-controls.js +1 -0
- package/dist/host/in-process-act.js +3 -0
- package/dist/host/in-process-context.js +3 -0
- package/dist/host/in-process-root.js +6 -0
- package/dist/host/in-process.js +4 -0
- package/dist/host/index.d.ts +1 -0
- package/dist/host/index.js +1 -0
- package/dist/host/io.js +1 -11
- package/dist/host/lifetime.js +1 -79
- package/dist/host/located.js +6 -105
- package/dist/host/native/bounded-file.js +1 -32
- package/dist/host/native/command.js +1 -91
- package/dist/host/native/control-bridge-worker.js +2 -88
- package/dist/host/native/control-bridge.js +1 -110
- package/dist/host/native/control-selection.js +1 -32
- package/dist/host/native/custody.js +1 -91
- package/dist/host/native/deadline.js +1 -28
- package/dist/host/native/descendants.js +1 -528
- package/dist/host/native/generated-custodian.js +5 -267
- package/dist/host/native/generated.js +1 -379
- package/dist/host/native/image.js +1 -45
- package/dist/host/native/jobs.js +2 -148
- package/dist/host/native/loader.js +1 -269
- package/dist/host/native/materialize.js +1 -175
- package/dist/host/native/module-closure.js +1 -130
- package/dist/host/native/module-delivery.d.ts +1 -62
- package/dist/host/native/module-delivery.js +1 -151
- package/dist/host/native/module-syntax.js +1 -69
- package/dist/host/native/native-binary.js +1 -115
- package/dist/host/native/native-program.js +1 -72
- package/dist/host/native/node-jobs.js +3 -187
- package/dist/host/native/node-launch.js +1 -159
- package/dist/host/native/preload.js +1 -45
- package/dist/host/native/reading.js +1 -249
- package/dist/host/native/receipt-environment.js +1 -41
- package/dist/host/native/references.js +1 -30
- package/dist/host/native/root.js +1 -37
- package/dist/host/native/route.js +1 -216
- package/dist/host/native/runtime.js +1 -9
- package/dist/host/native-reader-context.js +1 -8
- package/dist/host/native-receiver.js +1 -0
- package/dist/host/objects.js +1 -38
- package/dist/host/order.js +1 -2
- package/dist/host/place-act-transaction.js +4 -0
- package/dist/host/place-clock-selection.js +5 -124
- package/dist/host/ports/ingress.js +2 -57
- package/dist/host/ports/local-act.js +5 -172
- package/dist/host/ports/place-act.js +1 -169
- package/dist/host/ports/publication.js +1 -56
- package/dist/host/ports/signing.js +1 -74
- package/dist/host/ports/store.js +1 -18
- package/dist/host/ports/transport.js +1 -48
- package/dist/host/process-lifetime.js +1 -17
- package/dist/host/provide.js +1 -183
- package/dist/host/read-only.js +1 -9
- package/dist/host/reading-closures.js +7 -227
- package/dist/host/reading-context.js +1 -53
- package/dist/host/reading-identity-types.js +0 -0
- package/dist/host/reading-identity.js +3 -0
- package/dist/host/reading-preparation.js +6 -0
- package/dist/host/reference-authenticated.d.ts +1 -40
- package/dist/host/reference-authenticated.js +3 -100
- package/dist/host/reference-contracts.js +1 -6
- package/dist/host/reference-export.js +6 -81
- package/dist/host/reference-native-api.d.ts +1 -3
- package/dist/host/reference-native-api.js +1 -3
- package/dist/host/reference-native.js +1 -74
- package/dist/host/reference-profile.js +1 -63
- package/dist/host/reference-standing.js +2 -18
- package/dist/host/reference.js +1 -24
- package/dist/host/refusals.js +2 -43
- package/dist/host/release.js +4 -256
- package/dist/host/root-clock-source.js +1 -305
- package/dist/host/selected-capsules.js +1 -50
- package/dist/host/selected-topos.js +1 -80
- package/dist/host/signature-era.js +1 -0
- package/dist/host/signing.js +2 -48
- package/dist/host/source-content.js +2 -110
- package/dist/host/standing-carrier.js +1 -0
- package/dist/host/standing-projection.js +1 -0
- package/dist/host/timing.js +1 -7
- package/dist/host/walk.js +20 -193
- package/dist/judge/late.js +1 -33
- package/dist/judge/take.js +2 -75
- package/dist/judge/widening.js +1 -25
- package/dist/land/act.js +8 -302
- package/dist/land/alias.js +1 -41
- package/dist/land/held.js +1 -23
- package/dist/land/inert.js +1 -33
- package/dist/land/leaving.js +1 -55
- package/dist/land/ledger.js +7 -188
- package/dist/land/owned.js +4 -66
- package/dist/land/sign.js +1 -53
- package/dist/land/standing-pin.js +1 -49
- package/dist/land/vouched.js +1 -112
- package/dist/land/withdraw.js +1 -24
- package/dist/observe/files.js +1 -88
- package/dist/observe/identity.js +1 -61
- package/dist/observe/match.js +1 -8
- package/dist/observe/regions.js +7 -306
- package/dist/observe/run.js +2 -40
- package/dist/observe/runner.js +4 -93
- package/dist/public-types.d.ts +1114 -0
- package/dist/render/capsule.js +1 -30
- package/dist/render/field.js +1 -40
- package/dist/render/names.js +1 -4
- package/dist/render/open.js +1 -61
- package/dist/render/page.js +1 -7
- package/dist/render/receipts.js +1 -47
- package/dist/render/shape.js +1 -74
- package/dist/render/target.js +4 -52
- package/dist/render/view.js +7 -197
- package/dist/vendor/50086958553b9376d600583f7dff354196bfeb41170fc6d1482ca7bea7c493e2.LICENSE +21 -0
- package/dist/vendor/b3e938d28f88a1851af303a40c749e12ebd5a686daac4f0b343b5cdd2e040419.LICENSE +21 -0
- package/dist/vendor/bundle.json +1 -0
- package/dist/vendor/library11.js +1 -0
- package/dist/vendor/object-provider.js +42 -0
- package/dist/vendor/runtime.js +38 -0
- package/dist/vendor/sources.json +1 -0
- package/dist/wasm/adapter.d.ts +1 -51
- package/dist/wasm/adapter.js +1 -180
- package/dist/wasm/node-worker.js +1 -13
- package/dist/wasm/object.js +1 -76
- package/dist/wasm/worker.js +1 -7
- package/docs/contracts/closure-delivery.md +1 -1
- package/docs/contracts/closure-delivery.publication.json +1 -1
- package/docs/contracts/conformance.json +142 -0
- package/docs/contracts/determinism.bound +9 -0
- package/docs/contracts/engine-object-projection.publication.json +1 -1
- package/docs/contracts/engine.md +21 -1
- package/docs/contracts/native-host.md +21 -0
- package/docs/contracts/native-host.publication.json +25 -0
- package/docs/contracts/native-publication.inventory.json +3 -1
- package/docs/contracts/optional-wasm-adapter.publication.json +1 -1
- package/docs/contracts/publication.inventory.json +29 -0
- package/docs/contracts/reading-identity.md +37 -0
- package/docs/contracts/reading-identity.publication.json +25 -0
- package/docs/contracts/refusals.bound +51 -0
- package/docs/contracts/refusals.md +18 -3
- package/docs/contracts/refusals.publication.json +2 -2
- package/docs/contracts/views.md +8 -0
- package/docs/guide.md +10 -0
- package/docs/reference.md +40 -0
- package/docs/why.md +109 -0
- package/package.json +37 -18
- package/receipts.bound +11675 -9725
- package/samples/native-host/no.json +10 -0
- package/samples/native-host/yes.json +20 -0
- package/samples/reading-identity/no.json +20 -0
- package/samples/reading-identity/yes.json +46 -0
- package/vectors/native/native-host.vector.json +62 -0
- package/vectors/native/reading-identity.vector.json +101 -0
- package/vectors/native/refusals.vector.json +104 -0
- package/vendor/syntax/LICENSE.md +7 -0
- package/vendor/syntax/parser.js +1 -0
- package/dist/engine/object.d.ts +0 -17
- package/dist/engine/ports.d.ts +0 -32
- package/dist/host/native/module-closure.d.ts +0 -35
- package/dist/host/reference-export.d.ts +0 -33
- package/dist/host/reference-native.d.ts +0 -30
- package/dist/host/reference-profile.d.ts +0 -23
- package/dist/host/refusals.d.ts +0 -14
package/dist/host/release.js
CHANGED
|
@@ -1,256 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
import { canonical } from '@lapxo/topos/wire';
|
|
6
|
-
import { fieldOf, isWire, isConfig } from "../fold/claims.js";
|
|
7
|
-
import { archiveCoordinate, archiveDestination, members } from "./archive.js";
|
|
8
|
-
import { blobAt, holdStore, landBlob, replaceWhole, storeAt } from "../land/ledger.js";
|
|
9
|
-
import { coordinatesUnder, observeFile, observeText } from "../observe/files.js";
|
|
10
|
-
export const laidAt = (store, digest) => storeAt(store, 'cas', 'laid', digest.replace(/^[^:]*:/, ''));
|
|
11
|
-
/** A host artifact binding, not a measured ceiling or an object act. */
|
|
12
|
-
export const isReleaseBinding = (line) => isConfig(line)
|
|
13
|
-
&& fieldOf(line, 'scope').startsWith('dep/') && fieldOf(line, 'role') === 'reads'
|
|
14
|
-
&& fieldOf(line, 'value') !== 'withdraw'
|
|
15
|
-
&& (fieldOf(line, 'measure') === 'digest' || /^[a-zA-Z][a-zA-Z0-9-]*:[a-f0-9]+$/.test(fieldOf(line, 'value')));
|
|
16
|
-
export const releasesOf = (lock) => lock.filter(isReleaseBinding);
|
|
17
|
-
const laidMark = '.laid';
|
|
18
|
-
const digestOf = (bytes) => `sha256:${createHash('sha256').update(bytes).digest('hex')}`;
|
|
19
|
-
const hashed = (digest, bytes) => createHash(digest.includes(':') ? digest.slice(0, digest.indexOf(':')) : 'sha256').update(bytes).digest('hex') === digest.replace(/^[^:]*:/, '');
|
|
20
|
-
const snapshotOf = (text) => text.split('\n').filter(isWire).filter((line) => fieldOf(line, 'measure') === 'digest' && fieldOf(line, 'scope').startsWith('file/'))
|
|
21
|
-
.map((line) => [fieldOf(line, 'scope').slice('file/'.length), fieldOf(line, 'value')]);
|
|
22
|
-
/** Mount an explicitly declared archive coordinate; never infer a package root. */
|
|
23
|
-
function mountedFiles(files, root) {
|
|
24
|
-
if (!root)
|
|
25
|
-
return files;
|
|
26
|
-
const prefix = archiveCoordinate(root) + '/';
|
|
27
|
-
if (!files.length || files.some(([name]) => !name.startsWith(prefix)))
|
|
28
|
-
throw new Error(`REFUSE·release archive root ${root} does not contain every member`);
|
|
29
|
-
return files.map(([name, digest]) => [archiveCoordinate(name.slice(prefix.length)), digest]);
|
|
30
|
-
}
|
|
31
|
-
function snapshotLines(files) {
|
|
32
|
-
return files.map(([coord, digest]) => canonical({
|
|
33
|
-
scope: `file/${coord}`, role: 'writes', form: 'alphabet', measure: 'digest', value: digest, at: `place:${digest}`, by: 'target',
|
|
34
|
-
})).join('\n') + (files.length ? '\n' : '');
|
|
35
|
-
}
|
|
36
|
-
function layDir(from, into) {
|
|
37
|
-
for (const coord of coordinatesUnder(from, from)) {
|
|
38
|
-
if (coord === laidMark)
|
|
39
|
-
continue;
|
|
40
|
-
const src = join(from, coord);
|
|
41
|
-
const dest = archiveDestination(into, coord);
|
|
42
|
-
mkdirSync(dirname(dest), { recursive: true });
|
|
43
|
-
if (existsSync(dest))
|
|
44
|
-
continue;
|
|
45
|
-
const bytes = observeFile(src);
|
|
46
|
-
if (bytes === undefined)
|
|
47
|
-
throw new Error(`REFUSE·release unreadable historical member ${coord}`);
|
|
48
|
-
writeFileSync(dest, bytes, { flag: 'wx' });
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
function swapPlace(place, next, digest, ready) {
|
|
52
|
-
const old = `${next}.old`;
|
|
53
|
-
if (lstatSync(old, { throwIfNoEntry: false }) !== undefined)
|
|
54
|
-
throw new Error(`REFUSE·release recovery required for ${old}`);
|
|
55
|
-
writeFileSync(join(next, laidMark), digest, { flag: 'wx' });
|
|
56
|
-
if (ready !== undefined) {
|
|
57
|
-
// Digest-addressed trees publish once. A verified concurrent winner is reused,
|
|
58
|
-
// never moved aside while another process may be importing its members.
|
|
59
|
-
if (ready()) {
|
|
60
|
-
rmSync(next, { recursive: true, force: true });
|
|
61
|
-
return;
|
|
62
|
-
}
|
|
63
|
-
try {
|
|
64
|
-
renameSync(next, place);
|
|
65
|
-
return;
|
|
66
|
-
}
|
|
67
|
-
catch (error) {
|
|
68
|
-
const code = error.code;
|
|
69
|
-
if (code !== 'EEXIST' && code !== 'ENOTEMPTY')
|
|
70
|
-
throw error;
|
|
71
|
-
if (ready()) {
|
|
72
|
-
rmSync(next, { recursive: true, force: true });
|
|
73
|
-
return;
|
|
74
|
-
}
|
|
75
|
-
// An existing invalid cache retains the repair/rollback path below.
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
let moved = false;
|
|
79
|
-
try {
|
|
80
|
-
if (lstatSync(place, { throwIfNoEntry: false }) !== undefined) {
|
|
81
|
-
renameSync(place, old);
|
|
82
|
-
moved = true;
|
|
83
|
-
}
|
|
84
|
-
renameSync(next, place);
|
|
85
|
-
}
|
|
86
|
-
catch (error) {
|
|
87
|
-
if (moved && lstatSync(place, { throwIfNoEntry: false }) === undefined)
|
|
88
|
-
renameSync(old, place);
|
|
89
|
-
throw error;
|
|
90
|
-
}
|
|
91
|
-
if (moved)
|
|
92
|
-
rmSync(old, { recursive: true, force: true });
|
|
93
|
-
}
|
|
94
|
-
function nextPlace(place) {
|
|
95
|
-
const next = `${place}.${process.pid}`;
|
|
96
|
-
mkdirSync(dirname(place), { recursive: true });
|
|
97
|
-
mkdirSync(next); // An abandoned staging directory is never silently reused.
|
|
98
|
-
return next;
|
|
99
|
-
}
|
|
100
|
-
function layFiles(store, own, files, into) {
|
|
101
|
-
const held = files.map(([coordinate, digest]) => {
|
|
102
|
-
const coord = archiveCoordinate(coordinate);
|
|
103
|
-
const dest = archiveDestination(into, coord);
|
|
104
|
-
const src = [blobAt(own, digest), blobAt(store, digest)].find(existsSync);
|
|
105
|
-
if (src === undefined)
|
|
106
|
-
throw new Error(`REFUSE·release missing blob ${digest} for ${coord}`);
|
|
107
|
-
const bytes = observeFile(src);
|
|
108
|
-
if (bytes === undefined || !hashed(digest, bytes))
|
|
109
|
-
throw new Error(`REFUSE·release blob ${digest} for ${coord} does not hold`);
|
|
110
|
-
return { dest, bytes };
|
|
111
|
-
});
|
|
112
|
-
for (const { dest, bytes } of held) {
|
|
113
|
-
mkdirSync(dirname(dest), { recursive: true });
|
|
114
|
-
// Materialized files may be edited by their consumer; never share writable inodes with the CAS.
|
|
115
|
-
writeFileSync(dest, bytes, { flag: 'wx' });
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
function layoutMatches(place, files) {
|
|
119
|
-
return files.every(([coord, digest]) => {
|
|
120
|
-
const path = join(place, archiveCoordinate(coord));
|
|
121
|
-
const st = lstatSync(path, { throwIfNoEntry: false });
|
|
122
|
-
const bytes = st?.isFile() ? observeFile(path) : undefined;
|
|
123
|
-
return bytes !== undefined && hashed(digest, bytes);
|
|
124
|
-
});
|
|
125
|
-
}
|
|
126
|
-
function sameSnapshot(actual, expected) {
|
|
127
|
-
const named = new Map(actual);
|
|
128
|
-
return named.size === actual.length && actual.length === expected.length && expected.every(([name, digest]) => named.get(name) === digest);
|
|
129
|
-
}
|
|
130
|
-
function keepSnapshot(store, digest, bytes) {
|
|
131
|
-
const files = members(bytes).map((one) => {
|
|
132
|
-
const file = digestOf(one.bytes);
|
|
133
|
-
landBlob(store, file, one.bytes);
|
|
134
|
-
return [one.name, file];
|
|
135
|
-
});
|
|
136
|
-
const text = snapshotLines(files);
|
|
137
|
-
replaceWhole(laidAt(store, digest), text);
|
|
138
|
-
holdStore(store, 'laid', digest, Buffer.byteLength(text));
|
|
139
|
-
return files;
|
|
140
|
-
}
|
|
141
|
-
/**
|
|
142
|
-
* What the host does before anything runs: each release the instrument's own lock names by digest is a snapshot of
|
|
143
|
-
* lines and digests, kept by digest, and the place the line needs is a materialization of verified CAS bytes. Materialized files do not share writable inodes with the store. A blob is laid only when its bytes hash to the digest that names it. What stands laid is
|
|
144
|
-
* said by resolvedOf: a line for each release whose blob hashes to its name and whose place, or the cas when its line
|
|
145
|
-
* needs none, holds it.
|
|
146
|
-
*/
|
|
147
|
-
export function layReleases(store, own, lock, contentStore = own) {
|
|
148
|
-
let laid = 0;
|
|
149
|
-
for (const line of releasesOf(lock)) {
|
|
150
|
-
// A release catalog entry without a mount is materialized only when requested.
|
|
151
|
-
// Its absence must not block an unrelated place's fold.
|
|
152
|
-
const needs = fieldOf(line, 'needs');
|
|
153
|
-
if (!needs)
|
|
154
|
-
continue;
|
|
155
|
-
const digest = fieldOf(line, 'value');
|
|
156
|
-
const at = laidAt(store, digest);
|
|
157
|
-
const dir = lstatSync(at, { throwIfNoEntry: false })?.isDirectory() ?? false;
|
|
158
|
-
const bytes = observeFile(blobAt(contentStore, digest));
|
|
159
|
-
const named = digest.replace(/^[^:]*:/, '');
|
|
160
|
-
if (bytes !== undefined && !hashed(digest, bytes)) {
|
|
161
|
-
throw new Error(`REFUSE·release ${fieldOf(line, 'scope')} · blob ${named.slice(0, 12)} hashes to ${digestOf(bytes).slice(7, 19)} · not laid`);
|
|
162
|
-
}
|
|
163
|
-
if (bytes !== undefined && !dir && observeText(at) === undefined)
|
|
164
|
-
keepSnapshot(store, digest, bytes);
|
|
165
|
-
const current = observeText(at);
|
|
166
|
-
if (bytes !== undefined && current !== undefined && !dir && !sameSnapshot(snapshotOf(current), members(bytes).map((one) => [one.name, digestOf(one.bytes)])))
|
|
167
|
-
throw new Error(`REFUSE·release snapshot for ${digest} omits or changes archive members; explicit rebuild required`);
|
|
168
|
-
if (bytes === undefined && !dir && current === undefined)
|
|
169
|
-
throw new ContentNeeded(digest, `REFUSE·release ${fieldOf(line, 'scope')} has no available archive or snapshot`);
|
|
170
|
-
const root = fieldOf(line, 'shape');
|
|
171
|
-
const place = join(own, archiveCoordinate(needs));
|
|
172
|
-
const link = lstatSync(place, { throwIfNoEntry: false });
|
|
173
|
-
if (link?.isSymbolicLink() && !realpathSync(place).includes(`${sep}cas${sep}laid${sep}`))
|
|
174
|
-
continue;
|
|
175
|
-
if (dir) {
|
|
176
|
-
const files = bytes === undefined ? undefined : mountedFiles(members(bytes).map((one) => [one.name, digestOf(one.bytes)]), root);
|
|
177
|
-
if (!lstatSync(place, { throwIfNoEntry: false })?.isSymbolicLink() && observeText(join(place, laidMark)) === digest && files !== undefined && layoutMatches(place, files))
|
|
178
|
-
continue;
|
|
179
|
-
const next = nextPlace(place);
|
|
180
|
-
try {
|
|
181
|
-
layDir(root ? join(at, archiveCoordinate(root)) : at, next);
|
|
182
|
-
swapPlace(place, next, digest);
|
|
183
|
-
}
|
|
184
|
-
catch (error) {
|
|
185
|
-
rmSync(next, { recursive: true, force: true });
|
|
186
|
-
throw error;
|
|
187
|
-
}
|
|
188
|
-
laid += 1;
|
|
189
|
-
continue;
|
|
190
|
-
}
|
|
191
|
-
const snap = observeText(at);
|
|
192
|
-
if (snap === undefined)
|
|
193
|
-
throw new Error(`REFUSE·release ${fieldOf(line, 'scope')} has no available snapshot`);
|
|
194
|
-
const mounted = mountedFiles(snapshotOf(snap), root);
|
|
195
|
-
if (!lstatSync(place, { throwIfNoEntry: false })?.isSymbolicLink() && observeText(join(place, laidMark)) === digest && layoutMatches(place, mounted))
|
|
196
|
-
continue;
|
|
197
|
-
const next = nextPlace(place);
|
|
198
|
-
try {
|
|
199
|
-
layFiles(store, contentStore, mounted, next);
|
|
200
|
-
swapPlace(place, next, digest);
|
|
201
|
-
}
|
|
202
|
-
catch (error) {
|
|
203
|
-
rmSync(next, { recursive: true, force: true });
|
|
204
|
-
throw error;
|
|
205
|
-
}
|
|
206
|
-
laid += 1;
|
|
207
|
-
}
|
|
208
|
-
return laid;
|
|
209
|
-
}
|
|
210
|
-
export const resolvedOf = (store, own, lock, contentStore = own) => releasesOf(lock).flatMap((line) => {
|
|
211
|
-
const digest = fieldOf(line, 'value');
|
|
212
|
-
const bytes = observeFile(blobAt(contentStore, digest));
|
|
213
|
-
const ok = bytes !== undefined && hashed(digest, bytes);
|
|
214
|
-
const needs = fieldOf(line, 'needs');
|
|
215
|
-
const place = needs ? join(own, archiveCoordinate(needs)) : '';
|
|
216
|
-
const at = laidAt(store, digest);
|
|
217
|
-
const laid = needs
|
|
218
|
-
? observeText(join(place, laidMark)) === digest || (existsSync(place) && existsSync(at) && realpathSync(place) === realpathSync(at))
|
|
219
|
-
: existsSync(at);
|
|
220
|
-
const expected = ok ? members(bytes).map((one) => [one.name, digestOf(one.bytes)]) : undefined;
|
|
221
|
-
const historicalDirectory = lstatSync(at, { throwIfNoEntry: false })?.isDirectory() === true;
|
|
222
|
-
const snapshot = observeText(at);
|
|
223
|
-
const complete = expected !== undefined && (needs
|
|
224
|
-
? layoutMatches(place, mountedFiles(expected, fieldOf(line, 'shape')))
|
|
225
|
-
: historicalDirectory ? layoutMatches(at, expected) : snapshot !== undefined && sameSnapshot(snapshotOf(snapshot), expected));
|
|
226
|
-
return ok && laid && complete ? [canonical({ scope: `resolved/${fieldOf(line, 'scope').slice('dep/'.length)}`, role: 'writes', form: 'alphabet', measure: 'digest', value: digest, at: `place:${digest}`, by: 'target' })] : [];
|
|
227
|
-
});
|
|
228
|
-
/** A host loads verified release bytes from a runtime tree, never interprets a snapshot file as a directory. */
|
|
229
|
-
export function runtimeTreeAt(store, own, digest, contentStore = own, root = '') {
|
|
230
|
-
const named = /^([a-zA-Z0-9-]+):([a-fA-F0-9]+)$/.exec(digest);
|
|
231
|
-
if (named === null)
|
|
232
|
-
throw new Error(`REFUSE·release invalid digest ${digest}`);
|
|
233
|
-
const bytes = observeFile(blobAt(contentStore, digest));
|
|
234
|
-
if (bytes === undefined)
|
|
235
|
-
return undefined;
|
|
236
|
-
if (!hashed(digest, bytes))
|
|
237
|
-
throw new Error(`REFUSE·release ${digest} does not name its available bytes`);
|
|
238
|
-
const files = members(bytes).map((one) => [one.name, digestOf(one.bytes)]);
|
|
239
|
-
mountedFiles(files, root); // Validate the same declared projection used for installation.
|
|
240
|
-
const place = storeAt(store, 'cas', 'runtime', named[1], named[2]);
|
|
241
|
-
const selected = root ? join(place, archiveCoordinate(root)) : place;
|
|
242
|
-
if (observeText(join(place, laidMark)) === digest && layoutMatches(place, files))
|
|
243
|
-
return selected;
|
|
244
|
-
for (const member of members(bytes))
|
|
245
|
-
landBlob(store, digestOf(member.bytes), member.bytes);
|
|
246
|
-
const next = nextPlace(place);
|
|
247
|
-
try {
|
|
248
|
-
layFiles(store, own, files, next);
|
|
249
|
-
swapPlace(place, next, digest, () => observeText(join(place, laidMark)) === digest && layoutMatches(place, files));
|
|
250
|
-
}
|
|
251
|
-
catch (error) {
|
|
252
|
-
rmSync(next, { recursive: true, force: true });
|
|
253
|
-
throw error;
|
|
254
|
-
}
|
|
255
|
-
return selected;
|
|
256
|
-
}
|
|
1
|
+
var te=Object.defineProperty;var l=(e,r)=>te(e,"name",{value:r,configurable:!0});import{ContentNeeded as ne}from"./content-needed.js";import{createHash as _}from"node:crypto";import{existsSync as I,lstatSync as y,mkdirSync as D,realpathSync as z,renameSync as L,rmSync as F,writeFileSync as B}from"node:fs";import{dirname as C,join as m,sep as P}from"node:path";import{library11_canonical as G}from"../vendor/runtime.js";import{fieldOf as u,isWire as oe,isConfig as se}from"../fold/claims.js";import{archiveCoordinate as w,archiveDestination as J,members as g}from"./archive.js";import{blobAt as k,holdStore as ie,landBlob as K,replaceWhole as ce,storeAt as Q}from"../land/ledger.js";import{coordinatesUnder as ae,observeFile as R,observeText as b}from"../observe/files.js";const Z=l((e,r)=>Q(e,"cas","laid",r.replace(/^[^:]*:/,"")),"laidAt"),fe=l(e=>se(e)&&u(e,"scope").startsWith("dep/")&&u(e,"role")==="reads"&&u(e,"value")!=="withdraw"&&(u(e,"measure")==="digest"||/^[a-zA-Z][a-zA-Z0-9-]*:[a-f0-9]+$/.test(u(e,"value"))),"isReleaseBinding"),V=l(e=>e.filter(fe),"releasesOf"),$=".laid",S=l(e=>`sha256:${_("sha256").update(e).digest("hex")}`,"digestOf"),A=l((e,r)=>_(e.includes(":")?e.slice(0,e.indexOf(":")):"sha256").update(r).digest("hex")===e.replace(/^[^:]*:/,""),"hashed"),j=l(e=>e.split(`
|
|
2
|
+
`).filter(oe).filter(r=>u(r,"measure")==="digest"&&u(r,"scope").startsWith("file/")).map(r=>[u(r,"scope").slice(5),u(r,"value")]),"snapshotOf");function M(e,r){if(!r)return e;const t=w(r)+"/";if(!e.length||e.some(([c])=>!c.startsWith(t)))throw new Error(`REFUSE\xB7release archive root ${r} does not contain every member`);return e.map(([c,n])=>[w(c.slice(t.length)),n])}l(M,"mountedFiles");function le(e){return e.map(([r,t])=>G({scope:`file/${r}`,role:"writes",form:"alphabet",measure:"digest",value:t,at:`place:${t}`,by:"target"})).join(`
|
|
3
|
+
`)+(e.length?`
|
|
4
|
+
`:"")}l(le,"snapshotLines");function ue(e,r){for(const t of ae(e,e)){if(t===$)continue;const c=m(e,t),n=J(r,t);if(D(C(n),{recursive:!0}),I(n))continue;const o=R(c);if(o===void 0)throw new Error(`REFUSE\xB7release unreadable historical member ${t}`);B(n,o,{flag:"wx"})}}l(ue,"layDir");function q(e,r,t,c){const n=`${r}.old`;if(y(n,{throwIfNoEntry:!1})!==void 0)throw new Error(`REFUSE\xB7release recovery required for ${n}`);if(B(m(r,$),t,{flag:"wx"}),c!==void 0){if(c()){F(r,{recursive:!0,force:!0});return}try{L(r,e);return}catch(s){const i=s.code;if(i!=="EEXIST"&&i!=="ENOTEMPTY")throw s;if(c()){F(r,{recursive:!0,force:!0});return}}}let o=!1;try{y(e,{throwIfNoEntry:!1})!==void 0&&(L(e,n),o=!0),L(r,e)}catch(s){throw o&&y(e,{throwIfNoEntry:!1})===void 0&&L(n,e),s}o&&F(n,{recursive:!0,force:!0})}l(q,"swapPlace");function H(e){const r=`${e}.${process.pid}`;return D(C(e),{recursive:!0}),D(r),r}l(H,"nextPlace");function ee(e,r,t,c){const n=t.map(([o,s])=>{const i=w(o),f=J(c,i),d=[k(r,s),k(e,s)].find(I);if(d===void 0)throw new Error(`REFUSE\xB7release missing blob ${s} for ${i}`);const a=R(d);if(a===void 0||!A(s,a))throw new Error(`REFUSE\xB7release blob ${s} for ${i} does not hold`);return{dest:f,bytes:a}});for(const{dest:o,bytes:s}of n)D(C(o),{recursive:!0}),B(o,s,{flag:"wx"})}l(ee,"layFiles");function U(e,r){return r.every(([t,c])=>{const n=m(e,w(t)),s=y(n,{throwIfNoEntry:!1})?.isFile()?R(n):void 0;return s!==void 0&&A(c,s)})}l(U,"layoutMatches");function re(e,r){const t=new Map(e);return t.size===e.length&&e.length===r.length&&r.every(([c,n])=>t.get(c)===n)}l(re,"sameSnapshot");function de(e,r,t){const c=g(t).map(o=>{const s=S(o.bytes);return K(e,s,o.bytes),[o.name,s]}),n=le(c);return ce(Z(e,r),n),ie(e,"laid",r,Buffer.byteLength(n)),c}l(de,"keepSnapshot");function xe(e,r,t,c=r){let n=0;for(const o of V(t)){const s=u(o,"needs");if(!s)continue;const i=u(o,"value"),f=Z(e,i),d=y(f,{throwIfNoEntry:!1})?.isDirectory()??!1,a=R(k(c,i)),p=i.replace(/^[^:]*:/,"");if(a!==void 0&&!A(i,a))throw new Error(`REFUSE\xB7release ${u(o,"scope")} \xB7 blob ${p.slice(0,12)} hashes to ${S(a).slice(7,19)} \xB7 not laid`);a!==void 0&&!d&&b(f)===void 0&&de(e,i,a);const E=b(f);if(a!==void 0&&E!==void 0&&!d&&!re(j(E),g(a).map(v=>[v.name,S(v.bytes)])))throw new Error(`REFUSE\xB7release snapshot for ${i} omits or changes archive members; explicit rebuild required`);if(a===void 0&&!d&&E===void 0)throw new ne(i,`REFUSE\xB7release ${u(o,"scope")} has no available archive or snapshot`);const x=u(o,"shape"),h=m(r,w(s));if(y(h,{throwIfNoEntry:!1})?.isSymbolicLink()&&!z(h).includes(`${P}cas${P}laid${P}`))continue;if(d){const v=a===void 0?void 0:M(g(a).map(O=>[O.name,S(O.bytes)]),x);if(!y(h,{throwIfNoEntry:!1})?.isSymbolicLink()&&b(m(h,$))===i&&v!==void 0&&U(h,v))continue;const W=H(h);try{ue(x?m(f,w(x)):f,W),q(h,W,i)}catch(O){throw F(W,{recursive:!0,force:!0}),O}n+=1;continue}const N=b(f);if(N===void 0)throw new Error(`REFUSE\xB7release ${u(o,"scope")} has no available snapshot`);const Y=M(j(N),x);if(!y(h,{throwIfNoEntry:!1})?.isSymbolicLink()&&b(m(h,$))===i&&U(h,Y))continue;const T=H(h);try{ee(e,c,Y,T),q(h,T,i)}catch(v){throw F(T,{recursive:!0,force:!0}),v}n+=1}return n}l(xe,"layReleases");const Fe=l((e,r,t,c=r)=>V(t).flatMap(n=>{const o=u(n,"value"),s=R(k(c,o)),i=s!==void 0&&A(o,s),f=u(n,"needs"),d=f?m(r,w(f)):"",a=Z(e,o),p=f?b(m(d,$))===o||I(d)&&I(a)&&z(d)===z(a):I(a),E=i?g(s).map(N=>[N.name,S(N.bytes)]):void 0,x=y(a,{throwIfNoEntry:!1})?.isDirectory()===!0,h=b(a),X=E!==void 0&&(f?U(d,M(E,u(n,"shape"))):x?U(a,E):h!==void 0&&re(j(h),E));return i&&p&&X?[G({scope:`resolved/${u(n,"scope").slice(4)}`,role:"writes",form:"alphabet",measure:"digest",value:o,at:`place:${o}`,by:"target"})]:[]}),"resolvedOf");function ge(e,r,t,c=r,n=""){const o=/^([a-zA-Z0-9-]+):([a-fA-F0-9]+)$/.exec(t);if(o===null)throw new Error(`REFUSE\xB7release invalid digest ${t}`);const s=R(k(c,t));if(s===void 0)return;if(!A(t,s))throw new Error(`REFUSE\xB7release ${t} does not name its available bytes`);const i=g(s).map(p=>[p.name,S(p.bytes)]);M(i,n);const f=Q(e,"cas","runtime",o[1],o[2]),d=n?m(f,w(n)):f;if(b(m(f,$))===t&&U(f,i))return d;for(const p of g(s))K(e,S(p.bytes),p.bytes);const a=H(f);try{ee(e,r,i,a),q(f,a,t,()=>b(m(f,$))===t&&U(f,i))}catch(p){throw F(a,{recursive:!0,force:!0}),p}return d}l(ge,"runtimeTreeAt");export{fe as isReleaseBinding,Z as laidAt,xe as layReleases,V as releasesOf,Fe as resolvedOf,ge as runtimeTreeAt};
|
|
@@ -1,305 +1 @@
|
|
|
1
|
-
import { existsSync, opendirSync, statSync } from 'node:fs';
|
|
2
|
-
import { join, basename } from 'node:path';
|
|
3
|
-
/** Actual stored Root cursor. Inputs are fixed host selections from admitted
|
|
4
|
-
* lines, never packet epochs, receipt maxima, or caller-supplied verification. */
|
|
5
|
-
import { committedLocalActs, actLines } from "./ports/local-act.js";
|
|
6
|
-
import { verifiedLegacyActResult } from "../cli/act-result.js";
|
|
7
|
-
import { admissionAnchor, admissionHistoryAt } from "../fold/admission-history.js";
|
|
8
|
-
import { authorityFor } from "../fold/signers.js";
|
|
9
|
-
import { eraOf } from "../fold/digests.js";
|
|
10
|
-
import { fieldOf, foldClaims, isConfig, selfName } from "../fold/claims.js";
|
|
11
|
-
import { authenticatedRootStanding } from "./admitted-place.js";
|
|
12
|
-
import { verifiesOwnerLine } from "../land/sign.js";
|
|
13
|
-
import { placesIn } from "../fold/places.js";
|
|
14
|
-
import { appliedHistoryIn } from "../fold/signed.js";
|
|
15
|
-
import { storeOf } from "../land/ledger.js";
|
|
16
|
-
import { hostClockSelection } from "./place-clock-selection.js";
|
|
17
|
-
import { readPlaceActContext, placeActReceiptsOf } from '@lapxo/topos/core';
|
|
18
|
-
import { placeActResult, placeActHeads } from "./ports/place-act.js";
|
|
19
|
-
import { rootAuthorityProtection } from "../land/act.js";
|
|
20
|
-
import { readOnly } from "./read-only.js";
|
|
21
|
-
const fail = (s) => { throw Error('REFUSE·Root cursor ' + s); };
|
|
22
|
-
function sourceSelection(selected) {
|
|
23
|
-
const b = selected?.resources;
|
|
24
|
-
if (!b || Object.keys(b).sort().join(',') !== 'acts,bytes,lines,places' || Object.values(b).some(n => !Number.isSafeInteger(n) || n < 1) || typeof selected.selector !== 'string' || !selected.selector || typeof selected.profileSelector !== 'string' || !selected.profileSelector || !selected.bootstrapCheckpoint || Object.keys(selected.bootstrapCheckpoint).sort().join(',') !== 'measure,scope' || typeof selected.bootstrapCheckpoint.scope !== 'string' || !selected.bootstrapCheckpoint.scope || typeof selected.bootstrapCheckpoint.measure !== 'string' || !selected.bootstrapCheckpoint.measure)
|
|
25
|
-
fail('missing admitted source selection');
|
|
26
|
-
if (!selected.inherited?.clock || Object.keys(selected.inherited.clock).sort().join(',') !== 'bytes,placements,policies,proofs,records,signers' || !selected.inherited.storage || Object.keys(selected.inherited.storage).sort().join(',') !== 'acts,bytes' || [...Object.values(selected.inherited.clock), ...Object.values(selected.inherited.storage)].some(n => !Number.isSafeInteger(n) || n < 1))
|
|
27
|
-
fail('missing inherited source budgets');
|
|
28
|
-
return b;
|
|
29
|
-
}
|
|
30
|
-
function boundedInventory(root, selected) {
|
|
31
|
-
const b = sourceSelection(selected), store = storeOf(root);
|
|
32
|
-
let bytes = 0, files = 0, entries = 0;
|
|
33
|
-
const limit = b.acts * (b.places + 2) + b.lines;
|
|
34
|
-
if (!Number.isSafeInteger(limit))
|
|
35
|
-
fail('source file bound');
|
|
36
|
-
const visit = (path) => { if (!existsSync(path))
|
|
37
|
-
return; const s = statSync(path); if (s.isDirectory()) {
|
|
38
|
-
const directory = opendirSync(path);
|
|
39
|
-
try {
|
|
40
|
-
for (let entry = directory.readSync(); entry; entry = directory.readSync()) {
|
|
41
|
-
if (++entries > limit)
|
|
42
|
-
fail('source entry budget');
|
|
43
|
-
if (basename(path) === 'ledger' && entry.name === 'place-acts')
|
|
44
|
-
continue;
|
|
45
|
-
visit(join(path, entry.name));
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
finally {
|
|
49
|
-
directory.closeSync();
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
else {
|
|
53
|
-
if (!s.isFile() || ++files > limit || (bytes += s.size) > b.bytes)
|
|
54
|
-
fail('source file/byte budget');
|
|
55
|
-
} };
|
|
56
|
-
visit(join(store, 'ledger'));
|
|
57
|
-
visit(join(store, selfName() + '.keys'));
|
|
58
|
-
}
|
|
59
|
-
function current(root, selected) {
|
|
60
|
-
const b = sourceSelection(selected);
|
|
61
|
-
boundedInventory(root, selected);
|
|
62
|
-
const history = admissionHistoryAt(root, Number.MAX_SAFE_INTEGER);
|
|
63
|
-
if (history.length > b.lines)
|
|
64
|
-
fail('history line budget');
|
|
65
|
-
let bytes = 0;
|
|
66
|
-
for (const line of history)
|
|
67
|
-
if ((bytes += Buffer.byteLength(line)) > b.bytes)
|
|
68
|
-
fail('history byte budget');
|
|
69
|
-
const acts = committedLocalActs(storeOf(root));
|
|
70
|
-
if (acts.length > b.acts)
|
|
71
|
-
fail('act inventory budget');
|
|
72
|
-
const rooted = acts.flatMap(a => { const roots = a.bundle.placements?.filter(p => p.place === '.') ?? []; if (!roots.some(p => actLines(p.records).some(isConfig)))
|
|
73
|
-
return []; const verified = verifiedLegacyActResult(root, a.identity); return [{ ...verified, identity: a.identity, roots }]; });
|
|
74
|
-
// Comparison is over verified committed Root CONFIGURATION acts only. This
|
|
75
|
-
// is the existing local-act cursor, not a new maximum over arbitrary records.
|
|
76
|
-
rooted.sort((a, b) => a.epoch - b.epoch);
|
|
77
|
-
if (rooted.some((a, i) => i > 0 && a.epoch === rooted[i - 1].epoch && a.identity !== rooted[i - 1].identity))
|
|
78
|
-
fail('ambiguous committed Root cursor');
|
|
79
|
-
const latest = rooted.at(-1), anchor = admissionAnchor(root);
|
|
80
|
-
let checkpoint;
|
|
81
|
-
if (latest) {
|
|
82
|
-
const rows = latest.roots.flatMap(p => actLines(p.records)).filter(isConfig);
|
|
83
|
-
if (!rows.length)
|
|
84
|
-
fail('verified Root act has no configuration checkpoint');
|
|
85
|
-
checkpoint = rows[0];
|
|
86
|
-
}
|
|
87
|
-
else {
|
|
88
|
-
const rows = history.filter(l => fieldOf(l, 'scope') === selected.bootstrapCheckpoint.scope && fieldOf(l, 'measure') === selected.bootstrapCheckpoint.measure);
|
|
89
|
-
if (rows.length !== 1)
|
|
90
|
-
fail('bootstrap checkpoint missing/ambiguous');
|
|
91
|
-
checkpoint = rows[0];
|
|
92
|
-
}
|
|
93
|
-
const epoch = Number(fieldOf(checkpoint, 'epoch'));
|
|
94
|
-
if (!Number.isSafeInteger(epoch) || epoch < 0)
|
|
95
|
-
fail('unknown committed Root epoch');
|
|
96
|
-
// This synchronous observation already read the full current inventory. Select
|
|
97
|
-
// its admission prefix without opening that same inventory a second time;
|
|
98
|
-
// the next current() invocation still reads and authenticates fresh bytes.
|
|
99
|
-
const prefix = history.filter(line => !isConfig(line) || Number(fieldOf(line, 'epoch')) <= epoch), algorithms = eraOf(foldClaims(prefix.filter(l => fieldOf(l, 'scope').startsWith('wire/'))).standing).admitted;
|
|
100
|
-
const auth = authorityFor(prefix, anchor, algorithms);
|
|
101
|
-
if (auth.of(checkpoint).kind !== 'admitted')
|
|
102
|
-
fail('checkpoint is not authenticated');
|
|
103
|
-
const places = placesIn(root);
|
|
104
|
-
if (places.length > b.places)
|
|
105
|
-
fail('legacy namespace inventory budget');
|
|
106
|
-
const legacy = [...new Set([...history, ...places.flatMap(p => appliedHistoryIn(root, p))])];
|
|
107
|
-
if (legacy.length + prefix.length > b.lines || legacy.reduce((n, l) => n + Buffer.byteLength(l), 0) + prefix.reduce((n, l) => n + Buffer.byteLength(l), 0) > b.bytes)
|
|
108
|
-
fail('legacy origin inventory budget');
|
|
109
|
-
return { history: prefix, anchor, algorithms, authorityCheckpoint: checkpoint, legacyOriginals: legacy };
|
|
110
|
-
}
|
|
111
|
-
export function rootClockSource(root, selected) {
|
|
112
|
-
const state = current(root, selected), visiting = new Set(), cache = new Map();
|
|
113
|
-
const historical = (encoded) => {
|
|
114
|
-
const cached = cache.get(encoded);
|
|
115
|
-
if (cached)
|
|
116
|
-
return cached;
|
|
117
|
-
if (visiting.has(encoded) || visiting.size >= selected.resources.acts)
|
|
118
|
-
fail('historical predecessor cycle/budget');
|
|
119
|
-
visiting.add(encoded);
|
|
120
|
-
try {
|
|
121
|
-
const context = readPlaceActContext(encoded, selected.inherited.clock), epoch = context.root.epoch;
|
|
122
|
-
const saved = admissionHistoryAt(root, Number(epoch));
|
|
123
|
-
const rows = saved.filter(l => isConfig(l) && fieldOf(l, 'epoch') === epoch);
|
|
124
|
-
if (!rows.length)
|
|
125
|
-
fail('historical checkpoint absent');
|
|
126
|
-
// First obtain the explicitly declared historical genesis, without scanning
|
|
127
|
-
// current receiver chains. Each non-genesis predecessor is then authenticated
|
|
128
|
-
// through its stored signed receipt before supplying these HOST-only heads.
|
|
129
|
-
const basic = hostClockSelection({ ...input, history: saved, authorityCheckpoint: rows[0], legacyOriginals: saved, authenticatedReceiverHeads: [], readCurrent: () => current(root, selected) });
|
|
130
|
-
const heads = context.places.map(p => {
|
|
131
|
-
const initial = basic.trust.initialHeads.find(h => h.place === p.place);
|
|
132
|
-
if (initial?.identity === p.previous)
|
|
133
|
-
return initial;
|
|
134
|
-
const prior = placeActResult(input.store, p.previous, { historical }, basic.selection.storageBudget);
|
|
135
|
-
if (!prior)
|
|
136
|
-
fail('historical predecessor absent');
|
|
137
|
-
const decoded = readPlaceActContext(prior.context, selected.inherited.clock), place = decoded.places.find(one => one.place === p.place);
|
|
138
|
-
if (!place)
|
|
139
|
-
fail('historical predecessor namespace absent');
|
|
140
|
-
return { place: p.place, identity: p.previous, epoch: place.epoch };
|
|
141
|
-
});
|
|
142
|
-
const result = hostClockSelection({ ...input, history: saved, authorityCheckpoint: rows[0], legacyOriginals: saved, authenticatedReceiverHeads: heads, readCurrent: () => current(root, selected) }).trust.contract;
|
|
143
|
-
cache.set(encoded, result);
|
|
144
|
-
return result;
|
|
145
|
-
}
|
|
146
|
-
finally {
|
|
147
|
-
visiting.delete(encoded);
|
|
148
|
-
}
|
|
149
|
-
};
|
|
150
|
-
const input = { store: storeOf(root), selector: selected.selector, profileSelector: selected.profileSelector, ...state, resources: { bytes: selected.resources.bytes, lines: selected.resources.lines, places: selected.resources.places }, inherited: selected.inherited, readCurrent: () => current(root, selected), historical };
|
|
151
|
-
const resolved = hostClockSelection(input);
|
|
152
|
-
// Host-only bytes from this exact authenticated constructor observation. This
|
|
153
|
-
// is not a retained authority cache: every constructor calls current() again.
|
|
154
|
-
const observedAnchor = Object.freeze({ ...state.anchor, coverage: Object.freeze([...state.anchor.coverage]), depth: Object.freeze({ ...state.anchor.depth }), admittedBy: Object.freeze([...state.anchor.admittedBy]) });
|
|
155
|
-
const observation = Object.freeze({ history: Object.freeze([...state.history]), anchor: observedAnchor, algorithms: Object.freeze([...state.algorithms]) });
|
|
156
|
-
return { ...resolved, observation, resources: Object.freeze({ ...selected.resources }), authority: (encoded) => { const context = readPlaceActContext(encoded, selected.inherited.clock), saved = admissionHistoryAt(root, Number(context.root.epoch)); const rows = saved.filter(l => isConfig(l) && fieldOf(l, 'epoch') === context.root.epoch); if (!rows.length)
|
|
157
|
-
fail('public historical checkpoint absent'); return { anchor: state.anchor, history: saved, checkpoint: rows[0] }; } };
|
|
158
|
-
}
|
|
159
|
-
/** Source mapping is an explicit Root declaration; absence never means a zero
|
|
160
|
-
* clock or an inferred cursor. Existing legacy callers do not request it. */
|
|
161
|
-
export function declaredRootClockSource(root) {
|
|
162
|
-
const live = authenticatedRootStanding(root), rows = live.filter(l => fieldOf(l, 'scope') === 'wire/place-act-context-source' && fieldOf(l, 'measure') === 'text' && fieldOf(l, 'value') !== 'withdraw');
|
|
163
|
-
if (rows.length !== 1)
|
|
164
|
-
fail('requires one admitted source mapping');
|
|
165
|
-
const line = rows[0], anchor = admissionAnchor(root), algorithms = eraOf(live).admitted;
|
|
166
|
-
if (fieldOf(line, 'by') !== anchor.id || fieldOf(line, 'role') !== 'writes' || fieldOf(line, 'form') !== 'alphabet' || fieldOf(line, 'value') !== 'lock' || !verifiesOwnerLine(line, anchor.publicKey, algorithms))
|
|
167
|
-
fail('mapping is not Root signed');
|
|
168
|
-
const about = fieldOf(line, 'about');
|
|
169
|
-
let value;
|
|
170
|
-
try {
|
|
171
|
-
value = JSON.parse(about);
|
|
172
|
-
}
|
|
173
|
-
catch {
|
|
174
|
-
return fail('mapping JSON invalid');
|
|
175
|
-
}
|
|
176
|
-
if (!value || Object.keys(value).sort().join(',') !== 'bootstrapCheckpoint,inherited,profile,profileSelector,resources,selector' || value.profile !== 'place-act-context@1' || JSON.stringify(value) !== about)
|
|
177
|
-
fail('unknown/noncanonical mapping');
|
|
178
|
-
return rootClockSource(root, value);
|
|
179
|
-
}
|
|
180
|
-
export function hasRootClockSource(root) { return authenticatedRootStanding(root).some(l => fieldOf(l, 'scope') === 'wire/place-act-context-source' && fieldOf(l, 'measure') === 'text' && fieldOf(l, 'value') !== 'withdraw'); }
|
|
181
|
-
/** Historical inspection ONLY. The expected acting Root anchor and resource
|
|
182
|
-
* selection come from the consumer's selected place, never from this packet.
|
|
183
|
-
* This function returns data and cannot construct a current command permit. */
|
|
184
|
-
export function inspectPortablePlaceContexts(encoded, expectedAnchor, selected) {
|
|
185
|
-
sourceSelection(selected);
|
|
186
|
-
if (typeof encoded !== 'string' || Buffer.byteLength(encoded) > selected.resources.bytes)
|
|
187
|
-
fail('portable byte budget');
|
|
188
|
-
let rows;
|
|
189
|
-
try {
|
|
190
|
-
rows = JSON.parse(encoded);
|
|
191
|
-
}
|
|
192
|
-
catch {
|
|
193
|
-
return fail('portable JSON');
|
|
194
|
-
}
|
|
195
|
-
if (!Array.isArray(rows) || rows.length > selected.resources.acts)
|
|
196
|
-
fail('portable context inventory budget');
|
|
197
|
-
const byId = new Map();
|
|
198
|
-
for (const row of rows) {
|
|
199
|
-
if (!row || typeof row.identity !== 'string' || typeof row.context !== 'string' || !row.authority || JSON.stringify(row.authority.anchor) !== JSON.stringify(expectedAnchor) || !Array.isArray(row.authority.history) || !Array.isArray(row.receipts) || byId.has(row.identity))
|
|
200
|
-
fail('portable anchor/shape/identity');
|
|
201
|
-
byId.set(row.identity, row);
|
|
202
|
-
}
|
|
203
|
-
const done = new Map(), visiting = new Set();
|
|
204
|
-
const inspect = (id) => {
|
|
205
|
-
const saved = done.get(id);
|
|
206
|
-
if (saved)
|
|
207
|
-
return saved;
|
|
208
|
-
const row = byId.get(id);
|
|
209
|
-
if (!row || visiting.has(id))
|
|
210
|
-
fail('portable predecessor missing/cyclic');
|
|
211
|
-
visiting.add(id);
|
|
212
|
-
try {
|
|
213
|
-
const context = readPlaceActContext(row.context, selected.inherited.clock), input = { store: '', selector: selected.selector, profileSelector: selected.profileSelector, history: row.authority.history, anchor: expectedAnchor, algorithms: eraOf(foldClaims(row.authority.history.filter(l => fieldOf(l, 'scope').startsWith('wire/'))).standing).admitted, legacyOriginals: row.authority.history, authorityCheckpoint: row.authority.checkpoint, resources: { bytes: selected.resources.bytes, lines: selected.resources.lines, places: selected.resources.places }, inherited: selected.inherited, authenticatedReceiverHeads: [], readCurrent: () => fail('portable inspection has no current Root authority'), historical: () => fail('portable inspection uses declared predecessor graph') };
|
|
214
|
-
const basic = hostClockSelection(input), heads = context.places.map(p => { const initial = basic.trust.initialHeads.find(h => h.place === p.place); if (initial?.identity === p.previous)
|
|
215
|
-
return initial; const parent = inspect(p.previous), place = parent.context.places.find(one => one.place === p.place); if (!place)
|
|
216
|
-
fail('portable predecessor namespace'); return { place: p.place, identity: p.previous, epoch: place.epoch }; });
|
|
217
|
-
const contract = hostClockSelection({ ...input, authenticatedReceiverHeads: heads }).trust.contract, result = placeActReceiptsOf(row.context, row.receipts, contract);
|
|
218
|
-
if (result.identity !== id)
|
|
219
|
-
fail('portable identity differs');
|
|
220
|
-
const value = { contract, context };
|
|
221
|
-
done.set(id, value);
|
|
222
|
-
return value;
|
|
223
|
-
}
|
|
224
|
-
finally {
|
|
225
|
-
visiting.delete(id);
|
|
226
|
-
}
|
|
227
|
-
};
|
|
228
|
-
for (const id of byId.keys())
|
|
229
|
-
inspect(id);
|
|
230
|
-
return [...done].map(([identity, value]) => ({ identity, context: value.context }));
|
|
231
|
-
}
|
|
232
|
-
/** Prospective configuration is checked as authenticated Root history, never
|
|
233
|
-
* against a prose birth condition. Initial heads are explicit typed selections;
|
|
234
|
-
* stored committed chains cannot disappear when a selection is activated or
|
|
235
|
-
* replaced. Preview reads only; the real admission repeats under its Root lock. */
|
|
236
|
-
export function assertProspectiveRootClockSelection(root, signed) {
|
|
237
|
-
const sourceScope = 'wire/place-act-context-source';
|
|
238
|
-
const before = admissionHistoryAt(root, Number.MAX_SAFE_INTEGER);
|
|
239
|
-
const selectors = [sourceScope, 'wire/place-act-context-selection', 'wire/place-act-context'];
|
|
240
|
-
// Ordinary lots need no clock projection, including bootstrap before an
|
|
241
|
-
// admission anchor exists. Raw stored delivery is not authenticated claims.
|
|
242
|
-
if (!before.some(l => fieldOf(l, 'scope') === sourceScope) && !signed.some(l => selectors.includes(fieldOf(l, 'scope'))))
|
|
243
|
-
return;
|
|
244
|
-
const oldLive = authenticatedRootStanding(root).filter(l => fieldOf(l, 'value') !== 'withdraw');
|
|
245
|
-
const oldMapping = oldLive.find(l => fieldOf(l, 'scope') === sourceScope && fieldOf(l, 'measure') === 'text');
|
|
246
|
-
if (oldMapping) {
|
|
247
|
-
const anchor = admissionAnchor(root), algorithms = eraOf(oldLive.filter(l => fieldOf(l, 'scope').startsWith('wire/'))).admitted;
|
|
248
|
-
if (fieldOf(oldMapping, 'by') !== anchor.id || !verifiesOwnerLine(oldMapping, anchor.publicKey, algorithms))
|
|
249
|
-
fail('existing source mapping is not Root signed');
|
|
250
|
-
let old;
|
|
251
|
-
try {
|
|
252
|
-
old = JSON.parse(fieldOf(oldMapping, 'about'));
|
|
253
|
-
}
|
|
254
|
-
catch {
|
|
255
|
-
return fail('existing source mapping JSON invalid');
|
|
256
|
-
}
|
|
257
|
-
selectors.push(old.selector, old.profileSelector);
|
|
258
|
-
}
|
|
259
|
-
if (!signed.some(l => selectors.includes(fieldOf(l, 'scope'))))
|
|
260
|
-
return;
|
|
261
|
-
if (signed.every(line => before.includes(line)))
|
|
262
|
-
return;
|
|
263
|
-
const store = storeOf(root);
|
|
264
|
-
if (!readOnly())
|
|
265
|
-
rootAuthorityProtection(store);
|
|
266
|
-
const history = [...new Set([...before, ...signed])], live = foldClaims([...oldLive, ...signed].filter(isConfig)).standing.filter(l => fieldOf(l, 'value') !== 'withdraw'), rows = live.filter(l => fieldOf(l, 'scope') === sourceScope && fieldOf(l, 'measure') === 'text');
|
|
267
|
-
if (!rows.length) {
|
|
268
|
-
if (signed.some(l => fieldOf(l, 'scope') === sourceScope && fieldOf(l, 'value') === 'withdraw'))
|
|
269
|
-
return;
|
|
270
|
-
return fail('activation requires source and selection together');
|
|
271
|
-
}
|
|
272
|
-
if (rows.length !== 1)
|
|
273
|
-
fail('prospective source mapping ambiguous');
|
|
274
|
-
const line = rows[0], anchor = admissionAnchor(root), algorithms = eraOf(live.filter(l => fieldOf(l, 'scope').startsWith('wire/'))).admitted;
|
|
275
|
-
if (fieldOf(line, 'by') !== anchor.id || fieldOf(line, 'role') !== 'writes' || fieldOf(line, 'form') !== 'alphabet' || fieldOf(line, 'value') !== 'lock' || !verifiesOwnerLine(line, anchor.publicKey, algorithms))
|
|
276
|
-
fail('prospective mapping is not Root signed');
|
|
277
|
-
const about = fieldOf(line, 'about');
|
|
278
|
-
let selected;
|
|
279
|
-
try {
|
|
280
|
-
selected = JSON.parse(about);
|
|
281
|
-
}
|
|
282
|
-
catch {
|
|
283
|
-
return fail('prospective source mapping JSON invalid');
|
|
284
|
-
}
|
|
285
|
-
if (!selected || Object.keys(selected).sort().join(',') !== 'bootstrapCheckpoint,inherited,profile,profileSelector,resources,selector' || selected.profile !== 'place-act-context@1' || JSON.stringify(selected) !== about)
|
|
286
|
-
fail('unsupported prospective source mapping');
|
|
287
|
-
sourceSelection(selected);
|
|
288
|
-
const positive = (scope, measure) => signed.filter(l => fieldOf(l, 'scope') === scope && fieldOf(l, 'measure') === measure && fieldOf(l, 'value') !== 'withdraw');
|
|
289
|
-
if (!oldMapping && (positive(sourceScope, 'text').length !== 1 || positive(selected.selector, 'text').length !== 1))
|
|
290
|
-
fail('activation requires source and selection together');
|
|
291
|
-
const checkpoint = signed.filter(isConfig).at(-1) ?? fail('prospective authority checkpoint absent');
|
|
292
|
-
if (!oldMapping && positive(selected.bootstrapCheckpoint.scope, selected.bootstrapCheckpoint.measure).length !== 1)
|
|
293
|
-
fail('initial authority checkpoint must land with selection');
|
|
294
|
-
const old = oldMapping ? declaredRootClockSource(root) : undefined;
|
|
295
|
-
const actualLegacy = [...new Set([...before, ...placesIn(root).flatMap(place => appliedHistoryIn(root, place))])];
|
|
296
|
-
const observed = { history, anchor, algorithms, authorityCheckpoint: checkpoint, legacyOriginals: actualLegacy };
|
|
297
|
-
const resolved = hostClockSelection({ store, selector: selected.selector, profileSelector: selected.profileSelector, ...observed, resources: { bytes: selected.resources.bytes, lines: selected.resources.lines, places: selected.resources.places }, inherited: selected.inherited, readCurrent: () => observed, historical: encoded => { if (!old)
|
|
298
|
-
return fail('committed receiver history precedes initial selection'); return old.trust.historical(encoded); } });
|
|
299
|
-
const heads = placeActHeads(store, resolved.trust.initialHeads, resolved.trust, resolved.selection.storageBudget), expected = old ? old.trust.current().heads : resolved.trust.initialHeads;
|
|
300
|
-
for (const head of expected) {
|
|
301
|
-
const actual = heads.find(one => one.place === head.place);
|
|
302
|
-
if (!actual || actual.identity !== head.identity || actual.epoch !== head.epoch)
|
|
303
|
-
fail('prospective selection resets committed receiver ' + head.place);
|
|
304
|
-
}
|
|
305
|
-
}
|
|
1
|
+
var D=Object.defineProperty;var y=(e,t)=>D(e,"name",{value:t,configurable:!0});import{existsSync as U,opendirSync as Q,statSync as V}from"node:fs";import{join as _,basename as Y}from"node:path";import{committedLocalActs as Z,actLines as K}from"./ports/local-act.js";import{verifiedLegacyActResult as $}from"../cli/act-result.js";import{admissionAnchor as I,admissionHistoryAt as L}from"../fold/admission-history.js";import{authorityFor as ee}from"../fold/signers.js";import{signatureEraOf as H}from"./signature-era.js";import{fieldOf as i,foldClaims as F,isConfig as A,selfName as te}from"../fold/claims.js";import{authenticatedRootStanding as W}from"./admitted-place.js";import{verifiesOwnerLine as q}from"../land/sign.js";import{placesIn as G}from"../fold/places.js";import{appliedHistoryIn as T}from"../fold/signed.js";import{storeOf as M}from"../land/ledger.js";import{hostClockSelection as J}from"./place-clock-selection.js";import{library3_readPlaceActContext as B,library3_placeActReceiptsOf as re}from"../vendor/runtime.js";import{placeActResult as oe,placeActHeads as ie}from"./ports/place-act.js";import{rootAuthorityProtection as se}from"../land/act.js";import{readOnly as ce}from"./read-only.js";const o=y(e=>{throw Error("REFUSE\xB7Root cursor "+e)},"fail");function z(e){const t=e?.resources;return(!t||Object.keys(t).sort().join(",")!=="acts,bytes,lines,places"||Object.values(t).some(r=>!Number.isSafeInteger(r)||r<1)||typeof e.selector!="string"||!e.selector||typeof e.profileSelector!="string"||!e.profileSelector||!e.bootstrapCheckpoint||Object.keys(e.bootstrapCheckpoint).sort().join(",")!=="measure,scope"||typeof e.bootstrapCheckpoint.scope!="string"||!e.bootstrapCheckpoint.scope||typeof e.bootstrapCheckpoint.measure!="string"||!e.bootstrapCheckpoint.measure)&&o("missing admitted source selection"),(!e.inherited?.clock||Object.keys(e.inherited.clock).sort().join(",")!=="bytes,placements,policies,proofs,records,signers"||!e.inherited.storage||Object.keys(e.inherited.storage).sort().join(",")!=="acts,bytes"||[...Object.values(e.inherited.clock),...Object.values(e.inherited.storage)].some(r=>!Number.isSafeInteger(r)||r<1))&&o("missing inherited source budgets"),t}y(z,"sourceSelection");function ne(e,t){const r=z(t),p=M(e);let k=0,g=0,a=0;const b=r.acts*(r.places+2)+r.lines;Number.isSafeInteger(b)||o("source file bound");const n=y(d=>{if(!U(d))return;const l=V(d);if(l.isDirectory()){const f=Q(d);try{for(let m=f.readSync();m;m=f.readSync())++a>b&&o("source entry budget"),!(Y(d)==="ledger"&&m.name==="place-acts")&&n(_(d,m.name))}finally{f.closeSync()}}else(!l.isFile()||++g>b||(k+=l.size)>r.bytes)&&o("source file/byte budget")},"visit");n(_(p,"ledger")),n(_(p,te()+".keys"))}y(ne,"boundedInventory");function P(e,t){const r=z(t);ne(e,t);const p=L(e,Number.MAX_SAFE_INTEGER);p.length>r.lines&&o("history line budget");let k=0;for(const c of p)(k+=Buffer.byteLength(c))>r.bytes&&o("history byte budget");const g=Z(M(e));g.length>r.acts&&o("act inventory budget");const a=g.flatMap(c=>{const h=c.bundle.placements?.filter(v=>v.place===".")??[];return h.some(v=>K(v.records).some(A))?[{...$(e,c.identity),identity:c.identity,roots:h}]:[]});a.sort((c,h)=>c.epoch-h.epoch),a.some((c,h)=>h>0&&c.epoch===a[h-1].epoch&&c.identity!==a[h-1].identity)&&o("ambiguous committed Root cursor");const b=a.at(-1),n=I(e);let d;if(b){const c=b.roots.flatMap(h=>K(h.records)).filter(A);c.length||o("verified Root act has no configuration checkpoint"),d=c[0]}else{const c=p.filter(h=>i(h,"scope")===t.bootstrapCheckpoint.scope&&i(h,"measure")===t.bootstrapCheckpoint.measure);c.length!==1&&o("bootstrap checkpoint missing/ambiguous"),d=c[0]}const l=Number(i(d,"epoch"));(!Number.isSafeInteger(l)||l<0)&&o("unknown committed Root epoch");const f=p.filter(c=>!A(c)||Number(i(c,"epoch"))<=l),m=H(F(f.filter(c=>i(c,"scope").startsWith("wire/"))).standing).admitted;ee(f,n,m).of(d).kind!=="admitted"&&o("checkpoint is not authenticated");const w=G(e);w.length>r.places&&o("legacy namespace inventory budget");const u=[...new Set([...p,...w.flatMap(c=>T(e,c))])];return(u.length+f.length>r.lines||u.reduce((c,h)=>c+Buffer.byteLength(h),0)+f.reduce((c,h)=>c+Buffer.byteLength(h),0)>r.bytes)&&o("legacy origin inventory budget"),{history:f,anchor:n,algorithms:m,authorityCheckpoint:d,legacyOriginals:u}}y(P,"current");function ae(e,t){const r=P(e,t),p=new Set,k=new Map,g=y(l=>{const f=k.get(l);if(f)return f;(p.has(l)||p.size>=t.resources.acts)&&o("historical predecessor cycle/budget"),p.add(l);try{const m=B(l,t.inherited.clock),O=m.root.epoch,w=L(e,Number(O)),u=w.filter(v=>A(v)&&i(v,"epoch")===O);u.length||o("historical checkpoint absent");const c=J({...a,history:w,authorityCheckpoint:u[0],legacyOriginals:w,authenticatedReceiverHeads:[],readCurrent:y(()=>P(e,t),"readCurrent")}),h=m.places.map(v=>{const N=c.trust.initialHeads.find(s=>s.place===v.place);if(N?.identity===v.previous)return N;const x=oe(a.store,v.previous,{historical:g},c.selection.storageBudget);x||o("historical predecessor absent");const j=B(x.context,t.inherited.clock),E=j.places.find(s=>s.place===v.place);return E||o("historical predecessor namespace absent"),{place:v.place,identity:v.previous,epoch:E.epoch}}),S=J({...a,history:w,authorityCheckpoint:u[0],legacyOriginals:w,authenticatedReceiverHeads:h,readCurrent:y(()=>P(e,t),"readCurrent")}).trust.contract;return k.set(l,S),S}finally{p.delete(l)}},"historical"),a={store:M(e),selector:t.selector,profileSelector:t.profileSelector,...r,resources:{bytes:t.resources.bytes,lines:t.resources.lines,places:t.resources.places},inherited:t.inherited,readCurrent:y(()=>P(e,t),"readCurrent"),historical:g},b=J(a),n=Object.freeze({...r.anchor,coverage:Object.freeze([...r.anchor.coverage]),depth:Object.freeze({...r.anchor.depth}),admittedBy:Object.freeze([...r.anchor.admittedBy])}),d=Object.freeze({history:Object.freeze([...r.history]),anchor:n,algorithms:Object.freeze([...r.algorithms])});return{...b,observation:d,resources:Object.freeze({...t.resources}),authority:y(l=>{const f=B(l,t.inherited.clock),m=L(e,Number(f.root.epoch)),O=m.filter(w=>A(w)&&i(w,"epoch")===f.root.epoch);return O.length||o("public historical checkpoint absent"),{anchor:r.anchor,history:m,checkpoint:O[0]}},"authority")}}y(ae,"rootClockSource");function pe(e,t){const r=e.filter(b=>i(b,"scope")==="wire/place-act-context-source"&&i(b,"measure")==="text"&&i(b,"value")!=="withdraw");r.length!==1&&o("requires one admitted source mapping");const p=r[0],k=H(e).admitted;(i(p,"by")!==t.id||i(p,"role")!=="writes"||i(p,"form")!=="alphabet"||i(p,"value")!=="lock"||!q(p,t.publicKey,k))&&o("mapping is not Root signed");const g=i(p,"about");let a;try{a=JSON.parse(g)}catch{return o("mapping JSON invalid")}return(!a||Object.keys(a).sort().join(",")!=="bootstrapCheckpoint,inherited,profile,profileSelector,resources,selector"||a.profile!=="place-act-context@1"||JSON.stringify(a)!==g)&&o("unknown/noncanonical mapping"),z(a),a}y(pe,"rootClockSelectionOf");function le(e){return ae(e,pe(W(e),I(e)))}y(le,"declaredRootClockSource");function Je(e){return W(e).some(t=>i(t,"scope")==="wire/place-act-context-source"&&i(t,"measure")==="text"&&i(t,"value")!=="withdraw")}y(Je,"hasRootClockSource");function He(e,t,r){z(r),(typeof e!="string"||Buffer.byteLength(e)>r.resources.bytes)&&o("portable byte budget");let p;try{p=JSON.parse(e)}catch{return o("portable JSON")}(!Array.isArray(p)||p.length>r.resources.acts)&&o("portable context inventory budget");const k=new Map;for(const n of p)(!n||typeof n.identity!="string"||typeof n.context!="string"||!n.authority||JSON.stringify(n.authority.anchor)!==JSON.stringify(t)||!Array.isArray(n.authority.history)||!Array.isArray(n.receipts)||k.has(n.identity))&&o("portable anchor/shape/identity"),k.set(n.identity,n);const g=new Map,a=new Set,b=y(n=>{const d=g.get(n);if(d)return d;const l=k.get(n);(!l||a.has(n))&&o("portable predecessor missing/cyclic"),a.add(n);try{const f=B(l.context,r.inherited.clock),m={store:"",selector:r.selector,profileSelector:r.profileSelector,history:l.authority.history,anchor:t,algorithms:H(F(l.authority.history.filter(S=>i(S,"scope").startsWith("wire/"))).standing).admitted,legacyOriginals:l.authority.history,authorityCheckpoint:l.authority.checkpoint,resources:{bytes:r.resources.bytes,lines:r.resources.lines,places:r.resources.places},inherited:r.inherited,authenticatedReceiverHeads:[],readCurrent:y(()=>o("portable inspection has no current Root authority"),"readCurrent"),historical:y(()=>o("portable inspection uses declared predecessor graph"),"historical")},O=J(m),w=f.places.map(S=>{const v=O.trust.initialHeads.find(j=>j.place===S.place);if(v?.identity===S.previous)return v;const N=b(S.previous),x=N.context.places.find(j=>j.place===S.place);return x||o("portable predecessor namespace"),{place:S.place,identity:S.previous,epoch:x.epoch}}),u=J({...m,authenticatedReceiverHeads:w}).trust.contract;re(l.context,l.receipts,u).identity!==n&&o("portable identity differs");const h={contract:u,context:f};return g.set(n,h),h}finally{a.delete(n)}},"inspect");for(const n of k.keys())b(n);return[...g].map(([n,d])=>({identity:n,context:d.context}))}y(He,"inspectPortablePlaceContexts");function ze(e,t){const r="wire/place-act-context-source",p=L(e,Number.MAX_SAFE_INTEGER),k=[r,"wire/place-act-context-selection","wire/place-act-context"];if(!p.some(s=>i(s,"scope")===r)&&!t.some(s=>k.includes(i(s,"scope"))))return;const g=W(e).filter(s=>i(s,"value")!=="withdraw"),a=g.find(s=>i(s,"scope")===r&&i(s,"measure")==="text");if(a){const s=I(e),R=H(g.filter(X=>i(X,"scope").startsWith("wire/"))).admitted;(i(a,"by")!==s.id||!q(a,s.publicKey,R))&&o("existing source mapping is not Root signed");let C;try{C=JSON.parse(i(a,"about"))}catch{return o("existing source mapping JSON invalid")}k.push(C.selector,C.profileSelector)}if(!t.some(s=>k.includes(i(s,"scope")))||t.every(s=>p.includes(s)))return;const b=M(e);ce()||se(b);const n=[...new Set([...p,...t])],d=F([...g,...t].filter(A)).standing.filter(s=>i(s,"value")!=="withdraw"),l=d.filter(s=>i(s,"scope")===r&&i(s,"measure")==="text");if(!l.length)return t.some(s=>i(s,"scope")===r&&i(s,"value")==="withdraw")?void 0:o("activation requires source and selection together");l.length!==1&&o("prospective source mapping ambiguous");const f=l[0],m=I(e),O=H(d.filter(s=>i(s,"scope").startsWith("wire/"))).admitted;(i(f,"by")!==m.id||i(f,"role")!=="writes"||i(f,"form")!=="alphabet"||i(f,"value")!=="lock"||!q(f,m.publicKey,O))&&o("prospective mapping is not Root signed");const w=i(f,"about");let u;try{u=JSON.parse(w)}catch{return o("prospective source mapping JSON invalid")}(!u||Object.keys(u).sort().join(",")!=="bootstrapCheckpoint,inherited,profile,profileSelector,resources,selector"||u.profile!=="place-act-context@1"||JSON.stringify(u)!==w)&&o("unsupported prospective source mapping"),z(u);const c=y((s,R)=>t.filter(C=>i(C,"scope")===s&&i(C,"measure")===R&&i(C,"value")!=="withdraw"),"positive");!a&&(c(r,"text").length!==1||c(u.selector,"text").length!==1)&&o("activation requires source and selection together");const h=t.filter(A).at(-1)??o("prospective authority checkpoint absent");!a&&c(u.bootstrapCheckpoint.scope,u.bootstrapCheckpoint.measure).length!==1&&o("initial authority checkpoint must land with selection");const S=a?le(e):void 0,v=[...new Set([...p,...G(e).flatMap(s=>T(e,s))])],N={history:n,anchor:m,algorithms:O,authorityCheckpoint:h,legacyOriginals:v},x=J({store:b,selector:u.selector,profileSelector:u.profileSelector,...N,resources:{bytes:u.resources.bytes,lines:u.resources.lines,places:u.resources.places},inherited:u.inherited,readCurrent:y(()=>N,"readCurrent"),historical:y(s=>S?S.trust.historical(s):o("committed receiver history precedes initial selection"),"historical")}),j=ie(b,x.trust.initialHeads,x.trust,x.selection.storageBudget),E=S?S.trust.current().heads:x.trust.initialHeads;for(const s of E){const R=j.find(C=>C.place===s.place);(!R||R.identity!==s.identity||R.epoch!==s.epoch)&&o("prospective selection resets committed receiver "+s.place)}}y(ze,"assertProspectiveRootClockSelection");export{ze as assertProspectiveRootClockSelection,le as declaredRootClockSource,Je as hasRootClockSource,He as inspectPortablePlaceContexts,pe as rootClockSelectionOf,ae as rootClockSource};
|