@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.
Files changed (186) hide show
  1. package/CONTRIBUTING.md +44 -0
  2. package/LICENSE +22 -0
  3. package/README.md +97 -0
  4. package/TARGET.bound +361 -0
  5. package/dist/cli/accept.d.ts +23 -0
  6. package/dist/cli/accept.js +423 -0
  7. package/dist/cli/args.d.ts +10 -0
  8. package/dist/cli/args.js +34 -0
  9. package/dist/cli/host.d.ts +1 -0
  10. package/dist/cli/host.js +32 -0
  11. package/dist/cli/names.d.ts +25 -0
  12. package/dist/cli/names.js +12 -0
  13. package/dist/cli/observe.d.ts +1 -0
  14. package/dist/cli/observe.js +6 -0
  15. package/dist/cli/owner.d.ts +35 -0
  16. package/dist/cli/owner.js +119 -0
  17. package/dist/cli/pass.d.ts +6 -0
  18. package/dist/cli/pass.js +309 -0
  19. package/dist/cli/place.d.ts +74 -0
  20. package/dist/cli/place.js +290 -0
  21. package/dist/cli/takes.d.ts +30 -0
  22. package/dist/cli/takes.js +108 -0
  23. package/dist/cli/verb.d.ts +2 -0
  24. package/dist/cli/verb.js +478 -0
  25. package/dist/fold/ceilings.d.ts +24 -0
  26. package/dist/fold/ceilings.js +129 -0
  27. package/dist/fold/claims.d.ts +25 -0
  28. package/dist/fold/claims.js +107 -0
  29. package/dist/fold/closed.d.ts +15 -0
  30. package/dist/fold/closed.js +168 -0
  31. package/dist/fold/configures.d.ts +4 -0
  32. package/dist/fold/configures.js +22 -0
  33. package/dist/fold/digests.d.ts +29 -0
  34. package/dist/fold/digests.js +211 -0
  35. package/dist/fold/evidence.d.ts +32 -0
  36. package/dist/fold/evidence.js +22 -0
  37. package/dist/fold/fold-worker.d.ts +1 -0
  38. package/dist/fold/fold-worker.js +13 -0
  39. package/dist/fold/kept.d.ts +29 -0
  40. package/dist/fold/kept.js +142 -0
  41. package/dist/fold/keys.d.ts +17 -0
  42. package/dist/fold/keys.js +84 -0
  43. package/dist/fold/laid.d.ts +17 -0
  44. package/dist/fold/laid.js +90 -0
  45. package/dist/fold/nos.d.ts +8 -0
  46. package/dist/fold/nos.js +48 -0
  47. package/dist/fold/object.d.ts +17 -0
  48. package/dist/fold/object.js +38 -0
  49. package/dist/fold/observed.d.ts +33 -0
  50. package/dist/fold/observed.js +444 -0
  51. package/dist/fold/opened.d.ts +5 -0
  52. package/dist/fold/opened.js +33 -0
  53. package/dist/fold/own.d.ts +24 -0
  54. package/dist/fold/own.js +215 -0
  55. package/dist/fold/paid.d.ts +11 -0
  56. package/dist/fold/paid.js +92 -0
  57. package/dist/fold/place-inputs.d.ts +17 -0
  58. package/dist/fold/place-inputs.js +70 -0
  59. package/dist/fold/places.d.ts +20 -0
  60. package/dist/fold/places.js +84 -0
  61. package/dist/fold/rank.d.ts +3 -0
  62. package/dist/fold/rank.js +9 -0
  63. package/dist/fold/reach.d.ts +13 -0
  64. package/dist/fold/reach.js +65 -0
  65. package/dist/fold/readers.d.ts +44 -0
  66. package/dist/fold/readers.js +132 -0
  67. package/dist/fold/receipts.d.ts +8 -0
  68. package/dist/fold/receipts.js +47 -0
  69. package/dist/fold/region.d.ts +72 -0
  70. package/dist/fold/region.js +286 -0
  71. package/dist/fold/release-policy.d.ts +67 -0
  72. package/dist/fold/release-policy.js +103 -0
  73. package/dist/fold/resolved.d.ts +18 -0
  74. package/dist/fold/resolved.js +123 -0
  75. package/dist/fold/roles.d.ts +17 -0
  76. package/dist/fold/roles.js +59 -0
  77. package/dist/fold/second.d.ts +18 -0
  78. package/dist/fold/second.js +68 -0
  79. package/dist/fold/shards.d.ts +29 -0
  80. package/dist/fold/shards.js +100 -0
  81. package/dist/fold/signed.d.ts +18 -0
  82. package/dist/fold/signed.js +75 -0
  83. package/dist/fold/signers.d.ts +24 -0
  84. package/dist/fold/signers.js +58 -0
  85. package/dist/fold/stranger.d.ts +3 -0
  86. package/dist/fold/stranger.js +24 -0
  87. package/dist/fold/views.d.ts +27 -0
  88. package/dist/fold/views.js +66 -0
  89. package/dist/fold/wire.d.ts +19 -0
  90. package/dist/fold/wire.js +41 -0
  91. package/dist/fold/zoom.d.ts +16 -0
  92. package/dist/fold/zoom.js +80 -0
  93. package/dist/host/adapters/keys/ed25519.d.ts +7 -0
  94. package/dist/host/adapters/keys/ed25519.js +30 -0
  95. package/dist/host/archive.d.ts +11 -0
  96. package/dist/host/archive.js +152 -0
  97. package/dist/host/capsule-process.d.ts +20 -0
  98. package/dist/host/capsule-process.js +54 -0
  99. package/dist/host/capsule.d.ts +13 -0
  100. package/dist/host/capsule.js +149 -0
  101. package/dist/host/cell-receipts.d.ts +2 -0
  102. package/dist/host/cell-receipts.js +124 -0
  103. package/dist/host/digest.d.ts +3 -0
  104. package/dist/host/digest.js +15 -0
  105. package/dist/host/hash.d.ts +2 -0
  106. package/dist/host/hash.js +3 -0
  107. package/dist/host/io.d.ts +9 -0
  108. package/dist/host/io.js +9 -0
  109. package/dist/host/located.d.ts +4 -0
  110. package/dist/host/located.js +105 -0
  111. package/dist/host/objects.d.ts +5 -0
  112. package/dist/host/objects.js +18 -0
  113. package/dist/host/ports/store.d.ts +2 -0
  114. package/dist/host/ports/store.js +18 -0
  115. package/dist/host/provide.d.ts +23 -0
  116. package/dist/host/provide.js +81 -0
  117. package/dist/host/release.d.ts +12 -0
  118. package/dist/host/release.js +213 -0
  119. package/dist/host/selected-capsules.d.ts +8 -0
  120. package/dist/host/selected-capsules.js +19 -0
  121. package/dist/host/selected-topos.d.ts +17 -0
  122. package/dist/host/selected-topos.js +61 -0
  123. package/dist/host/source-content.d.ts +10 -0
  124. package/dist/host/source-content.js +94 -0
  125. package/dist/host/timing.d.ts +2 -0
  126. package/dist/host/timing.js +7 -0
  127. package/dist/judge/late.d.ts +7 -0
  128. package/dist/judge/late.js +33 -0
  129. package/dist/judge/take.d.ts +57 -0
  130. package/dist/judge/take.js +75 -0
  131. package/dist/judge/widening.d.ts +1 -0
  132. package/dist/judge/widening.js +25 -0
  133. package/dist/land/act.d.ts +4 -0
  134. package/dist/land/act.js +209 -0
  135. package/dist/land/alias.d.ts +10 -0
  136. package/dist/land/alias.js +41 -0
  137. package/dist/land/held.d.ts +7 -0
  138. package/dist/land/held.js +23 -0
  139. package/dist/land/inert.d.ts +26 -0
  140. package/dist/land/inert.js +33 -0
  141. package/dist/land/leaving.d.ts +14 -0
  142. package/dist/land/leaving.js +51 -0
  143. package/dist/land/ledger.d.ts +28 -0
  144. package/dist/land/ledger.js +148 -0
  145. package/dist/land/owned.d.ts +23 -0
  146. package/dist/land/owned.js +65 -0
  147. package/dist/land/sign.d.ts +16 -0
  148. package/dist/land/sign.js +30 -0
  149. package/dist/land/vouched.d.ts +21 -0
  150. package/dist/land/vouched.js +112 -0
  151. package/dist/land/withdraw.d.ts +14 -0
  152. package/dist/land/withdraw.js +24 -0
  153. package/dist/observe/files.d.ts +14 -0
  154. package/dist/observe/files.js +68 -0
  155. package/dist/observe/identity.d.ts +19 -0
  156. package/dist/observe/identity.js +61 -0
  157. package/dist/observe/match.d.ts +1 -0
  158. package/dist/observe/match.js +8 -0
  159. package/dist/observe/regions.d.ts +29 -0
  160. package/dist/observe/regions.js +200 -0
  161. package/dist/observe/run.d.ts +30 -0
  162. package/dist/observe/run.js +34 -0
  163. package/dist/observe/runner.d.ts +16 -0
  164. package/dist/observe/runner.js +83 -0
  165. package/dist/render/capsule.d.ts +24 -0
  166. package/dist/render/capsule.js +30 -0
  167. package/dist/render/field.d.ts +9 -0
  168. package/dist/render/field.js +46 -0
  169. package/dist/render/names.d.ts +3 -0
  170. package/dist/render/names.js +4 -0
  171. package/dist/render/open.d.ts +27 -0
  172. package/dist/render/open.js +61 -0
  173. package/dist/render/page.d.ts +3 -0
  174. package/dist/render/page.js +7 -0
  175. package/dist/render/receipts.d.ts +3 -0
  176. package/dist/render/receipts.js +46 -0
  177. package/dist/render/shape.d.ts +16 -0
  178. package/dist/render/shape.js +74 -0
  179. package/dist/render/target.d.ts +18 -0
  180. package/dist/render/target.js +52 -0
  181. package/dist/render/view.d.ts +22 -0
  182. package/dist/render/view.js +191 -0
  183. package/docs/readme-assets/atlas.svg +169 -0
  184. package/docs/usage.md +53 -0
  185. package/package.json +48 -0
  186. package/receipts.bound +59 -0
@@ -0,0 +1,444 @@
1
+ import { basename, dirname, existsSync, mkdirSync, resolve, rmSync, spawn, statSync, writeFileSync } from "../host/io.js";
2
+ import { bundleOf, processOf } from "../observe/runner.js";
3
+ import { canonical, parse, PROTOCOL } from '@lapxo/topos/wire';
4
+ import { bytesDigest, memoized } from "./digests.js";
5
+ import { land, landMemo, landShard, memoAt, shardLines, storeAt } from "../land/ledger.js";
6
+ import { fieldOf, isWire } from "./claims.js";
7
+ import { rolesOf } from "./roles.js";
8
+ import { keyFor, lockStanding } from "./keys.js";
9
+ import { spoken } from "./places.js";
10
+ import { capsuleAt, childFlags } from "../host/capsule.js";
11
+ import { wordOf } from "./wire.js";
12
+ import { observeFile, observeText } from "../observe/files.js";
13
+ import { identityOf, vectorsOf } from "../observe/identity.js";
14
+ import { capsuleReadersOf, handedAt, lookOf, offersOf } from "./readers.js";
15
+ import { runReader, runs } from "../observe/run.js";
16
+ import { LEVEL, OBSERVED, keepObservation, keyOf, legacyOf, observation, observed as observedAt, opened, pointer, readerOf, regionIn, setAside, sha, trimmed } from "./shards.js";
17
+ import { saidDigest } from "./kept.js";
18
+ import { carriedIn } from "./resolved.js";
19
+ export const cased = (question) => /^(?:vector|sample)\//.test(question);
20
+ export function isReaderLock(line) {
21
+ const got = parse(line);
22
+ return got.kind === 'fact' && got.value.fields['measure'] === 'reader';
23
+ }
24
+ export function readerModules(store) {
25
+ return readersOf(store).map((reader) => reader.module);
26
+ }
27
+ /** The readers of a tree: those a place's own lock names for what it holds, and those the lock names that no place's lock rules. */
28
+ function readersOf(store) {
29
+ const standing = lockStanding(store);
30
+ const family = `${wordOf(standing, 'families', 'reader')}/`;
31
+ const own = spoken(dirname(store)).map((one) => one.line).filter((line) => fieldOf(line, 'scope').startsWith(family));
32
+ const ruled = new Set(own.map((line) => fieldOf(line, 'scope')));
33
+ return [...own, ...standing.filter((line) => !ruled.has(fieldOf(line, 'scope')))]
34
+ .map((line) => parse(line))
35
+ .flatMap((got) => (got.kind === 'fact' ? [got.value.fields] : []))
36
+ .filter((f) => (f['scope'] ?? '').startsWith(family) && f['measure'] === 'reader' && f['value'] !== 'withdraw')
37
+ .flatMap((f) => {
38
+ const kind = f['kind'] || ((f['shape'] ?? '').split('|').includes(wordOf(standing, 'shapes', 'run')) ? 'process' : 'js');
39
+ return runs(kind) ? [{
40
+ id: (f['scope'] ?? '').slice(family.length),
41
+ shape: (f['shape'] ?? '').split('|').filter(Boolean),
42
+ where: (f['needs'] ?? '').split('|').filter(Boolean),
43
+ module: f['value'] ?? '',
44
+ kind,
45
+ }] : [];
46
+ })
47
+ .filter((reader) => reader.id !== '' && reader.shape.length > 0 && reader.where.length > 0 && reader.module !== '');
48
+ }
49
+ function lineOf(seen, digest, by, reader) {
50
+ const interval = seen.bound.kind === 'interval';
51
+ return canonical({
52
+ scope: seen.scope,
53
+ role: seen.role,
54
+ form: interval ? 'interval' : 'alphabet',
55
+ measure: seen.measure,
56
+ value: interval ? `${seen.bound.lo ?? 0}..${seen.bound.hi ?? 0}` : (seen.bound.values ?? []).join('|'),
57
+ by: `${reader}:${by}`,
58
+ at: `place:${digest}`,
59
+ });
60
+ }
61
+ const placed = new WeakMap();
62
+ export function placedReadings(observed) {
63
+ const already = placed.get(observed);
64
+ if (already !== undefined)
65
+ return already;
66
+ const taken = takeReadings(observed);
67
+ placed.set(observed, taken);
68
+ return taken;
69
+ }
70
+ export function casesHeld(observed) {
71
+ return new Set(placedReadings(observed).filter((r) => r.measure === 'pass' && r.value === 'pass' && cased(r.question)).map((r) => r.question)).size;
72
+ }
73
+ function takeReadings(observed) {
74
+ const placesOf = new Map();
75
+ for (const line of observed) {
76
+ if (!OBSERVED.test(line))
77
+ continue;
78
+ const key = `${readerOf(line) ?? ''} ${fieldOf(line, 'at')}`;
79
+ placesOf.set(key, (placesOf.get(key) ?? new Set()).add(fieldOf(line, 'scope')));
80
+ }
81
+ return observed.flatMap((line) => {
82
+ if (OBSERVED.test(line))
83
+ return [];
84
+ const reading = { question: fieldOf(line, 'scope'), measure: fieldOf(line, 'measure'), value: fieldOf(line, 'value'), by: fieldOf(line, 'by'), at: fieldOf(line, 'at') };
85
+ return [...(placesOf.get(`${readerOf(line) ?? ''} ${fieldOf(line, 'at')}`) ?? [])].map((place) => ({ ...reading, place }));
86
+ });
87
+ }
88
+ const behind = [];
89
+ export const readersBehind = () => behind;
90
+ const alive = (pid) => { try {
91
+ return process.kill(pid, 0);
92
+ }
93
+ catch {
94
+ return false;
95
+ } };
96
+ function travelled(store, speaker) {
97
+ for (const file of carriedIn(store)) {
98
+ const text = observeText(file);
99
+ if (text === undefined)
100
+ continue;
101
+ const marker = storeAt(store, 'cas', 'travelled', sha(text));
102
+ if (existsSync(marker))
103
+ continue;
104
+ const whole = new Map();
105
+ for (const line of text.split('\n').filter(isWire)) {
106
+ const bare = line.replace(/ sig="[^"]*"/, '');
107
+ if (!whole.has(bare) || / sig=/.test(line))
108
+ whole.set(bare, line);
109
+ }
110
+ const byHead = new Map();
111
+ for (const line of whole.values()) {
112
+ const head = (fieldOf(line, 'by').split(':')[0] ?? '').trim();
113
+ if (head)
114
+ byHead.set(head, [...(byHead.get(head) ?? []), line]);
115
+ }
116
+ for (const [head, lines] of byHead) {
117
+ if (head !== speaker) {
118
+ land(store, head, lines);
119
+ continue;
120
+ }
121
+ const at = new Map();
122
+ for (const line of lines.filter((one) => !OBSERVED.test(one)))
123
+ at.set(`${readerOf(line) ?? ''} ${fieldOf(line, 'at')}`, [...(at.get(`${readerOf(line) ?? ''} ${fieldOf(line, 'at')}`) ?? []), line]);
124
+ const groups = new Map();
125
+ for (const line of lines.filter((one) => OBSERVED.test(one))) {
126
+ const group = `${readerOf(line) ?? ''}\u0000`;
127
+ groups.set(group, [...(groups.get(group) ?? []), line]);
128
+ }
129
+ for (const [group, observed] of groups) {
130
+ const [by = '', region = ''] = group.split('\u0000');
131
+ landShard(store, speaker, region, observed);
132
+ const pairs = observed.map((line) => `${fieldOf(line, 'scope')} ${fieldOf(line, 'at')}`);
133
+ keepObservation(store, keyOf(by, region, pairs, 'receipt'), [...new Set(observed.map((line) => fieldOf(line, 'at')))].flatMap((one) => at.get(`${by} ${one}`) ?? []));
134
+ }
135
+ }
136
+ mkdirSync(dirname(marker), { recursive: true });
137
+ writeFileSync(marker, `${basename(file)}\n`);
138
+ }
139
+ }
140
+ function carried(store) {
141
+ const speaker = (() => { try {
142
+ return keyFor(store, 'read');
143
+ }
144
+ catch {
145
+ return undefined;
146
+ } })();
147
+ travelled(store, speaker ?? '');
148
+ return carriedIn(store).flatMap((file) => (observeText(file) ?? '').split('\n'))
149
+ .filter((line) => isWire(line) && speaker !== undefined && fieldOf(line, 'by').split(':')[0] === speaker);
150
+ }
151
+ const lineagesOf = (store, speaker, readers, regions) => new Set(readers.flatMap((reader) => shardLines(store, speaker, regionIn(regions, reader.module))
152
+ .filter((line) => fieldOf(line, 'scope') === `reader/${reader.module}` && /(?:^|\s)measure=(?:identity|extension)\s/.test(line)).map((line) => fieldOf(line, 'value'))));
153
+ export function readerLines(store) {
154
+ const readers = readersOf(store);
155
+ const regions = [...new Set(readers.flatMap((reader) => reader.where.map(trimmed)))];
156
+ const speaker = keyFor(store, 'read');
157
+ return [...new Set(readers.map((reader) => regionIn(regions, reader.module)))].flatMap((region) => shardLines(store, speaker, region).filter((line) => LEVEL.test(line)));
158
+ }
159
+ /**
160
+ * A reader never blocks a fold that did not ask to wait: what it read before is served at once, and a reader whose
161
+ * identity is new, or whose places moved, is grey by name and runs behind the fold. A fold that judges waits. A fold
162
+ * that reaches only some regions observes only those, and a place with no reader reads what it carries.
163
+ */
164
+ export const carriesOnly = (root, store) => ((lines) => !lines.length && !capsuleReadersOf(root, store, lockStanding(store), lines).length)(readersOf(store));
165
+ /** A reader whose `needs` meet the reach runs, wherever its module lives. The module path is not the region it reads. */
166
+ export function readerReaches(reach, where) {
167
+ if (reach === undefined)
168
+ return true;
169
+ const reached = (region) => reach.map(trimmed).some((one) => one === '' || region === one || region.startsWith(`${one}/`) || one.startsWith(`${region}/`));
170
+ return where.some((one) => reached(trimmed(one)));
171
+ }
172
+ export function observedClaims(root, store, options = {}) {
173
+ const wait = options.wait ?? true;
174
+ const standing = lockStanding(store);
175
+ const lines = readersOf(store);
176
+ const readers = [...lines, ...capsuleReadersOf(root, store, standing, lines)];
177
+ if (!readers.length)
178
+ return carried(store);
179
+ const speaker = keyFor(store, 'read');
180
+ const regions = [...new Set(readers.flatMap((reader) => reader.where.map(trimmed)))];
181
+ const whole = options.only === undefined && options.reach === undefined;
182
+ const legacy = whole ? legacyOf(store, speaker, regions) : undefined;
183
+ const reached = (region) => readerReaches(options.reach, [region]);
184
+ const shards = new Map();
185
+ const lineages = lineagesOf(store, speaker, readers, regions);
186
+ const open = (region) => shards.get(region) ?? shards.set(region, opened(store, speaker, region, lineages)).get(region);
187
+ const read_ = new Map();
188
+ const byAt = (key) => {
189
+ if (key === undefined)
190
+ return undefined;
191
+ if (!read_.has(key)) {
192
+ const lines = observation(store, key);
193
+ const at = new Map();
194
+ for (const line of lines ?? [])
195
+ at.set(fieldOf(line, 'at'), [...(at.get(fieldOf(line, 'at')) ?? []), line]);
196
+ read_.set(key, lines === undefined ? undefined : at);
197
+ }
198
+ return read_.get(key);
199
+ };
200
+ const lastOf = (shard, one, places) => {
201
+ if (shard.keys.has(one))
202
+ return byAt(shard.keys.get(one));
203
+ const known = places.flatMap((place) => shard.places.get(`${one} ${place}`) ?? []);
204
+ if (!known.length)
205
+ return undefined;
206
+ const pairs = known.map((held) => `${fieldOf(held.line, 'scope')} ${held.at}`);
207
+ if (legacy === undefined)
208
+ return byAt(keyOf(one, shard.region, pairs, 'receipt'));
209
+ const key = keyOf(one, shard.region, pairs);
210
+ keepObservation(store, key, [...new Set(known.map((held) => held.at))].flatMap((at) => legacy.get(`${one} ${at}`) ?? []));
211
+ landShard(store, speaker, shard.region, [pointer(speaker, one, shard.region, key, pairs)]);
212
+ shard.keys.set(one, key);
213
+ return byAt(key);
214
+ };
215
+ const servedAt = (shard, one, place, held) => {
216
+ const known = shard.places.get(`${one} ${place}`);
217
+ return known === undefined ? [] : [known.line, ...(held?.get(known.at) ?? [])];
218
+ };
219
+ const out = [];
220
+ const answered = options.answered;
221
+ const digestsAt = (key) => Object.entries(JSON.parse(memoized(store, `said ${key}`, () => {
222
+ const at = new Map();
223
+ for (const line of observation(store, key) ?? [])
224
+ at.set(fieldOf(line, 'at'), [...(at.get(fieldOf(line, 'at')) ?? []), line]);
225
+ return JSON.stringify(Object.fromEntries([...at].map(([one, lines]) => [one, saidDigest(store, lines)])));
226
+ })));
227
+ const heard = (lines) => {
228
+ const at = new Map();
229
+ for (const line of lines)
230
+ if (!OBSERVED.test(line))
231
+ at.set(`${readerOf(line) ?? ''} ${fieldOf(line, 'at')}`, [...(at.get(`${readerOf(line) ?? ''} ${fieldOf(line, 'at')}`) ?? []), line]);
232
+ for (const line of lines)
233
+ if (OBSERVED.test(line))
234
+ answered?.set(`${readerOf(line) ?? ''} ${fieldOf(line, 'at')}`, saidDigest(store, at.get(`${readerOf(line) ?? ''} ${fieldOf(line, 'at')}`) ?? []));
235
+ };
236
+ const began = Date.now();
237
+ let ran = 0;
238
+ let read = 0;
239
+ let whole_ = 0;
240
+ const roleOf = rolesOf(store);
241
+ const look = lookOf(root, roleOf);
242
+ behind.length = 0;
243
+ const spent = [];
244
+ for (const reader of readers) {
245
+ if (options.only !== undefined && reader.module !== options.only)
246
+ continue;
247
+ if (!readerReaches(options.reach, reader.where))
248
+ continue;
249
+ const started = Date.now();
250
+ const mark = out.length;
251
+ if (!observeOne(reader, started) && answered !== undefined)
252
+ heard(out.slice(mark));
253
+ }
254
+ function observeOne(reader, started) {
255
+ const module = resolve(root, reader.module);
256
+ const asked = reader.capsule === undefined ? undefined : capsuleAt(reader.capsule.digest, reader.capsule.entry);
257
+ const source = reader.capsule === undefined ? observeFile(module) : asked === undefined ? undefined : new Uint8Array();
258
+ const bundle = source && reader.capsule === undefined ? bundleOf(module) ?? 'unbundled' : 'none';
259
+ const offered = offersOf(root, store, reader, roleOf, look, (where) => reached(trimmed(where)));
260
+ const vectors = reader.capsule === undefined ? observeText(resolve(root, vectorsOf(reader.module, standing))) : undefined;
261
+ const kept = whole && legacy === undefined && answered === undefined && source ? sha([
262
+ speaker, reader.module, reader.kind, reader.shape.join('|'), reader.where.join('|'), bundle, vectors === undefined ? 'no vectors' : sha(vectors),
263
+ reader.capsule === undefined ? '' : `${reader.capsule.digest} ${reader.capsule.region} ${reader.capsule.reads.join('|')} ${[...reader.capsule.globs].map(([place, globs]) => `${place}${globs.join('|')}`).join(' ')}`,
264
+ ...offered.map((offer) => `${offer.place} ${offer.digest?.() ?? bytesDigest(store, offer.bytes())}`),
265
+ ].join('\n')) : undefined;
266
+ const stored = kept === undefined ? undefined : observeText(memoAt(store, `observed/${kept}`));
267
+ if (stored !== undefined) {
268
+ out.push(...stored.split('\n').filter(isWire));
269
+ read += offered.length;
270
+ whole_ += 1;
271
+ return false;
272
+ }
273
+ const mark = out.length;
274
+ const home = regionIn(regions, reader.module);
275
+ const own = shardLines(store, speaker, home).filter((line) => fieldOf(line, 'scope') === `reader/${reader.module}`);
276
+ const lineage = own.filter((line) => fieldOf(line, 'measure') === 'identity').map((line) => fieldOf(line, 'value'));
277
+ const sum = source && reader.capsule === undefined ? bytesDigest(store, new TextEncoder().encode(`${reader.module} ${bundle}`)).replace(/^sha256:/, '').slice(0, 12) : 'none';
278
+ const measured = reader.capsule !== undefined ? { by: source ? sha(`${reader.capsule.digest}\n${reader.capsule.region}`).slice(0, 12) : 'none', lines: [] }
279
+ : source ? identityOf({ store, root, speaker, module: reader.module, kind: reader.kind, code: sum, lineage, kept: own.filter((line) => /(?:^|\s)measure=(?:extension|emits)\s/.test(line)) })
280
+ : { by: 'none', lines: [] };
281
+ const by = measured.by;
282
+ const level = [...measured.lines];
283
+ const identified = Date.now();
284
+ const lines = [...lineage, by];
285
+ const before = [...lineage].reverse().find((one) => one !== by);
286
+ const regionsOf = new Map();
287
+ for (const offer of offered) {
288
+ const region = regionIn(regions, offer.place);
289
+ if (reached(region))
290
+ regionsOf.set(region, [...(regionsOf.get(region) ?? []), offer]);
291
+ }
292
+ const planned = [...regionsOf].map(([region, offers]) => {
293
+ const shard = open(region);
294
+ const places = offers.map((offer) => offer.place);
295
+ const pairs = [];
296
+ const kept = [];
297
+ const moved = [];
298
+ const all = [];
299
+ for (const offer of offers) {
300
+ const known = shard.places.get(`${by} ${offer.place}`);
301
+ if (known && fieldOf(known.line, 'value') === offer.seen) {
302
+ pairs.push(`${offer.place} ${known.at}`);
303
+ kept.push(known.line);
304
+ all.push({ ...offer, digest: known.at.slice('place:'.length) });
305
+ read += 1;
306
+ continue;
307
+ }
308
+ const bytes = offer.digest ? undefined : offer.bytes();
309
+ const digest = offer.digest?.() ?? bytesDigest(store, bytes);
310
+ pairs.push(`${offer.place} place:${digest}`);
311
+ kept.push(canonical({ scope: offer.place, role: 'writes', form: 'alphabet', measure: 'observed', value: offer.seen, by: `${speaker}:${by}`, at: `place:${digest}` }));
312
+ all.push({ ...offer, digest, bytes: () => bytes ?? offer.bytes() });
313
+ if (known?.at === `place:${digest}`)
314
+ read += 1;
315
+ else
316
+ moved.push({ place: offer.place, seen: offer.seen, digest, bytes: bytes ?? offer.bytes() });
317
+ }
318
+ return { region, shard, places, pairs, kept, moved, all, key: keyOf(by, region, pairs) };
319
+ });
320
+ if (answered !== undefined && planned.every((plan) => !plan.moved.length && observedAt(store, plan.key))) {
321
+ for (const plan of planned) {
322
+ for (const [at, digest] of digestsAt(plan.key))
323
+ answered.set(`${by} ${at}`, digest);
324
+ if (plan.shard.keys.get(by) !== plan.key || plan.kept.some((line) => plan.shard.places.get(`${by} ${fieldOf(line, 'scope')}`)?.line !== line)) {
325
+ landShard(store, speaker, plan.region, [...plan.kept, pointer(speaker, by, plan.region, plan.key, plan.pairs)]);
326
+ }
327
+ out.push(...plan.kept);
328
+ }
329
+ if (level.length)
330
+ landShard(store, speaker, home, level);
331
+ return true;
332
+ }
333
+ const plans = planned.map(({ region, shard, places, pairs, kept, moved, all, key }) => {
334
+ const prior = lastOf(shard, by, places);
335
+ if (before !== undefined)
336
+ lastOf(shard, before, places);
337
+ const ats = [...new Set(pairs.map((pair) => pair.slice(pair.indexOf(' ') + 1)))];
338
+ const found = byAt(key);
339
+ const said = found !== undefined ? ats.flatMap((at) => found.get(at) ?? [])
340
+ : moved.length || prior === undefined ? undefined : ats.flatMap((at) => prior.get(at) ?? []);
341
+ const due = said === undefined && !moved.length ? all.map((one) => ({ place: one.place, seen: one.seen, digest: one.digest, bytes: one.bytes() })) : moved;
342
+ return { region, shard, places, pairs, kept, due, key, said, prior, found: found !== undefined };
343
+ });
344
+ const due = plans.flatMap((plan) => (plan.said === undefined ? plan.due.map((one) => ({ ...one, region: plan.region })) : []));
345
+ const heldOf = (place, region) => {
346
+ for (const one of [...lines].reverse()) {
347
+ const shard = open(region);
348
+ const mine = servedAt(shard, one, place, lastOf(shard, one, [place])).slice(1);
349
+ if (mine.length)
350
+ return mine.map((line) => [fieldOf(line, 'scope'), fieldOf(line, 'value')]);
351
+ }
352
+ return [];
353
+ };
354
+ if (due.length && !source)
355
+ return false;
356
+ if (due.length && !wait) {
357
+ for (const plan of plans) {
358
+ if (plan.said !== undefined) {
359
+ out.push(...plan.kept, ...plan.said);
360
+ continue;
361
+ }
362
+ for (const place of plan.places) {
363
+ const mine = servedAt(plan.shard, by, place, plan.prior);
364
+ out.push(...(mine.length || before === undefined ? mine : servedAt(plan.shard, before, place, lastOf(plan.shard, before, plan.places))));
365
+ }
366
+ }
367
+ const marker = storeAt(store, 'pending', by);
368
+ const pid = Number((observeText(marker) ?? '').split('\n')[1]);
369
+ const running = existsSync(marker) && Date.now() - statSync(marker).mtimeMs < 30 * 60 * 1000 && Number.isInteger(pid) && pid > 0 && alive(pid);
370
+ if (!running) {
371
+ mkdirSync(dirname(marker), { recursive: true });
372
+ const child = spawn(process.execPath, [...childFlags(), processOf('observe'), root, reader.module], { cwd: root, detached: true, stdio: 'ignore' });
373
+ child.unref();
374
+ writeFileSync(marker, `${reader.module}\n${child.pid ?? ''}\n`);
375
+ }
376
+ behind.push({ module: reader.module, places: due.length });
377
+ process.stderr.write(`GREY reading: ${reader.module} · ${due.length} places · runs behind${running ? ', already running' : ''}\n`);
378
+ if (level.length)
379
+ landShard(store, speaker, home, level);
380
+ if (Date.now() - started > 250)
381
+ spent.push(`${reader.module.split('/').pop()} behind ${Date.now() - started}`);
382
+ return false;
383
+ }
384
+ if (due.length) {
385
+ const at = Date.now();
386
+ let rows;
387
+ try {
388
+ rows = reader.capsule !== undefined && asked !== undefined
389
+ ? asked.ask(due.map((d) => ({
390
+ protocol: PROTOCOL, verb: 'read', rootScope: d.place, region: reader.capsule.region, held: heldOf(d.place, d.region),
391
+ files: (reader.capsule.reads.some((read) => read.includes('@')) ? handedAt(root, reader, roleOf, look, d.place) : [d.place]).map((coordinate) => ({ place: coordinate, text: observeText(resolve(root, coordinate)) ?? '' })),
392
+ }))).map((answer) => (answer?.claims ?? []))
393
+ : runReader(module, root, due.map((d) => ({ place: d.place, ...(reader.kind === 'js' ? { text: new TextDecoder().decode(d.bytes) } : {}), held: heldOf(d.place, d.region) })));
394
+ }
395
+ catch (error) {
396
+ process.stderr.write(`REFUSE reader ${reader.module} · ${error instanceof Error ? error.message : String(error)}\n`);
397
+ return false;
398
+ }
399
+ if (rows.every((mine) => !mine.length)) {
400
+ process.stderr.write(`REFUSE reader ${reader.module} · answered none of ${due.length} places · nothing landed, they stay due\n`);
401
+ return false;
402
+ }
403
+ ran += due.length;
404
+ process.stderr.write(`READER ${reader.module} · ${due.length} run · ${Date.now() - at} ms\n`);
405
+ const fresh = new Map();
406
+ due.forEach((d, i) => fresh.set(d.place, (rows[i] ?? []).map((row) => lineOf(row, d.digest, by, speaker))));
407
+ for (const plan of plans.filter((one) => one.said === undefined)) {
408
+ const mine = new Set(plan.due.map((d) => d.place));
409
+ const stay = [...new Set(plan.pairs.filter((pair) => !mine.has(pair.slice(0, pair.indexOf(' ')))).map((pair) => pair.slice(pair.indexOf(' ') + 1)))];
410
+ const said = [...stay.flatMap((one) => plan.prior?.get(one) ?? []), ...[...mine].flatMap((place) => fresh.get(place) ?? [])];
411
+ keepObservation(store, plan.key, said);
412
+ landShard(store, speaker, plan.region, [...plan.kept, pointer(speaker, by, plan.region, plan.key, plan.pairs)]);
413
+ out.push(...plan.kept, ...said);
414
+ }
415
+ const spent = Date.now() - at;
416
+ const over = bytesDigest(store, new TextEncoder().encode(due.map((d) => d.digest).sort().join('\n'))).replace(/^sha256:/, '');
417
+ level.push(canonical({ scope: `cost/reader/${reader.module}`, role: 'writes', form: 'interval', measure: 'ms', value: `${spent}..${spent}`, by: `${speaker}:${by}`, at: `place:${over}` }));
418
+ if (lineage.at(-1) !== by)
419
+ level.push(canonical({ scope: `reader/${reader.module}`, role: 'writes', form: 'alphabet', measure: 'identity', value: by, by: `${speaker}:${by}`, at: `place:${by}` }));
420
+ rmSync(storeAt(store, 'pending', by), { force: true });
421
+ }
422
+ for (const plan of plans.filter((one) => one.said !== undefined)) {
423
+ if (!plan.found)
424
+ keepObservation(store, plan.key, plan.said);
425
+ if (plan.shard.keys.get(by) !== plan.key || plan.kept.some((line) => plan.shard.places.get(`${by} ${fieldOf(line, 'scope')}`)?.line !== line)) {
426
+ landShard(store, speaker, plan.region, [...plan.kept, pointer(speaker, by, plan.region, plan.key, plan.pairs)]);
427
+ }
428
+ out.push(...plan.kept, ...plan.said);
429
+ }
430
+ if (level.length)
431
+ landShard(store, speaker, home, level);
432
+ if (Date.now() - started > 250)
433
+ spent.push(`${reader.module.split('/').pop()} ${identified - started}+${Date.now() - identified}`);
434
+ if (kept !== undefined)
435
+ landMemo(store, `observed/${kept}`, new TextEncoder().encode(out.slice(mark).join('\n')));
436
+ return false;
437
+ }
438
+ if (spent.length)
439
+ process.stderr.write(`SPENT observe ${spent.join(' · ')} ms\n`);
440
+ if (legacy)
441
+ setAside(store, speaker);
442
+ process.stderr.write(`OBSERVE ${ran} run · ${read} read by key${whole_ ? ` · ${whole_} readers kept whole` : ''} · ${Date.now() - began} ms\n`);
443
+ return [...new Set(out)].sort();
444
+ }
@@ -0,0 +1,5 @@
1
+ /**
2
+ * What one landed line opens inside its place: a region whose reads cover the line's scope, a view whose shape covers
3
+ * it, and the scope's own tail. Every other region stays on its receipt at one step.
4
+ */
5
+ export declare function openedBy(places: readonly string[], standing: readonly string[], lines: readonly string[]): readonly string[];
@@ -0,0 +1,33 @@
1
+ import { matches } from '@lapxo/topos/wire';
2
+ import { fieldOf } from "./claims.js";
3
+ import { viewsOf } from "./views.js";
4
+ const wild = (glob) => glob.split('/').every((step) => step === '' || step === '*' || step === '**');
5
+ const covers = (glob, scope, rel) => !wild(glob) && (matches(glob, rel) || matches(glob, scope));
6
+ /**
7
+ * What one landed line opens inside its place: a region whose reads cover the line's scope, a view whose shape covers
8
+ * it, and the scope's own tail. Every other region stays on its receipt at one step.
9
+ */
10
+ export function openedBy(places, standing, lines) {
11
+ const known = new Set(places);
12
+ const reads = standing.filter((line) => fieldOf(line, 'scope').startsWith('region/') && fieldOf(line, 'measure') === 'reads' && fieldOf(line, 'value') !== 'withdraw');
13
+ const views = [...viewsOf(standing).values()];
14
+ const opened = new Set();
15
+ for (const line of lines) {
16
+ const scope = fieldOf(line, 'scope');
17
+ const [place = '', ...rest] = scope.split('/');
18
+ if (!known.has(place))
19
+ continue;
20
+ const rel = rest.join('/');
21
+ if (rel)
22
+ opened.add(`${place}/${rel}`);
23
+ for (const region of reads) {
24
+ const name = fieldOf(region, 'scope').slice('region/'.length);
25
+ if (covers(fieldOf(region, 'value'), scope, rel))
26
+ opened.add(`${place}/${name}`);
27
+ }
28
+ for (const view of views)
29
+ if (view.shape && covers(view.shape, scope, rel))
30
+ opened.add(`${place}/${view.name}`);
31
+ }
32
+ return [...opened].sort();
33
+ }
@@ -0,0 +1,24 @@
1
+ import type { CoordinateRole } from './wire.ts';
2
+ type RoleOf = (coordinate: string) => CoordinateRole;
3
+ export declare const outsideTheirCapsule: (root: string, standing: readonly string[], lines: readonly string[]) => readonly string[];
4
+ /**
5
+ * What the instrument reads of itself rather than through a reader: how many programs its source opens, how many
6
+ * signed lines stand unadmitted, how many stores it keeps, which of the modules it runs are no source, how many samples
7
+ * nothing names, how many alphabets are spelled both in code and in the lock, and how many regions it writes that no
8
+ * line of the lock describes, how many bodies are written twice anywhere in the tree rather than once and used by
9
+ * digest, and whether the version a package carries is the one the lock names. Inside one package, only what lies
10
+ * there. A cost is read now and never read back from the fold it was taken with: a beat is a line of the ledger.
11
+ */
12
+ export declare function costReadings(store: string, standing: readonly string[], under?: string): Readonly<Record<string, number>>;
13
+ export declare function ownReadings(input: {
14
+ readonly root: string;
15
+ readonly store: string;
16
+ readonly entry: string;
17
+ readonly roleOf: RoleOf;
18
+ readonly standing: readonly string[];
19
+ readonly observed: readonly string[];
20
+ readonly unadmitted: number;
21
+ readonly orphans: number;
22
+ readonly under?: string;
23
+ }): Readonly<Record<string, number>>;
24
+ export {};