@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.
Files changed (242) hide show
  1. package/CITATION.cff +19 -0
  2. package/TARGET.bound +95 -39
  3. package/dist/cli/accept.js +72 -558
  4. package/dist/cli/act-result.js +3 -103
  5. package/dist/cli/args.js +3 -44
  6. package/dist/cli/custodian.js +2 -29
  7. package/dist/cli/host.js +4 -32
  8. package/dist/cli/names.js +1 -12
  9. package/dist/cli/native-descendant.js +1 -19
  10. package/dist/cli/native.js +3 -60
  11. package/dist/cli/observe.js +1 -13
  12. package/dist/cli/owner.js +3 -139
  13. package/dist/cli/pass.js +21 -321
  14. package/dist/cli/place.js +6 -281
  15. package/dist/cli/release-result.js +3 -80
  16. package/dist/cli/retained-judgment.js +1 -25
  17. package/dist/cli/standing-result.js +4 -21
  18. package/dist/cli/takes.js +10 -111
  19. package/dist/cli/verb.d.ts +1 -2
  20. package/dist/cli/verb.js +83 -683
  21. package/dist/engine/computation.js +2 -0
  22. package/dist/engine/conformance.js +4 -0
  23. package/dist/engine/encoding.js +1 -2
  24. package/dist/engine/index.d.ts +1 -13
  25. package/dist/engine/index.js +2 -11
  26. package/dist/engine/object.js +1 -38
  27. package/dist/engine/ports.js +2 -103
  28. package/dist/fold/admission-history.js +1 -17
  29. package/dist/fold/bootstrap-anchor.js +1 -28
  30. package/dist/fold/ceilings.js +1 -142
  31. package/dist/fold/claims.js +2 -131
  32. package/dist/fold/closed.js +24 -234
  33. package/dist/fold/configures.js +1 -25
  34. package/dist/fold/digests.js +10 -226
  35. package/dist/fold/evidence.js +1 -22
  36. package/dist/fold/fold-worker.js +1 -13
  37. package/dist/fold/kept.js +12 -173
  38. package/dist/fold/keys.js +3 -91
  39. package/dist/fold/laid.js +3 -90
  40. package/dist/fold/nos.js +1 -48
  41. package/dist/fold/object.js +1 -2
  42. package/dist/fold/observed.js +27 -534
  43. package/dist/fold/opened.js +1 -33
  44. package/dist/fold/own.js +3 -217
  45. package/dist/fold/paid.js +1 -92
  46. package/dist/fold/place-inputs.js +1 -70
  47. package/dist/fold/places.js +3 -100
  48. package/dist/fold/rank.js +1 -9
  49. package/dist/fold/reach.js +1 -83
  50. package/dist/fold/readers.js +6 -142
  51. package/dist/fold/receipts.js +1 -47
  52. package/dist/fold/region-declarations.js +1 -13
  53. package/dist/fold/region.js +8 -286
  54. package/dist/fold/release-policy.js +1 -101
  55. package/dist/fold/resolved.js +6 -145
  56. package/dist/fold/roles.js +2 -59
  57. package/dist/fold/second.js +4 -71
  58. package/dist/fold/shards.js +12 -100
  59. package/dist/fold/signed.js +3 -81
  60. package/dist/fold/signers.js +1 -54
  61. package/dist/fold/stranger.js +6 -24
  62. package/dist/fold/views.js +1 -66
  63. package/dist/fold/wire.js +1 -46
  64. package/dist/fold/zoom.js +3 -81
  65. package/dist/host/act-cost.js +1 -25
  66. package/dist/host/adapters/keys/ed25519.js +1 -30
  67. package/dist/host/adapters/modules/javascript.js +1 -50
  68. package/dist/host/adapters/transport/http.js +1 -28
  69. package/dist/host/admitted-place.js +1 -63
  70. package/dist/host/archive.js +1 -152
  71. package/dist/host/call-context.js +1 -0
  72. package/dist/host/capsule-process.js +2 -61
  73. package/dist/host/capsule.js +9 -168
  74. package/dist/host/cell-receipts.js +12 -128
  75. package/dist/host/commit-result.js +1 -0
  76. package/dist/host/content-needed.js +1 -8
  77. package/dist/host/content-store.js +1 -31
  78. package/dist/host/contextual-place.js +2 -77
  79. package/dist/host/digest.js +2 -15
  80. package/dist/host/hash.js +1 -9
  81. package/dist/host/host-controls.js +1 -0
  82. package/dist/host/in-process-act.js +3 -0
  83. package/dist/host/in-process-context.js +3 -0
  84. package/dist/host/in-process-root.js +6 -0
  85. package/dist/host/in-process.js +4 -0
  86. package/dist/host/index.d.ts +1 -0
  87. package/dist/host/index.js +1 -0
  88. package/dist/host/io.js +1 -11
  89. package/dist/host/lifetime.js +1 -79
  90. package/dist/host/located.js +6 -105
  91. package/dist/host/native/bounded-file.js +1 -32
  92. package/dist/host/native/command.js +1 -91
  93. package/dist/host/native/control-bridge-worker.js +2 -88
  94. package/dist/host/native/control-bridge.js +1 -110
  95. package/dist/host/native/control-selection.js +1 -32
  96. package/dist/host/native/custody.js +1 -91
  97. package/dist/host/native/deadline.js +1 -28
  98. package/dist/host/native/descendants.js +1 -528
  99. package/dist/host/native/generated-custodian.js +5 -267
  100. package/dist/host/native/generated.js +1 -379
  101. package/dist/host/native/image.js +1 -45
  102. package/dist/host/native/jobs.js +2 -148
  103. package/dist/host/native/loader.js +1 -269
  104. package/dist/host/native/materialize.js +1 -175
  105. package/dist/host/native/module-closure.js +1 -130
  106. package/dist/host/native/module-delivery.d.ts +1 -62
  107. package/dist/host/native/module-delivery.js +1 -151
  108. package/dist/host/native/module-syntax.js +1 -69
  109. package/dist/host/native/native-binary.js +1 -115
  110. package/dist/host/native/native-program.js +1 -72
  111. package/dist/host/native/node-jobs.js +3 -187
  112. package/dist/host/native/node-launch.js +1 -159
  113. package/dist/host/native/preload.js +1 -45
  114. package/dist/host/native/reading.js +1 -249
  115. package/dist/host/native/receipt-environment.js +1 -41
  116. package/dist/host/native/references.js +1 -30
  117. package/dist/host/native/root.js +1 -37
  118. package/dist/host/native/route.js +1 -216
  119. package/dist/host/native/runtime.js +1 -9
  120. package/dist/host/native-reader-context.js +1 -8
  121. package/dist/host/native-receiver.js +1 -0
  122. package/dist/host/objects.js +1 -38
  123. package/dist/host/order.js +1 -2
  124. package/dist/host/place-act-transaction.js +4 -0
  125. package/dist/host/place-clock-selection.js +5 -124
  126. package/dist/host/ports/ingress.js +2 -57
  127. package/dist/host/ports/local-act.js +5 -172
  128. package/dist/host/ports/place-act.js +1 -169
  129. package/dist/host/ports/publication.js +1 -56
  130. package/dist/host/ports/signing.js +1 -74
  131. package/dist/host/ports/store.js +1 -18
  132. package/dist/host/ports/transport.js +1 -48
  133. package/dist/host/process-lifetime.js +1 -17
  134. package/dist/host/provide.js +1 -183
  135. package/dist/host/read-only.js +1 -9
  136. package/dist/host/reading-closures.js +7 -227
  137. package/dist/host/reading-context.js +1 -53
  138. package/dist/host/reading-identity-types.js +0 -0
  139. package/dist/host/reading-identity.js +3 -0
  140. package/dist/host/reading-preparation.js +6 -0
  141. package/dist/host/reference-authenticated.d.ts +1 -40
  142. package/dist/host/reference-authenticated.js +3 -100
  143. package/dist/host/reference-contracts.js +1 -6
  144. package/dist/host/reference-export.js +6 -81
  145. package/dist/host/reference-native-api.d.ts +1 -3
  146. package/dist/host/reference-native-api.js +1 -3
  147. package/dist/host/reference-native.js +1 -74
  148. package/dist/host/reference-profile.js +1 -63
  149. package/dist/host/reference-standing.js +2 -18
  150. package/dist/host/reference.js +1 -24
  151. package/dist/host/refusals.js +2 -43
  152. package/dist/host/release.js +4 -256
  153. package/dist/host/root-clock-source.js +1 -305
  154. package/dist/host/selected-capsules.js +1 -50
  155. package/dist/host/selected-topos.js +1 -80
  156. package/dist/host/signature-era.js +1 -0
  157. package/dist/host/signing.js +2 -48
  158. package/dist/host/source-content.js +2 -110
  159. package/dist/host/standing-carrier.js +1 -0
  160. package/dist/host/standing-projection.js +1 -0
  161. package/dist/host/timing.js +1 -7
  162. package/dist/host/walk.js +20 -193
  163. package/dist/judge/late.js +1 -33
  164. package/dist/judge/take.js +2 -75
  165. package/dist/judge/widening.js +1 -25
  166. package/dist/land/act.js +8 -302
  167. package/dist/land/alias.js +1 -41
  168. package/dist/land/held.js +1 -23
  169. package/dist/land/inert.js +1 -33
  170. package/dist/land/leaving.js +1 -55
  171. package/dist/land/ledger.js +7 -188
  172. package/dist/land/owned.js +4 -66
  173. package/dist/land/sign.js +1 -53
  174. package/dist/land/standing-pin.js +1 -49
  175. package/dist/land/vouched.js +1 -112
  176. package/dist/land/withdraw.js +1 -24
  177. package/dist/observe/files.js +1 -88
  178. package/dist/observe/identity.js +1 -61
  179. package/dist/observe/match.js +1 -8
  180. package/dist/observe/regions.js +7 -306
  181. package/dist/observe/run.js +2 -40
  182. package/dist/observe/runner.js +4 -93
  183. package/dist/public-types.d.ts +1114 -0
  184. package/dist/render/capsule.js +1 -30
  185. package/dist/render/field.js +1 -40
  186. package/dist/render/names.js +1 -4
  187. package/dist/render/open.js +1 -61
  188. package/dist/render/page.js +1 -7
  189. package/dist/render/receipts.js +1 -47
  190. package/dist/render/shape.js +1 -74
  191. package/dist/render/target.js +4 -52
  192. package/dist/render/view.js +7 -197
  193. package/dist/vendor/50086958553b9376d600583f7dff354196bfeb41170fc6d1482ca7bea7c493e2.LICENSE +21 -0
  194. package/dist/vendor/b3e938d28f88a1851af303a40c749e12ebd5a686daac4f0b343b5cdd2e040419.LICENSE +21 -0
  195. package/dist/vendor/bundle.json +1 -0
  196. package/dist/vendor/library11.js +1 -0
  197. package/dist/vendor/object-provider.js +42 -0
  198. package/dist/vendor/runtime.js +38 -0
  199. package/dist/vendor/sources.json +1 -0
  200. package/dist/wasm/adapter.d.ts +1 -51
  201. package/dist/wasm/adapter.js +1 -180
  202. package/dist/wasm/node-worker.js +1 -13
  203. package/dist/wasm/object.js +1 -76
  204. package/dist/wasm/worker.js +1 -7
  205. package/docs/contracts/closure-delivery.md +1 -1
  206. package/docs/contracts/closure-delivery.publication.json +1 -1
  207. package/docs/contracts/conformance.json +142 -0
  208. package/docs/contracts/determinism.bound +9 -0
  209. package/docs/contracts/engine-object-projection.publication.json +1 -1
  210. package/docs/contracts/engine.md +21 -1
  211. package/docs/contracts/native-host.md +21 -0
  212. package/docs/contracts/native-host.publication.json +25 -0
  213. package/docs/contracts/native-publication.inventory.json +3 -1
  214. package/docs/contracts/optional-wasm-adapter.publication.json +1 -1
  215. package/docs/contracts/publication.inventory.json +29 -0
  216. package/docs/contracts/reading-identity.md +37 -0
  217. package/docs/contracts/reading-identity.publication.json +25 -0
  218. package/docs/contracts/refusals.bound +51 -0
  219. package/docs/contracts/refusals.md +18 -3
  220. package/docs/contracts/refusals.publication.json +2 -2
  221. package/docs/contracts/views.md +8 -0
  222. package/docs/guide.md +10 -0
  223. package/docs/reference.md +40 -0
  224. package/docs/why.md +109 -0
  225. package/package.json +37 -18
  226. package/receipts.bound +11675 -9725
  227. package/samples/native-host/no.json +10 -0
  228. package/samples/native-host/yes.json +20 -0
  229. package/samples/reading-identity/no.json +20 -0
  230. package/samples/reading-identity/yes.json +46 -0
  231. package/vectors/native/native-host.vector.json +62 -0
  232. package/vectors/native/reading-identity.vector.json +101 -0
  233. package/vectors/native/refusals.vector.json +104 -0
  234. package/vendor/syntax/LICENSE.md +7 -0
  235. package/vendor/syntax/parser.js +1 -0
  236. package/dist/engine/object.d.ts +0 -17
  237. package/dist/engine/ports.d.ts +0 -32
  238. package/dist/host/native/module-closure.d.ts +0 -35
  239. package/dist/host/reference-export.d.ts +0 -33
  240. package/dist/host/reference-native.d.ts +0 -30
  241. package/dist/host/reference-profile.d.ts +0 -23
  242. package/dist/host/refusals.d.ts +0 -14
@@ -1,152 +1 @@
1
- import { lstatSync, mkdirSync, writeFileSync } from 'node:fs';
2
- import { dirname, join, resolve } from 'node:path';
3
- import { gunzipSync } from 'node:zlib';
4
- const BLOCK = 512; // POSIX tar record size.
5
- const refuse = (why) => { throw new Error(`REFUSE·archive ${why}`); };
6
- /** POSIX archive coordinates; normalization cannot make two members share one destination. */
7
- export function archiveCoordinate(name) {
8
- if (!name || name.startsWith('/') || /^[A-Za-z]:/.test(name) || name.includes('\\') || name.includes('\0'))
9
- refuse(`invalid coordinate ${JSON.stringify(name)}`);
10
- const parts = name.replace(/\/+$/, '').split('/');
11
- while (parts[0] === '.')
12
- parts.shift();
13
- if (!parts.length || parts.some((part) => !part || part === '.' || part === '..'))
14
- refuse(`invalid coordinate ${JSON.stringify(name)}`);
15
- return parts.join('/');
16
- }
17
- function pax(bytes) {
18
- const fields = {};
19
- for (let at = 0; at < bytes.length;) {
20
- const space = bytes.indexOf(32, at);
21
- if (space < 0)
22
- refuse('malformed PAX record length');
23
- const lengthText = new TextDecoder().decode(bytes.subarray(at, space));
24
- if (!/^\d+$/.test(lengthText))
25
- refuse('malformed PAX record length');
26
- const length = Number(lengthText);
27
- const end = at + length;
28
- if (!Number.isSafeInteger(length) || length <= space - at + 1 || end > bytes.length || bytes[end - 1] !== 10)
29
- refuse('truncated PAX record');
30
- const record = new TextDecoder().decode(bytes.subarray(space + 1, end - 1));
31
- const equals = record.indexOf('=');
32
- if (equals < 1)
33
- refuse('malformed PAX attribute');
34
- const key = record.slice(0, equals);
35
- if (key.startsWith('GNU.sparse') || key.startsWith('SCHILY.realsize'))
36
- refuse('sparse PAX members are unsupported');
37
- fields[key] = record.slice(equals + 1);
38
- at = end;
39
- }
40
- return fields;
41
- }
42
- /** The gzip/POSIX tar adapter reads regular files, directories and PAX attributes, or rejects the whole archive. */
43
- export function members(archive) {
44
- const bytes = gunzipSync(archive);
45
- const text = (at, length) => bytes.subarray(at, at + length).toString('utf8').replace(/\0[\s\S]*$/, '');
46
- const octal = (at, length) => {
47
- const raw = text(at, length).trim();
48
- if (!/^[0-7]+$/.test(raw))
49
- refuse(`invalid octal field at ${at}`);
50
- const n = Number.parseInt(raw, 8);
51
- if (!Number.isSafeInteger(n) || n < 0)
52
- refuse(`unrepresentable field at ${at}`);
53
- return n;
54
- };
55
- const out = [];
56
- const files = new Set();
57
- const directories = new Set();
58
- let global = {};
59
- let local;
60
- for (let at = 0;;) {
61
- if (at + BLOCK > bytes.length)
62
- refuse('missing complete end records');
63
- const header = bytes.subarray(at, at + BLOCK);
64
- if (header.every((byte) => byte === 0)) {
65
- if (local !== undefined)
66
- refuse('PAX attributes have no following member');
67
- if (at + 2 * BLOCK > bytes.length || !bytes.subarray(at).every((byte) => byte === 0))
68
- refuse('invalid end records or trailing data');
69
- return out;
70
- }
71
- const checksum = header.reduce((sum, byte, i) => sum + (i >= 148 && i < 156 ? 32 : byte), 0);
72
- if (octal(at + 148, 8) !== checksum)
73
- refuse(`header checksum mismatch at ${at}`);
74
- const magic = text(at + 257, 6);
75
- if (magic && magic !== 'ustar' && magic !== 'ustar ')
76
- refuse(`unsupported tar format ${magic}`);
77
- const type = text(at + 156, 1);
78
- const attrs = { ...global, ...local };
79
- let size = octal(at + 124, 12);
80
- if (type !== 'x' && type !== 'g' && attrs['size'] !== undefined) {
81
- if (!/^\d+$/.test(attrs['size']))
82
- refuse('invalid PAX size');
83
- size = Number(attrs['size']);
84
- if (!Number.isSafeInteger(size))
85
- refuse('unrepresentable PAX size');
86
- }
87
- const next = at + BLOCK + Math.ceil(size / BLOCK) * BLOCK;
88
- if (next > bytes.length)
89
- refuse(`truncated member at ${at}`);
90
- const body = bytes.subarray(at + BLOCK, at + BLOCK + size);
91
- if (type === 'x' || type === 'g') {
92
- const fields = pax(body);
93
- if (type === 'g')
94
- global = { ...global, ...fields };
95
- else
96
- local = { ...local, ...fields };
97
- }
98
- else {
99
- const rawName = attrs['path'] ?? [magic ? text(at + 345, 155) : '', text(at, 100)].filter(Boolean).join('/');
100
- local = undefined;
101
- if (type === '5' && /^\.\/?$/.test(rawName) && size === 0) {
102
- at = next;
103
- continue;
104
- }
105
- const name = archiveCoordinate(rawName);
106
- if (!['', '0', '5'].includes(type))
107
- refuse(`unsupported member type ${type} at ${name}`);
108
- const steps = name.split('/');
109
- if (steps.slice(0, -1).some((_, i) => files.has(steps.slice(0, i + 1).join('/'))))
110
- refuse(`file is an ancestor of ${name}`);
111
- if (type === '5') {
112
- if (size !== 0 || files.has(name))
113
- refuse(`invalid directory ${name}`);
114
- directories.add(name);
115
- }
116
- else {
117
- if (files.has(name) || directories.has(name))
118
- refuse(`duplicate or conflicting member ${name}`);
119
- files.add(name);
120
- steps.slice(0, -1).forEach((_, i) => directories.add(steps.slice(0, i + 1).join('/')));
121
- out.push({ name, bytes: body });
122
- }
123
- }
124
- at = next;
125
- }
126
- }
127
- /** Existing links or files are never followed or overwritten by extraction. */
128
- export function archiveDestination(into, name) {
129
- const root = resolve(into);
130
- const parts = archiveCoordinate(name).split('/');
131
- let at = root;
132
- const base = lstatSync(at, { throwIfNoEntry: false });
133
- if (base !== undefined && !base.isDirectory())
134
- refuse(`destination is not a directory: ${root}`);
135
- for (let i = 0; i < parts.length; i += 1) {
136
- at = join(at, parts[i]);
137
- const st = lstatSync(at, { throwIfNoEntry: false });
138
- if (st !== undefined && (i === parts.length - 1 || !st.isDirectory()))
139
- refuse(`destination already occupied: ${at}`);
140
- }
141
- return at;
142
- }
143
- export function unpack(archive, into) {
144
- const files = members(archive);
145
- const destinations = files.map((one) => archiveDestination(into, one.name));
146
- for (const [i, one] of files.entries()) {
147
- const at = destinations[i];
148
- mkdirSync(dirname(at), { recursive: true });
149
- writeFileSync(at, one.bytes, { flag: 'wx' });
150
- }
151
- return files.map((one) => one.name);
152
- }
1
+ var X=Object.defineProperty;var p=(t,r)=>X(t,"name",{value:r,configurable:!0});import{lstatSync as z,mkdirSync as I,writeFileSync as E}from"node:fs";import{dirname as j,join as k,resolve as D}from"node:path";import{gunzipSync as O}from"node:zlib";const g=512,n=p(t=>{throw new Error(`REFUSE\xB7archive ${t}`)},"refuse");function A(t){(!t||t.startsWith("/")||/^[A-Za-z]:/.test(t)||t.includes("\\")||t.includes("\0"))&&n(`invalid coordinate ${JSON.stringify(t)}`);const r=t.replace(/\/+$/,"").split("/");for(;r[0]===".";)r.shift();return(!r.length||r.some(i=>!i||i==="."||i===".."))&&n(`invalid coordinate ${JSON.stringify(t)}`),r.join("/")}p(A,"archiveCoordinate");function C(t){const r={};for(let i=0;i<t.length;){const c=t.indexOf(32,i);c<0&&n("malformed PAX record length");const s=new TextDecoder().decode(t.subarray(i,c));/^\d+$/.test(s)||n("malformed PAX record length");const a=Number(s),o=i+a;(!Number.isSafeInteger(a)||a<=c-i+1||o>t.length||t[o-1]!==10)&&n("truncated PAX record");const d=new TextDecoder().decode(t.subarray(c+1,o-1)),u=d.indexOf("=");u<1&&n("malformed PAX attribute");const e=d.slice(0,u);(e.startsWith("GNU.sparse")||e.startsWith("SCHILY.realsize"))&&n("sparse PAX members are unsupported"),r[e]=d.slice(u+1),i=o}return r}p(C,"pax");function T(t){const r=O(t),i=p((e,y)=>r.subarray(e,e+y).toString("utf8").replace(/\0[\s\S]*$/,""),"text"),c=p((e,y)=>{const x=i(e,y).trim();/^[0-7]+$/.test(x)||n(`invalid octal field at ${e}`);const m=Number.parseInt(x,8);return(!Number.isSafeInteger(m)||m<0)&&n(`unrepresentable field at ${e}`),m},"octal"),s=[],a=new Set,o=new Set;let d={},u;for(let e=0;;){e+g>r.length&&n("missing complete end records");const y=r.subarray(e,e+g);if(y.every(f=>f===0))return u!==void 0&&n("PAX attributes have no following member"),(e+2*g>r.length||!r.subarray(e).every(f=>f===0))&&n("invalid end records or trailing data"),s;const x=y.reduce((f,l,b)=>f+(b>=148&&b<156?32:l),0);c(e+148,8)!==x&&n(`header checksum mismatch at ${e}`);const m=i(e+257,6);m&&m!=="ustar"&&m!=="ustar "&&n(`unsupported tar format ${m}`);const h=i(e+156,1),S={...d,...u};let $=c(e+124,12);h!=="x"&&h!=="g"&&S.size!==void 0&&(/^\d+$/.test(S.size)||n("invalid PAX size"),$=Number(S.size),Number.isSafeInteger($)||n("unrepresentable PAX size"));const v=e+g+Math.ceil($/g)*g;v>r.length&&n(`truncated member at ${e}`);const N=r.subarray(e+g,e+g+$);if(h==="x"||h==="g"){const f=C(N);h==="g"?d={...d,...f}:u={...u,...f}}else{const f=S.path??[m?i(e+345,155):"",i(e,100)].filter(Boolean).join("/");if(u=void 0,h==="5"&&/^\.\/?$/.test(f)&&$===0){e=v;continue}const l=A(f);["","0","5"].includes(h)||n(`unsupported member type ${h} at ${l}`);const b=l.split("/");b.slice(0,-1).some((P,w)=>a.has(b.slice(0,w+1).join("/")))&&n(`file is an ancestor of ${l}`),h==="5"?(($!==0||a.has(l))&&n(`invalid directory ${l}`),o.add(l)):((a.has(l)||o.has(l))&&n(`duplicate or conflicting member ${l}`),a.add(l),b.slice(0,-1).forEach((P,w)=>o.add(b.slice(0,w+1).join("/"))),s.push({name:l,bytes:N}))}e=v}}p(T,"members");function W(t,r){const i=D(t),c=A(r).split("/");let s=i;const a=z(s,{throwIfNoEntry:!1});a!==void 0&&!a.isDirectory()&&n(`destination is not a directory: ${i}`);for(let o=0;o<c.length;o+=1){s=k(s,c[o]);const d=z(s,{throwIfNoEntry:!1});d!==void 0&&(o===c.length-1||!d.isDirectory())&&n(`destination already occupied: ${s}`)}return s}p(W,"archiveDestination");function U(t,r){const i=T(t),c=i.map(s=>W(r,s.name));for(const[s,a]of i.entries()){const o=c[s];I(j(o),{recursive:!0}),E(o,a.bytes,{flag:"wx"})}return i.map(s=>s.name)}p(U,"unpack");export{A as archiveCoordinate,W as archiveDestination,T as members,U as unpack};
@@ -0,0 +1 @@
1
+ var n=Object.defineProperty;var e=(t,o)=>n(t,"name",{value:o,configurable:!0});import{AsyncLocalStorage as u}from"node:async_hooks";const r=new u,i=e(()=>r.getStore()?.instrumentName,"hostInstrumentName"),m=e(()=>r.getStore()?.runtime,"hostInstrumentRuntime"),l=e((t,o)=>r.run({...r.getStore(),instrumentName:t},o),"withHostInstrument"),a=e((t,o)=>r.run(t,o),"withHostCall");function g(t,o){const s=r.getStore()?.output;s?s(t,o):(t==="out"?process.stdout:process.stderr).write(o)}e(g,"hostOutput");export{i as hostInstrumentName,m as hostInstrumentRuntime,g as hostOutput,a as withHostCall,l as withHostInstrument};
@@ -1,61 +1,2 @@
1
- import { actCosts } from "./act-cost.js";
2
- import { fileURLToPath } from 'node:url';
3
- import { boundedProcess } from "./process-lifetime.js";
4
- import { spawnSync } from 'node:child_process';
5
- import { PROTOCOL } from '@lapxo/topos/wire';
6
- /** Operational process failure, distinct from a valid contract refusal or abstention. */
7
- export class CapsuleProcessError extends Error {
8
- constructor(reason, result) {
9
- const stdout = result.stdout ?? '';
10
- const stderr = result.stderr ?? '';
11
- super(`REFUSE·capsule process ${reason}\n${stderr}${stdout}`);
12
- this.name = 'CapsuleProcessError';
13
- this.stdout = stdout;
14
- this.stderr = stderr;
15
- this.status = result.status;
16
- this.signal = result.signal;
17
- this.cause = result.error;
18
- }
19
- }
20
- export function validResponse(value, request) {
21
- if (value === null || typeof value !== 'object' || Array.isArray(value))
22
- return false;
23
- const one = value;
24
- if (one['protocol'] !== PROTOCOL)
25
- return false;
26
- if (one['kind'] === 'refuse' || one['kind'] === 'abstain')
27
- return typeof one['why'] === 'string';
28
- if (one['kind'] !== 'fact')
29
- return false;
30
- if (request.verb === 'render')
31
- return Array.isArray(one['lines']) && one['lines'].every((line) => typeof line === 'string');
32
- if (request.verb === 'read')
33
- return Array.isArray(one['claims']);
34
- return true;
35
- }
36
- /** Last-line JSON is the wire framing. No partial batch, failed exit or synthetic missing response is accepted. */
37
- export function executeCapsule(command, args, requests, limits = {}, lifetime) {
38
- return actCosts.measure(() => {
39
- const cwd = limits.cwd;
40
- const directory = cwd === undefined ? undefined : typeof cwd === 'string' ? cwd : fileURLToPath(cwd);
41
- const run = lifetime === undefined ? spawnSync(command, args, { input: JSON.stringify(requests), encoding: 'utf8', maxBuffer: 1 << 26, ...limits }) : boundedProcess(command, args, JSON.stringify(requests), lifetime, directory, limits.env);
42
- if ('error' in run && run.error)
43
- throw new CapsuleProcessError(`failed: ${run.error.message}`, run);
44
- if (run.signal !== null || run.status !== 0)
45
- throw new CapsuleProcessError(`exited ${run.status ?? `with signal ${run.signal}`}`, run);
46
- const last = /(?:^|\n)([^\n]*)$/.exec(run.stdout.trimEnd())?.[1] ?? '';
47
- let values;
48
- try {
49
- values = JSON.parse(last);
50
- }
51
- catch (error) {
52
- throw new CapsuleProcessError('returned malformed or absent JSON', { ...run, error });
53
- }
54
- if (!Array.isArray(values) || values.length !== requests.length || !values.every((value, i) => validResponse(value, requests[i]))) {
55
- throw new CapsuleProcessError(`returned an invalid batch; expected ${requests.length} responses`, run);
56
- }
57
- if (run.stderr)
58
- process.stderr.write(run.stderr);
59
- return values;
60
- });
61
- }
1
+ var y=Object.defineProperty;var f=(e,n)=>y(e,"name",{value:n,configurable:!0});import{hostOutput as h}from"./call-context.js";import{actCosts as m}from"./act-cost.js";import{fileURLToPath as g}from"node:url";import{boundedProcess as w}from"./process-lifetime.js";import{spawnSync as O}from"node:child_process";import{library11_PROTOCOL as x}from"../vendor/runtime.js";class d extends Error{static{f(this,"CapsuleProcessError")}constructor(n,r){const s=r.stdout??"",o=r.stderr??"";super(`REFUSE\xB7capsule process ${n}
2
+ ${o}${s}`),this.name="CapsuleProcessError",this.stdout=s,this.stderr=o,this.status=r.status,this.signal=r.signal,this.cause=r.error}}function b(e,n){if(e===null||typeof e!="object"||Array.isArray(e))return!1;const r=e;return r.protocol!==x?!1:r.kind==="refuse"||r.kind==="abstain"?typeof r.why=="string":r.kind!=="fact"?!1:n.verb==="render"?Array.isArray(r.lines)&&r.lines.every(s=>typeof s=="string"):n.verb==="read"?Array.isArray(r.claims):!0}f(b,"validResponse");function R(e,n,r,s={},o){return m.measure(()=>{const a=s.cwd,u=a===void 0?void 0:typeof a=="string"?a:g(a),t=o===void 0?O(e,n,{input:JSON.stringify(r),encoding:"utf8",maxBuffer:1<<26,...s}):w(e,n,JSON.stringify(r),o,u,s.env);if("error"in t&&t.error)throw new d(`failed: ${t.error.message}`,t);if(t.signal!==null||t.status!==0)throw new d(`exited ${t.status??`with signal ${t.signal}`}`,t);const p=/(?:^|\n)([^\n]*)$/.exec(t.stdout.trimEnd())?.[1]??"";let i;try{i=JSON.parse(p)}catch(c){throw new d("returned malformed or absent JSON",{...t,error:c})}if(!Array.isArray(i)||i.length!==r.length||!i.every((c,l)=>b(c,r[l])))throw new d(`returned an invalid batch; expected ${r.length} responses`,t);return t.stderr&&h("error",t.stderr),i})}f(R,"executeCapsule");export{d as CapsuleProcessError,R as executeCapsule,b as validResponse};
@@ -1,168 +1,9 @@
1
- import { readerLifetime, parse } from '@lapxo/topos/wire';
2
- import { contentStore, verifiedContent, contentPolicy } from "./content-store.js";
3
- import { readOnly } from "./read-only.js";
4
- import { readingBoundary } from "./reading-context.js";
5
- import { nativeReaderRoute } from "./native-reader-context.js";
6
- import { assertNativeRouteCurrent, invokeNativeRoute, materializeNativeRoute } from "./native/route.js";
7
- import { sha } from "./hash.js";
8
- import { wireLinesOf as linesOf } from "../fold/claims.js";
9
- import { createHash } from 'node:crypto';
10
- import { mkdtempSync, rmSync, statSync } from 'node:fs';
11
- import { tmpdir } from 'node:os';
12
- import { basename, join, resolve, relative, isAbsolute, sep } from 'node:path';
13
- import { CAPSULE } from '@lapxo/topos/wire';
14
- import { declarationOf } from '@lapxo/topos/capsule';
15
- import { unpack } from "./archive.js";
16
- import { executeCapsule, validResponse } from "./capsule-process.js";
17
- import { holdStore, replaceWhole, storeAt } from "../land/ledger.js";
18
- import { coordinatesUnder, observeFile, observeText } from "../observe/files.js";
19
- import { hostOf } from "../observe/run.js";
20
- import { ownLock, ownRoot, ownStore, processOf } from "../observe/runner.js";
21
- import { said } from "../fold/wire.js";
22
- const declared = new Map();
23
- const laid = new Map();
24
- process.on('exit', () => { for (const at of laid.values())
25
- rmSync(at, { recursive: true, force: true }); });
26
- /** A blob is unpacked once in a process, where every run of it the process asks for reads it. */
27
- function unpacked(digest, bytes) {
28
- const held = laid.get(digest);
29
- if (held !== undefined)
30
- return held;
31
- const at = mkdtempSync(join(tmpdir(), `${basename(ownRoot())}-capsule-`));
32
- try {
33
- unpack(bytes, at);
34
- }
35
- catch (error) {
36
- rmSync(at, { recursive: true, force: true });
37
- throw error;
38
- }
39
- laid.set(digest, at);
40
- return at;
41
- }
42
- /**
43
- * A capsule is the bytes a digest names in the instrument's own store, and only when they hash to it: its own lock is
44
- * read from them, and the coordinate a lock names runs as a process of its own, like a reader. Each answer is kept by the
45
- * digest of its complete declared request and implementation; distinct views retain distinct answers, and the same
46
- * request reads its verified answer without running again. Operational failures never enter the cache; refusals and abstentions are asked again.
47
- */
48
- const capsules = new Map();
49
- /** A blob is loaded once in an act. A later ask for the same digest reads that capsule, and does not hash or unpack it again. */
50
- export function capsuleAt(digest, entry, store) {
51
- const cacheStore = store ?? contentStore();
52
- const { bytes } = verifiedContent(digest, cacheStore);
53
- const identity = `${cacheStore}\0${digest}\0${entry}`;
54
- const held = capsules.get(identity);
55
- if (held !== undefined)
56
- return held;
57
- const known = declared.get(digest) ?? ((lines) => ({ lines, declaration: declarationOf(lines) }))(linesOf(observeText(join(unpacked(digest, bytes), CAPSULE))));
58
- declared.set(digest, known);
59
- const names = Object.keys(known.declaration.regions);
60
- const root = unpacked(digest, bytes);
61
- const module = resolve(root, entry);
62
- const coordinate = relative(root, module);
63
- if (!entry || isAbsolute(entry) || (coordinate === '..' || coordinate.startsWith(`..${sep}`)) || entry.split(/[\\/]/).includes('..')) {
64
- throw new Error(`REFUSE·capsule ${digest} declared entry ${entry || '(absent)'} unavailable`);
65
- }
66
- const capsule = { digest, ...known, ask: (requests, reader) => {
67
- if (!statSync(module, { throwIfNoEntry: false })?.isFile())
68
- throw new Error(`REFUSE·capsule ${digest} declared entry ${entry} unavailable`);
69
- return answered(digest, bytes, requests, names, root, cacheStore, module, reader);
70
- } };
71
- capsules.set(identity, capsule);
72
- return capsule;
73
- }
74
- const layoutsOf = () => said(ownLock(), 'release/layout')
75
- .map((token) => token.split(':')).filter((pair) => pair.length === 2 && pair[0] !== undefined && pair[1] !== undefined);
76
- const oneModule = (at) => {
77
- const files = coordinatesUnder(at, at).filter((one) => one.endsWith('.js'));
78
- return files.length === 1 ? files[0] : undefined;
79
- };
80
- const split = new Map();
81
- const spawnOf = (at, names) => {
82
- const held = split.get(at);
83
- if (held !== undefined)
84
- return held;
85
- const layout = names.length === 0 ? undefined : layoutsOf().find(([dir, ext]) => names.some((name) => observeText(join(at, dir, `${name}${ext}`)) !== undefined));
86
- const child = layout !== undefined ? [processOf('located', 'host'), at, layout[0], layout[1]]
87
- : ((file) => (file === undefined ? [] : [processOf('located', 'host'), at, file, 'render']))(oneModule(at));
88
- split.set(at, child);
89
- return child;
90
- };
91
- export const childFlags = (argv = process.execArgv) => argv.filter((flag, i) => !/^(-e|-p|--eval|--print)$/.test(argv[i - 1] ?? '') && !/^(-e|-p|--eval|--print|--input-type)(=|$)/.test(flag));
92
- function answered(digest, bytes, requests, names, tree, cacheStore = ownStore(), entry, reader) {
93
- const boundary = readingBoundary();
94
- const native = nativeReaderRoute();
95
- if (native) {
96
- if (!boundary?.execution || digest !== native.profile.selection.archive)
97
- throw Error('REFUSE·native reader lacks its complete implementation identity');
98
- assertNativeRouteCurrent(native);
99
- }
100
- const child = entry === undefined ? spawnOf(tree ?? unpacked(digest, bytes), names) : [entry];
101
- if (!child.length) {
102
- process.stderr.write(`LOAD regions · none of ${names.length} under the directories release/layout names\n`);
103
- return requests.map(() => undefined);
104
- }
105
- const loader = entry !== undefined ? `${createHash('sha256').update(`${relative(tree, entry)}\n`).digest('hex')}\n` : child.length > 1 ? `${createHash('sha256').update(observeFile(child[0]) ?? new Uint8Array()).digest('hex')}\n` : '';
106
- const lifetime = readerLifetime(contentPolicy().flatMap(line => { const p = parse(line); return p.kind === 'fact' ? [p.value.fields] : []; }), reader ?? digest);
107
- const policy = lifetime === undefined ? '' : JSON.stringify(lifetime) + '\n';
108
- const hitOf = (request) => sha(`${digest}\n${loader}${policy}${boundary === undefined ? '' : boundary.identity + '\n'}${JSON.stringify(request)}`);
109
- const hits = requests.map(hitOf);
110
- const kept = hits.map(hit => storeAt(cacheStore, 'cas', 'answers', hit));
111
- const held = kept.map((at, i) => {
112
- const text = observeText(at);
113
- if (text === undefined)
114
- return undefined;
115
- try {
116
- const got = JSON.parse(text);
117
- return got.format === 'capsule-answer/2' && got.hit === hits[i] && validResponse(got.said, requests[i]) && got.said.kind === 'fact' ? got.said : undefined;
118
- }
119
- catch (error) {
120
- throw new Error(`${at}: ${error instanceof Error ? error.message : String(error)}`);
121
- }
122
- });
123
- const missing = requests.filter((_, i) => held[i] === undefined);
124
- if (missing.length && readOnly())
125
- throw Error(`REFUSE·preview capsule ${digest} has no verified answer for the proposed inputs; fold the declared inputs first`);
126
- if (missing.length)
127
- process.stderr.write(`CAPSULE ${digest.slice(7, 19)} · ${requests.length} asked · ${missing.length} run · ${missing.map((one) => `${one.rootScope}${one.region}`).join(' ')}\n`);
128
- // Execute and validate the entire batch before committing any answer to the cache.
129
- if (native && !lifetime)
130
- throw Error('REFUSE·native reader missing declared lifetime');
131
- const ran = !missing.length ? [] : native
132
- ? invokeNativeRoute(native, { ...materializeNativeRoute(native), requests: missing, execution: boundary.execution, limits: lifetime })
133
- : executeCapsule(process.execPath, [...childFlags(boundary?.execution?.flags), hostOf(), ...child], missing, boundary?.execution ? { env: boundary.execution.env, cwd: boundary.execution.cwd } : {}, lifetime);
134
- return requests.map((request, i) => {
135
- if (held[i] !== undefined)
136
- return held[i];
137
- const said = ran[missing.indexOf(request)];
138
- if (said?.kind === 'fact') {
139
- const text = JSON.stringify({ format: 'capsule-answer/2', hit: hits[i], said });
140
- replaceWhole(kept[i], text);
141
- holdStore(cacheStore, 'answers', hits[i], Buffer.byteLength(text));
142
- }
143
- return said;
144
- });
145
- }
146
- /** The host may supply its cache store independently of the located executable. */
147
- export function locatedAt(tree, place, cacheStore = ownStore()) {
148
- const at = join(tree, place);
149
- const lines = linesOf(observeText(join(at, CAPSULE)));
150
- if (!lines.length)
151
- return undefined;
152
- const known = { lines, declaration: declarationOf(lines) };
153
- const names = Object.keys(known.declaration.regions);
154
- if (!spawnOf(at, names).length)
155
- return undefined;
156
- const hash = createHash('sha256');
157
- hash.update(observeFile(join(at, CAPSULE)) ?? new Uint8Array());
158
- for (const name of names)
159
- for (const [dir, ext] of layoutsOf()) {
160
- const bytes = observeFile(join(at, dir, `${name}${ext}`));
161
- if (bytes !== undefined) {
162
- hash.update(bytes);
163
- break;
164
- }
165
- }
166
- const digest = `sha256:${hash.digest('hex')}`;
167
- return { digest, ...known, ask: (requests, reader) => answered(digest, new Uint8Array(), requests, names, at, cacheStore, undefined, reader) };
168
- }
1
+ var G=Object.defineProperty;var l=(e,n)=>G(e,"name",{value:n,configurable:!0});import{hostOutput as F}from"./call-context.js";import{library11_readerLifetime as K,library11_parse as Q}from"../vendor/runtime.js";import{contentStore as V,verifiedContent as X,contentPolicy as Y}from"./content-store.js";import{readOnly as Z}from"./read-only.js";import{readingBoundary as q}from"./reading-context.js";import{nativeReaderRoute as ee}from"./native-reader-context.js";import{assertNativeRouteCurrent as ne,invokeNativeRoute as te,materializeNativeRoute as re}from"./native/route.js";import{sha as oe}from"./hash.js";import{wireLinesOf as N}from"../fold/claims.js";import{createHash as b}from"node:crypto";import{mkdtempSync as ie,rmSync as C,statSync as se}from"node:fs";import{tmpdir as ae}from"node:os";import{basename as ce,join as $,resolve as de,relative as M,isAbsolute as fe,sep as le}from"node:path";import{library11_CAPSULE as k}from"../vendor/runtime.js";import{library1_declarationOf as j}from"../vendor/runtime.js";import{unpack as ue}from"./archive.js";import{executeCapsule as pe,validResponse as me}from"./capsule-process.js";import{holdStore as he,replaceWhole as we,storeAt as $e}from"../land/ledger.js";import{coordinatesUnder as ve,observeFile as A,observeText as E}from"../observe/files.js";import{hostOf as ye}from"../observe/run.js";import{ownLock as xe,ownRoot as ge,ownStore as P,processOf as _}from"../observe/runner.js";import{said as Ee}from"../fold/wire.js";const J=new Map,R=new Map,W=l(()=>{for(const e of R.values())C(e,{recursive:!0,force:!0})},"releaseLaid");function U(e,n){const r=R.get(e);if(r!==void 0)return r;const t=ie($(ae(),`${ce(ge())}-capsule-`));try{ue(n,t)}catch(o){throw C(t,{recursive:!0,force:!0}),o}return R.set(e,t),process.listeners("exit").includes(W)||process.once("exit",W),t}l(U,"unpacked");const B=new Map;function Qe(e,n,r){const t=r??V(),{bytes:o}=X(e,t),a=`${t}\0${e}\0${n}`,c=B.get(a);if(c!==void 0)return c;const m=J.get(e)??(y=>({lines:y,declaration:j(y)}))(N(E($(U(e,o),k))));J.set(e,m);const d=Object.keys(m.declaration.regions),i=U(e,o),u=de(i,n),v=M(i,u);if(!n||fe(n)||v===".."||v.startsWith(`..${le}`)||n.split(/[\\/]/).includes(".."))throw new Error(`REFUSE\xB7capsule ${e} declared entry ${n||"(absent)"} unavailable`);const h={digest:e,...m,ask:l((y,S)=>{if(!se(u,{throwIfNoEntry:!1})?.isFile())throw new Error(`REFUSE\xB7capsule ${e} declared entry ${n} unavailable`);return I(e,o,y,d,i,t,u,S)},"ask")};return B.set(a,h),h}l(Qe,"capsuleAt");const z=l(()=>Ee(xe(),"release/layout").map(e=>e.split(":")).filter(e=>e.length===2&&e[0]!==void 0&&e[1]!==void 0),"layoutsOf"),Se=l(e=>{const n=ve(e,e).filter(r=>r.endsWith(".js"));return n.length===1?n[0]:void 0},"oneModule"),D=new Map,H=l((e,n)=>{const r=D.get(e);if(r!==void 0)return r;const t=n.length===0?void 0:z().find(([a,c])=>n.some(m=>E($(e,a,`${m}${c}`))!==void 0)),o=t!==void 0?[_("located","host"),e,t[0],t[1]]:(a=>a===void 0?[]:[_("located","host"),e,a,"render"])(Se(e));return D.set(e,o),o},"spawnOf"),Oe=l((e=process.execArgv)=>e.filter((n,r)=>!/^(-e|-p|--eval|--print)$/.test(e[r-1]??"")&&!/^(-e|-p|--eval|--print|--input-type)(=|$)/.test(n)),"childFlags");function I(e,n,r,t,o,a=P(),c,m){const d=q(),i=ee();if(i){if(!d?.execution||e!==i.profile.selection.archive)throw Error("REFUSE\xB7native reader lacks its complete implementation identity");ne(i)}const u=c===void 0?H(o??U(e,n),t):[c];if(!u.length)return F("error",`LOAD regions \xB7 none of ${t.length} under the directories release/layout names
2
+ `),r.map(()=>{});const v=c!==void 0?`${b("sha256").update(`${M(o,c)}
3
+ `).digest("hex")}
4
+ `:u.length>1?`${b("sha256").update(A(u[0])??new Uint8Array).digest("hex")}
5
+ `:"",h=K(Y().flatMap(s=>{const f=Q(s);return f.kind==="fact"?[f.value.fields]:[]}),m??e),y=h===void 0?"":JSON.stringify(h)+`
6
+ `,S=l(s=>oe(`${e}
7
+ ${v}${y}${d===void 0?"":d.identity+`
8
+ `}${JSON.stringify(s)}`),"hitOf"),g=r.map(S),L=g.map(s=>$e(a,"cas","answers",s)),O=L.map((s,f)=>{const x=E(s);if(x!==void 0)try{const p=JSON.parse(x);return p.format==="capsule-answer/2"&&p.hit===g[f]&&me(p.said,r[f])&&p.said.kind==="fact"?p.said:void 0}catch(p){throw new Error(`${s}: ${p instanceof Error?p.message:String(p)}`)}}),w=r.filter((s,f)=>O[f]===void 0);if(w.length&&Z())throw Error(`REFUSE\xB7preview capsule ${e} has no verified answer for the proposed inputs; fold the declared inputs first`);if(w.length&&F("error",`CAPSULE ${e.slice(7,19)} \xB7 ${r.length} asked \xB7 ${w.length} run \xB7 ${w.map(s=>`${s.rootScope}${s.region}`).join(" ")}
9
+ `),i&&!h)throw Error("REFUSE\xB7native reader missing declared lifetime");const T=w.length?i?te(i,{...re(i),requests:w,execution:d.execution,limits:h}):pe(process.execPath,[...Oe(d?.execution?.flags),ye(),...u],w,d?.execution?{env:d.execution.env,cwd:d.execution.cwd}:{},h):[];return r.map((s,f)=>{if(O[f]!==void 0)return O[f];const x=T[w.indexOf(s)];if(x?.kind==="fact"){const p=JSON.stringify({format:"capsule-answer/2",hit:g[f],said:x});we(L[f],p),he(a,"answers",g[f],Buffer.byteLength(p))}return x})}l(I,"answered");function Ve(e,n,r=P()){const t=$(e,n),o=N(E($(t,k)));if(!o.length)return;const a={lines:o,declaration:j(o)},c=Object.keys(a.declaration.regions);if(!H(t,c).length)return;const m=b("sha256");m.update(A($(t,k))??new Uint8Array);for(const i of c)for(const[u,v]of z()){const h=A($(t,u,`${i}${v}`));if(h!==void 0){m.update(h);break}}const d=`sha256:${m.digest("hex")}`;return{digest:d,...a,ask:l((i,u)=>I(d,new Uint8Array,i,c,t,r,void 0,u),"ask")}}l(Ve,"locatedAt");export{Qe as capsuleAt,Oe as childFlags,Ve as locatedAt};
@@ -1,128 +1,12 @@
1
- import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'node:fs';
2
- import { join, dirname } from 'node:path';
3
- import { performance } from 'node:perf_hooks';
4
- import { fileURLToPath } from 'node:url';
5
- import { canonical, objectHistory, parse, byBytes, wireAt, RECEIPTS } from '@lapxo/topos/wire';
6
- import { cellInputs, cellReceiptContract, cellInputProfile } from '@lapxo/topos/cell-inputs';
7
- import { renderCells } from '@lapxo/topos/cells-view';
8
- import { admittedObjects } from "./objects.js";
9
- import { objectFold } from "../engine/index.js";
10
- import { fullDigest, bytesDigest } from "./digest.js";
11
- import { closeReceipts, noteIdle } from "../fold/closed.js";
12
- import { authorityFor, rootSigner } from "../fold/signers.js";
13
- import { signaturesOf } from "../fold/digests.js";
14
- import { storeOf } from "../land/ledger.js";
15
- import { ownLock } from "../observe/runner.js";
16
- import { toposAt } from "./selected-topos.js";
17
- const fields = (line) => { const p = parse(line); return p.kind === 'fact' ? p.value.fields : {}; };
18
- /** Only Topos input/render entry bytes enter region identity; host locations do not. */
19
- function implementation(algorithm) {
20
- return ['@lapxo/topos/cell-inputs', '@lapxo/topos/cells-view'].map(member => bytesDigest(readFileSync(fileURLToPath(import.meta.resolve(member))), algorithm)).sort(byBytes);
21
- }
22
- /** Native count/bytes receipt lines; carried render blobs are separate verified cache payloads. */
23
- export async function cellsWithReceipts(root, history, emit) {
24
- const started = performance.now(), store = storeOf(root), parsed = objectHistory(history);
25
- if (parsed.kind !== 'fact')
26
- throw Error(`REFUSE·wire ${parsed.why}`);
27
- const authority = authorityFor(history, rootSigner(root), signaturesOf(store).admitted);
28
- for (const record of parsed.value.objects)
29
- if (authority.of(canonical(record.fields)).kind !== 'admitted')
30
- throw Error(`REFUSE·authority receipt input ${record.fields.id}`);
31
- const config = parsed.value.configuration.filter(f => authority.of(canonical(f)).kind === 'admitted');
32
- cellReceiptContract(config);
33
- const profile = cellInputProfile(config);
34
- const instrument = ownLock().map(fields);
35
- const wire = wireAt(instrument, Number.MAX_SAFE_INTEGER);
36
- if (!wire)
37
- throw Error('REFUSE·receipt no admitted digest algorithm');
38
- const algorithm = [...wire.digests][0];
39
- if (!algorithm)
40
- throw Error('REFUSE·receipt no digest algorithm');
41
- const code = implementation(algorithm);
42
- const inputs = cellInputs(parsed.value.objects, profile), semantic = new Map(), inputBytes = new Map();
43
- const pins = new Map();
44
- for (const record of parsed.value.objects.filter(r => r.record === 'cell')) {
45
- const pin = record.fields.topos;
46
- if (!pins.has(pin)) {
47
- const top = toposAt(pin, store);
48
- pins.set(pin, [pin, ...top.dependencies]);
49
- }
50
- const scope = `receipts/cells/${record.fields.scope}`;
51
- const ownInputs = inputs.get(record.fields.scope);
52
- const metadata = [...pins.get(pin), ...code, 'resolution=1'];
53
- const bytes = profile === 'semantic-live@1' ? [...ownInputs, ...metadata].sort(byBytes).join('\n') :
54
- [...ownInputs, ...metadata.sort(byBytes), `receipt-inputs=${profile}`].join('\n');
55
- inputBytes.set(scope, bytes);
56
- semantic.set(scope, fullDigest(bytes, algorithm));
57
- }
58
- // The context stamp is a cache validity guard, not any cell's semantic receipt identity.
59
- const contextStamp = fullDigest([...history, ...code].sort(byBytes), algorithm);
60
- const at = join(root, RECEIPTS), raw = existsSync(at) ? readFileSync(at, 'utf8').split('\n').filter(Boolean) : [], native = raw.filter(l => parse(l).kind === 'fact');
61
- const headers = native.filter(l => fields(l).scope === 'receipts' && fields(l).measure === 'digest');
62
- const carried = (digest) => join(store, 'cas', 'carried', digest.slice(digest.indexOf(':') + 1));
63
- const readVerified = (digest) => {
64
- if (!digest.startsWith(algorithm + ':') || !/^[a-f0-9]+$/.test(digest.split(':')[1] ?? ''))
65
- return;
66
- const file = carried(digest);
67
- if (!existsSync(file))
68
- return;
69
- const bytes = readFileSync(file);
70
- const got = bytesDigest(bytes, algorithm);
71
- if (got !== digest) {
72
- emit(`RECEIPT invalid ${digest} · got ${got} · recompute`);
73
- return;
74
- }
75
- return bytes.toString('utf8');
76
- };
77
- const opened = new Set(await closeReceipts(root, store, semantic));
78
- const bodyLines = native.filter(l => fields(l).scope !== 'receipts').sort(byBytes).join('\n') + '\n';
79
- const intact = raw.length === native.length && headers.length === 1 && readVerified(fields(headers[0]).at.replace(/^place:/, '')) === bodyLines;
80
- if (!intact)
81
- for (const scope of semantic.keys())
82
- opened.add(scope);
83
- const cached = new Map();
84
- for (const [scope, digest] of semantic) {
85
- const bytes = native.filter(l => fields(l).scope === scope && fields(l).measure === 'bytes');
86
- const counts = native.filter(l => fields(l).scope === scope && fields(l).measure === 'count');
87
- if (bytes.length !== 1 || counts.length !== 1 || fields(bytes[0]).value !== digest || fields(counts[0]).value !== '1..1') {
88
- opened.add(scope);
89
- continue;
90
- }
91
- const output = readVerified(fields(counts[0]).at.replace(/^place:/, ''));
92
- if (output === undefined) {
93
- opened.add(scope);
94
- continue;
95
- }
96
- cached.set(scope, output);
97
- }
98
- const idle = headers.length === 1 && fields(headers[0]).value === contextStamp && readVerified(fields(headers[0]).at.replace(/^place:/, '')) !== undefined && opened.size === 0;
99
- let readings;
100
- if (!idle) {
101
- const held = await admittedObjects(root, history, emit);
102
- readings = objectFold(held.records, held.context);
103
- }
104
- const newLines = [], outputs = [];
105
- const save = (text) => { const digest = bytesDigest(Buffer.from(text), algorithm); const path = carried(digest); mkdirSync(dirname(path), { recursive: true }); if (!existsSync(path) || readFileSync(path, 'utf8') !== text)
106
- writeFileSync(path, text); return digest; };
107
- for (const [scope, digest] of semantic) {
108
- const cellName = scope.slice('receipts/cells/'.length);
109
- const output = (!opened.has(scope) && cached.has(scope)) ? cached.get(scope) : renderCells([readings.find(c => c.cell === cellName)]).join('\n') + '\n';
110
- outputs.push(output.trimEnd());
111
- const payload = save(output);
112
- if (save(inputBytes.get(scope)) !== digest)
113
- throw Error('REFUSE·receipt input closure hash mismatch');
114
- newLines.push(canonical({ scope, role: 'writes', form: 'interval', measure: 'count', value: '1..1', at: `place:${payload}`, by: 'bound' }), canonical({ scope, role: 'writes', form: 'alphabet', measure: 'bytes', value: digest, at: `place:${digest}`, by: 'bound' }));
115
- emit(`RECEIPT ${scope}@1 · ${digest} · ${opened.has(scope) ? 'opened' : 'same'}`);
116
- }
117
- const nativeBody = newLines.sort(byBytes).join('\n') + '\n', body = save(nativeBody);
118
- // One native header names carried count/bytes lines; at closes the exact carried bytes.
119
- const next = [canonical({ scope: 'receipts', role: 'writes', form: 'alphabet', measure: 'digest', value: contextStamp, at: `place:${body}`, by: 'bound' }), ...newLines.sort(byBytes)].join('\n') + '\n';
120
- if (!existsSync(at) || readFileSync(at, 'utf8') !== next)
121
- writeFileSync(at, next);
122
- if (idle)
123
- noteIdle();
124
- emit(`RECEIPTS ${semantic.size} read · ${opened.size} opened · ${idle ? 'idle' : 'context checked'} · provider ${idle ? 0 : pins.size}`);
125
- for (const output of outputs)
126
- emit(output);
127
- emit(`COST cells@1 · ${(performance.now() - started).toFixed(3)} ms · admission/context/receipt/render included`);
128
- }
1
+ var ee=Object.defineProperty;var f=(n,i)=>ee(n,"name",{value:i,configurable:!0});import{readFileSync as w,writeFileSync as D,existsSync as R,mkdirSync as te}from"node:fs";import{join as H,dirname as oe}from"node:path";import{performance as W}from"node:perf_hooks";import{fileURLToPath as re}from"node:url";import{library11_canonical as g,library11_objectHistory as se,library11_parse as G,library11_byBytes as d,library11_wireAt as ne,library11_RECEIPTS as ie}from"../vendor/runtime.js";import{library8_cellInputs as ce,library8_cellReceiptContract as ae,library8_cellInputProfile as le}from"../vendor/runtime.js";import{library9_renderCells as pe}from"../vendor/runtime.js";import{admittedObjects as fe}from"./objects.js";import{objectFold as de}from"../engine/index.js";import{fullDigest as V,bytesDigest as k}from"./digest.js";import{closeReceipts as ue,noteIdle as me}from"../fold/closed.js";import{authorityFor as ye,rootSigner as be}from"../fold/signers.js";import{signaturesOf as he}from"../fold/digests.js";import{storeOf as Ee}from"../land/ledger.js";import{ownLock as we}from"../observe/runner.js";import{toposAt as ge}from"./selected-topos.js";const r=f(n=>{const i=G(n);return i.kind==="fact"?i.value.fields:{}},"fields");function ve(n){return["@lapxo/topos/cell-inputs","@lapxo/topos/cells-view"].map(i=>k(w(re(import.meta.resolve(i))),n)).sort(d)}f(ve,"implementation");async function Le(n,i,u){const X=W.now(),v=Ee(n),m=se(i);if(m.kind!=="fact")throw Error(`REFUSE\xB7wire ${m.why}`);const x=ye(i,be(n),he(v).admitted);for(const e of m.value.objects)if(x.of(g(e.fields)).kind!=="admitted")throw Error(`REFUSE\xB7authority receipt input ${e.fields.id}`);const P=m.value.configuration.filter(e=>x.of(g(e)).kind==="admitted");ae(P);const j=le(P),q=we().map(r),T=ne(q,Number.MAX_SAFE_INTEGER);if(!T)throw Error("REFUSE\xB7receipt no admitted digest algorithm");const a=[...T.digests][0];if(!a)throw Error("REFUSE\xB7receipt no digest algorithm");const B=ve(a),J=ce(m.value.objects,j),y=new Map,U=new Map,S=new Map;for(const e of m.value.objects.filter(t=>t.record==="cell")){const t=e.fields.topos;if(!S.has(t)){const Z=ge(t,v);S.set(t,[t,...Z.dependencies])}const o=`receipts/cells/${e.fields.scope}`,s=J.get(e.fields.scope),p=[...S.get(t),...B,"resolution=1"],c=j==="semantic-live@1"?[...s,...p].sort(d).join(`
2
+ `):[...s,...p.sort(d),`receipt-inputs=${j}`].join(`
3
+ `);U.set(o,c),y.set(o,V(c,a))}const A=V([...i,...B].sort(d),a),b=H(n,ie),M=R(b)?w(b,"utf8").split(`
4
+ `).filter(Boolean):[],h=M.filter(e=>G(e).kind==="fact"),E=h.filter(e=>r(e).scope==="receipts"&&r(e).measure==="digest"),O=f(e=>H(v,"cas","carried",e.slice(e.indexOf(":")+1)),"carried"),I=f(e=>{if(!e.startsWith(a+":")||!/^[a-f0-9]+$/.test(e.split(":")[1]??""))return;const t=O(e);if(!R(t))return;const o=w(t),s=k(o,a);if(s!==e){u(`RECEIPT invalid ${e} \xB7 got ${s} \xB7 recompute`);return}return o.toString("utf8")},"readVerified"),l=new Set(await ue(n,v,y)),K=h.filter(e=>r(e).scope!=="receipts").sort(d).join(`
5
+ `)+`
6
+ `;if(!(M.length===h.length&&E.length===1&&I(r(E[0]).at.replace(/^place:/,""))===K))for(const e of y.keys())l.add(e);const _=new Map;for(const[e,t]of y){const o=h.filter(c=>r(c).scope===e&&r(c).measure==="bytes"),s=h.filter(c=>r(c).scope===e&&r(c).measure==="count");if(o.length!==1||s.length!==1||r(o[0]).value!==t||r(s[0]).value!=="1..1"){l.add(e);continue}const p=I(r(s[0]).at.replace(/^place:/,""));if(p===void 0){l.add(e);continue}_.set(e,p)}const $=E.length===1&&r(E[0]).value===A&&I(r(E[0]).at.replace(/^place:/,""))!==void 0&&l.size===0;let z;if(!$){const e=await fe(n,i,u);z=de(e.records,e.context)}const F=[],L=[],C=f(e=>{const t=k(Buffer.from(e),a),o=O(t);return te(oe(o),{recursive:!0}),(!R(o)||w(o,"utf8")!==e)&&D(o,e),t},"save");for(const[e,t]of y){const o=e.slice(15),s=!l.has(e)&&_.has(e)?_.get(e):pe([z.find(c=>c.cell===o)]).join(`
7
+ `)+`
8
+ `;L.push(s.trimEnd());const p=C(s);if(C(U.get(e))!==t)throw Error("REFUSE\xB7receipt input closure hash mismatch");F.push(g({scope:e,role:"writes",form:"interval",measure:"count",value:"1..1",at:`place:${p}`,by:"bound"}),g({scope:e,role:"writes",form:"alphabet",measure:"bytes",value:t,at:`place:${t}`,by:"bound"})),u(`RECEIPT ${e}@1 \xB7 ${t} \xB7 ${l.has(e)?"opened":"same"}`)}const Q=F.sort(d).join(`
9
+ `)+`
10
+ `,Y=C(Q),N=[g({scope:"receipts",role:"writes",form:"alphabet",measure:"digest",value:A,at:`place:${Y}`,by:"bound"}),...F.sort(d)].join(`
11
+ `)+`
12
+ `;(!R(b)||w(b,"utf8")!==N)&&D(b,N),$&&me(),u(`RECEIPTS ${y.size} read \xB7 ${l.size} opened \xB7 ${$?"idle":"context checked"} \xB7 provider ${$?0:S.size}`);for(const e of L)u(e);u(`COST cells@1 \xB7 ${(W.now()-X).toFixed(3)} ms \xB7 admission/context/receipt/render included`)}f(Le,"cellsWithReceipts");export{Le as cellsWithReceipts};
@@ -0,0 +1 @@
1
+ var e=Object.defineProperty;var r=(n,t)=>e(n,"name",{value:t,configurable:!0});import{BoundRefusal as i}from"./refusals.js";class c extends i{static{r(this,"ActCommitUnknown")}constructor(t,o){super("host/commit-unknown",`REFUSE\xB7act publication uncertain \xB7 ${o instanceof Error?o.message:"storage failure"}`),this.identity=t}}export{c as ActCommitUnknown};
@@ -1,8 +1 @@
1
- /** The synchronous host names unavailable content; the enclosing fold resolves exactly that request. */
2
- export class ContentNeeded extends Error {
3
- constructor(digest, why = `REFUSE·pin ${digest} content unavailable · not laid`) {
4
- super(why);
5
- this.digest = digest;
6
- this.name = 'ContentNeeded';
7
- }
8
- }
1
+ var r=Object.defineProperty;var n=(t,e)=>r(t,"name",{value:e,configurable:!0});class a extends Error{static{n(this,"ContentNeeded")}constructor(e,o=`REFUSE\xB7pin ${e} content unavailable \xB7 not laid`){super(o),this.digest=e,this.name="ContentNeeded"}}export{a as ContentNeeded};
@@ -1,31 +1 @@
1
- import { AsyncLocalStorage } from 'node:async_hooks';
2
- import { createHash } from 'node:crypto';
3
- import { blobAt } from "../land/ledger.js";
4
- import { observeFile } from "../observe/files.js";
5
- import { ownLock, ownStore } from "../observe/runner.js";
6
- import { parse, wireAt } from '@lapxo/topos/wire';
7
- import { ContentNeeded } from "./content-needed.js";
8
- // Each invocation carries its place explicitly; asynchronous requests cannot share a mutable current root.
9
- const requested = new AsyncLocalStorage();
10
- export const withContentStore = (store, run, policy = () => []) => requested.run({ store, policy }, run);
11
- export const contentPolicy = () => requested.getStore()?.policy() ?? [];
12
- export const contentStore = () => requested.getStore()?.store ?? ownStore();
13
- /** Only immutable verified bytes may be shared with the instrument. Local corruption is never hidden by fallback. */
14
- export function verifiedContent(digest, store = contentStore(), instrument = ownStore()) {
15
- const fields = ownLock().flatMap(line => { const p = parse(line); return p.kind === 'fact' ? [p.value.fields] : []; });
16
- const wire = wireAt(fields, Number.MAX_SAFE_INTEGER);
17
- if (!wire)
18
- throw Error('REFUSE·pin instrument has no admitted wire');
19
- const split = digest.indexOf(':'), algorithm = digest.slice(0, split), hex = digest.slice(split + 1);
20
- if (split < 1 || !wire.digests.has(algorithm) || !/^[a-f0-9]+$/.test(hex))
21
- throw Error(`REFUSE·pin ${digest} digest is not admitted`);
22
- for (const at of new Set([store, instrument])) {
23
- const path = blobAt(at, digest), bytes = observeFile(path);
24
- if (bytes === undefined)
25
- continue;
26
- if (createHash(algorithm).update(bytes).digest('hex') !== hex)
27
- throw Error(`REFUSE·pin ${digest} content hash mismatch · store ${at} · not laid`);
28
- return { bytes, path };
29
- }
30
- throw new ContentNeeded(digest);
31
- }
1
+ var w=Object.defineProperty;var e=(t,o)=>w(t,"name",{value:o,configurable:!0});import{AsyncLocalStorage as d}from"node:async_hooks";import{createHash as E}from"node:crypto";import{blobAt as u}from"../land/ledger.js";import{observeFile as S}from"../observe/files.js";import{ownLock as b,ownStore as l}from"../observe/runner.js";import{library11_parse as x,library11_wireAt as y}from"../vendor/runtime.js";import{ContentNeeded as A}from"./content-needed.js";const c=new d,L=e((t,o,i=()=>[])=>c.run({store:t,policy:i},o),"withContentStore"),M=e(()=>c.getStore()?.policy()??[],"contentPolicy"),F=e(()=>c.getStore()?.store??l(),"contentStore");function g(t,o=F(),i=l()){const h=b().flatMap(n=>{const r=x(n);return r.kind==="fact"?[r.value.fields]:[]}),f=y(h,Number.MAX_SAFE_INTEGER);if(!f)throw Error("REFUSE\xB7pin instrument has no admitted wire");const s=t.indexOf(":"),p=t.slice(0,s),m=t.slice(s+1);if(s<1||!f.digests.has(p)||!/^[a-f0-9]+$/.test(m))throw Error(`REFUSE\xB7pin ${t} digest is not admitted`);for(const n of new Set([o,i])){const r=u(n,t),a=S(r);if(a!==void 0){if(E(p).update(a).digest("hex")!==m)throw Error(`REFUSE\xB7pin ${t} content hash mismatch \xB7 store ${n} \xB7 not laid`);return{bytes:a,path:r}}}throw new A(t)}e(g,"verifiedContent");export{M as contentPolicy,F as contentStore,g as verifiedContent,L as withContentStore};