@lapxo/bound 0.2.4 → 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 (250) hide show
  1. package/CITATION.cff +1 -1
  2. package/TARGET.bound +44 -39
  3. package/dist/cli/accept.js +72 -590
  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 -282
  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 -19
  18. package/dist/cli/takes.js +10 -112
  19. package/dist/cli/verb.d.ts +1 -2
  20. package/dist/cli/verb.js +83 -684
  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 -117
  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 -132
  32. package/dist/fold/closed.js +24 -235
  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 -35
  70. package/dist/host/archive.js +1 -152
  71. package/dist/host/call-context.js +1 -14
  72. package/dist/host/capsule-process.js +2 -62
  73. package/dist/host/capsule.js +9 -171
  74. package/dist/host/cell-receipts.js +12 -128
  75. package/dist/host/commit-result.js +1 -5
  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 -72
  79. package/dist/host/digest.js +2 -15
  80. package/dist/host/hash.js +1 -9
  81. package/dist/host/host-controls.js +1 -19
  82. package/dist/host/in-process-act.js +3 -84
  83. package/dist/host/in-process-context.js +3 -91
  84. package/dist/host/in-process-root.js +6 -84
  85. package/dist/host/in-process.js +4 -55
  86. package/dist/host/index.d.ts +1 -15
  87. package/dist/host/index.js +1 -7
  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 -25
  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 -78
  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 -183
  128. package/dist/host/ports/place-act.js +1 -134
  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 -307
  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 -12
  157. package/dist/host/signing.js +2 -48
  158. package/dist/host/source-content.js +2 -111
  159. package/dist/host/standing-carrier.js +1 -18
  160. package/dist/host/standing-projection.js +1 -50
  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 -336
  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 -41
  182. package/dist/observe/runner.js +4 -101
  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-publication.inventory.json +2 -1
  212. package/docs/contracts/optional-wasm-adapter.publication.json +1 -1
  213. package/docs/contracts/reading-identity.md +37 -0
  214. package/docs/contracts/reading-identity.publication.json +25 -0
  215. package/docs/contracts/refusals.bound +51 -0
  216. package/docs/contracts/refusals.md +18 -3
  217. package/docs/contracts/refusals.publication.json +2 -2
  218. package/docs/contracts/views.md +8 -0
  219. package/docs/reference.md +1 -0
  220. package/package.json +23 -34
  221. package/receipts.bound +11627 -10323
  222. package/samples/reading-identity/no.json +20 -0
  223. package/samples/reading-identity/yes.json +46 -0
  224. package/vectors/native/reading-identity.vector.json +101 -0
  225. package/vectors/native/refusals.vector.json +104 -0
  226. package/vendor/syntax/LICENSE.md +7 -0
  227. package/vendor/syntax/parser.js +1 -0
  228. package/.github/workflows/release.yml +0 -67
  229. package/.github/workflows/test.yml +0 -35
  230. package/dist/engine/object.d.ts +0 -17
  231. package/dist/engine/ports.d.ts +0 -37
  232. package/dist/fold/claims.d.ts +0 -29
  233. package/dist/host/call-context.d.ts +0 -20
  234. package/dist/host/commit-result.d.ts +0 -6
  235. package/dist/host/in-process-act.d.ts +0 -82
  236. package/dist/host/in-process-context.d.ts +0 -46
  237. package/dist/host/in-process-root.d.ts +0 -117
  238. package/dist/host/in-process.d.ts +0 -41
  239. package/dist/host/native/module-closure.d.ts +0 -35
  240. package/dist/host/native-receiver.d.ts +0 -19
  241. package/dist/host/place-act-transaction.d.ts +0 -24
  242. package/dist/host/place-clock-selection.d.ts +0 -55
  243. package/dist/host/ports/local-act.d.ts +0 -44
  244. package/dist/host/ports/place-act.d.ts +0 -49
  245. package/dist/host/ports/publication.d.ts +0 -10
  246. package/dist/host/reference-export.d.ts +0 -33
  247. package/dist/host/reference-native.d.ts +0 -30
  248. package/dist/host/reference-profile.d.ts +0 -23
  249. package/dist/host/refusals.d.ts +0 -14
  250. package/dist/host/root-clock-source.d.ts +0 -86
@@ -1,79 +1 @@
1
- import { spawn } from 'node:child_process';
2
- const deadline = BigInt(process.argv[2] ?? '0'), responseBytes = Number(process.argv[3]);
3
- if (deadline <= 0n || !Number.isSafeInteger(responseBytes) || responseBytes <= 0)
4
- throw Error('REFUSE·reader invalid process limits');
5
- let child, finished = false, failure, size = 0;
6
- const stdout = [], stderr = [];
7
- const grouped = process.platform !== 'win32';
8
- const cancel = () => { if (child?.pid === undefined)
9
- return; try {
10
- if (grouped)
11
- process.kill(-child.pid, 'SIGKILL');
12
- else
13
- child.kill('SIGKILL');
14
- }
15
- catch { } };
16
- const finish = (status, signal) => {
17
- if (finished)
18
- return;
19
- finished = true;
20
- clearTimeout(timer);
21
- cancel();
22
- child?.stdin?.destroy();
23
- child?.stdout?.destroy();
24
- child?.stderr?.destroy();
25
- process.stdin.destroy();
26
- const bytes = failure ? Buffer.alloc(0) : Buffer.concat(stdout);
27
- const result = { stdout: bytes.toString('utf8'), stdoutBytes: bytes.toString('base64'), stderr: Buffer.concat(stderr).toString('utf8'), status, signal, ...(failure ? { failure } : {}) };
28
- process.stdout.write(JSON.stringify(result));
29
- };
30
- const refuse = (why) => { if (failure && why === 'response exceeds declared byte bound' && !failure.includes(why))
31
- failure += ' · ' + why;
32
- else
33
- failure ?? (failure = why); finish(null, 'SIGKILL'); };
34
- // A closed input pipe can precede the queued stderr/close events of an early
35
- // rejecting child. Cancel immediately, but drain only until the SAME deadline.
36
- const inputFailed = (why) => { if (finished)
37
- return; failure ?? (failure = why); stdout.length = 0; cancel(); child?.stdin?.destroy(); process.stdin.destroy(); if (process.hrtime.bigint() >= deadline)
38
- finish(null, 'SIGKILL'); };
39
- const remaining = Number(deadline - process.hrtime.bigint()) / 1e6;
40
- const timer = setTimeout(() => refuse('declared timeout exhausted'), Math.max(0, remaining));
41
- process.once('SIGTERM', () => refuse('interrupted'));
42
- try {
43
- if (remaining <= 0)
44
- refuse('declared timeout exhausted');
45
- const chunks = [];
46
- for await (const chunk of process.stdin) {
47
- if (finished)
48
- break;
49
- chunks.push(Buffer.from(chunk));
50
- }
51
- if (!finished) {
52
- const request = JSON.parse(Buffer.concat(chunks).toString('utf8'));
53
- if (request.limits.responseBytes !== responseBytes || !Number.isSafeInteger(request.limits.timeoutMs) || request.limits.timeoutMs <= 0)
54
- throw Error('invalid process limits');
55
- // Parsing is synchronous; recheck before spawning even if the timer has not
56
- // had a turn to run. An expired handoff must never start the command.
57
- if (process.hrtime.bigint() >= deadline) {
58
- refuse('declared timeout exhausted');
59
- }
60
- else {
61
- child = spawn(request.command, [...request.args], { cwd: request.cwd, env: request.env, detached: grouped, stdio: ['pipe', 'pipe', 'pipe'] });
62
- const receive = (items, bytes) => { if (finished)
63
- return; size += bytes.length; if (size > responseBytes)
64
- refuse('response exceeds declared byte bound');
65
- else if (!failure || items === stderr)
66
- items.push(bytes); };
67
- child.stdout.on('data', (b) => receive(stdout, b));
68
- child.stderr.on('data', (b) => receive(stderr, b));
69
- child.stdin.on('error', error => inputFailed('input failed: ' + error.message));
70
- child.once('error', error => refuse(error.message));
71
- child.once('close', finish);
72
- child.stdin.end(request.input);
73
- }
74
- }
75
- }
76
- catch {
77
- if (!finished)
78
- refuse('input unavailable or malformed');
79
- }
1
+ var I=Object.defineProperty;var d=(s,e)=>I(s,"name",{value:e,configurable:!0});import{spawn as y}from"node:child_process";const u=BigInt(process.argv[2]??"0"),a=Number(process.argv[3]);if(u<=0n||!Number.isSafeInteger(a)||a<=0)throw Error("REFUSE\xB7reader invalid process limits");let t,o=!1,i,g=0;const f=[],l=[],h=process.platform!=="win32",b=d(()=>{if(t?.pid!==void 0)try{h?process.kill(-t.pid,"SIGKILL"):t.kill("SIGKILL")}catch{}},"cancel"),p=d((s,e)=>{if(o)return;o=!0,clearTimeout(v),b(),t?.stdin?.destroy(),t?.stdout?.destroy(),t?.stderr?.destroy(),process.stdin.destroy();const c=i?Buffer.alloc(0):Buffer.concat(f),r={stdout:c.toString("utf8"),stdoutBytes:c.toString("base64"),stderr:Buffer.concat(l).toString("utf8"),status:s,signal:e,...i?{failure:i}:{}};process.stdout.write(JSON.stringify(r))},"finish"),n=d(s=>{i&&s==="response exceeds declared byte bound"&&!i.includes(s)?i+=" \xB7 "+s:i??(i=s),p(null,"SIGKILL")},"refuse"),B=d(s=>{o||(i??(i=s),f.length=0,b(),t?.stdin?.destroy(),process.stdin.destroy(),process.hrtime.bigint()>=u&&p(null,"SIGKILL"))},"inputFailed"),S=Number(u-process.hrtime.bigint())/1e6,v=setTimeout(()=>n("declared timeout exhausted"),Math.max(0,S));process.once("SIGTERM",()=>n("interrupted"));try{S<=0&&n("declared timeout exhausted");const s=[];for await(const e of process.stdin){if(o)break;s.push(Buffer.from(e))}if(!o){const e=JSON.parse(Buffer.concat(s).toString("utf8"));if(e.limits.responseBytes!==a||!Number.isSafeInteger(e.limits.timeoutMs)||e.limits.timeoutMs<=0)throw Error("invalid process limits");if(process.hrtime.bigint()>=u)n("declared timeout exhausted");else{t=y(e.command,[...e.args],{cwd:e.cwd,env:e.env,detached:h,stdio:["pipe","pipe","pipe"]});const c=d((r,m)=>{o||(g+=m.length,g>a?n("response exceeds declared byte bound"):(!i||r===l)&&r.push(m))},"receive");t.stdout.on("data",r=>c(f,r)),t.stderr.on("data",r=>c(l,r)),t.stdin.on("error",r=>B("input failed: "+r.message)),t.once("error",r=>n(r.message)),t.once("close",p),t.stdin.end(e.input)}}}catch{o||n("input unavailable or malformed")}
@@ -1,105 +1,6 @@
1
- var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExtension) || function (path, preserveJsx) {
2
- if (typeof path === "string" && /^\.\.?\//.test(path)) {
3
- return path.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function (m, tsx, d, ext, cm) {
4
- return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : (d + ext + "." + cm.toLowerCase() + "js");
5
- });
6
- }
7
- return path;
8
- };
9
- import { readFileSync } from 'node:fs';
10
- import { join } from 'node:path';
11
- import { pathToFileURL } from 'node:url';
12
- import { pinExport, provide } from "./provide.js";
13
- const wanted = (role) => role === 'reader' ? ['observe', 'run'] : [role];
14
- const foreign = (role) => ['render', 'receipt', 'observe', 'run'].filter((name) => !wanted(role).includes(name));
15
- const isFn = (module, name) => typeof module[name] === 'function';
16
- const text = (error) => (error instanceof Error ? error.message : String(error)).replace(/\s+/g, ' ').trim();
17
- /**
18
- * Wire and capsule of the world, from `dep/topos` (or the lock's `uses/topos` pin). The host never spells where that
19
- * package lies.
20
- */
21
- const sdk = async () => {
22
- provide();
23
- const [wireAt, capsuleAt] = [pinExport('topos', './wire'), pinExport('topos', './capsule')];
24
- if (!wireAt || !capsuleAt)
25
- throw new Error('REFUSE·pin dep/topos names no wire or capsule');
26
- const [wire, capsule] = await Promise.all([import(__rewriteRelativeImportExtension(pathToFileURL(wireAt).href)), import(__rewriteRelativeImportExtension(pathToFileURL(capsuleAt).href))]);
27
- return {
28
- CAPSULE: String(wire['CAPSULE'] ?? ''),
29
- PROTOCOL: String(wire['PROTOCOL'] ?? ''),
30
- parse: wire['parse'],
31
- declarationOf: capsule['declarationOf'],
32
- readers: capsule['readers'],
33
- receiptShell: capsule['receiptShell'],
34
- shell: capsule['shell'],
35
- };
36
- };
37
- /**
38
- * The host loads a world from its lock: the lock read once, each region imported from its one file by name and role into
39
- * the shell of its kind. A region that fails is one line on stderr and is left out; the regions that loaded still form
40
- * the shell. A world with no region of a kind has no shell of it. `load` is the host's child entry, the same load with
41
- * its places read from the arguments the host spawned it with.
42
- */
43
- /** A capsule that ships one render module: the host calls it per region and hands it the lines. The module does not read. */
44
- const pagesOf = async (root, file) => {
45
- const { CAPSULE, PROTOCOL, declarationOf, shell } = await sdk();
46
- const lines = readFileSync(join(root, CAPSULE), 'utf8').split('\n').filter((line) => line.startsWith(PROTOCOL));
47
- const { regions } = declarationOf(lines);
48
- const module = await import(__rewriteRelativeImportExtension(pathToFileURL(join(root, file)).href));
49
- const render = module['render'] ?? module['default']?.['render'];
50
- if (typeof render !== 'function')
51
- return {};
52
- const held = Object.fromEntries(Object.entries(regions).map(([name, reads]) => [name, {
53
- reads,
54
- region: (asked) => render({ ...asked, region: name }),
55
- }]));
56
- return held && Object.keys(held).length ? { render: shell(held) } : {};
57
- };
58
- export const locate = async (root, dir, ext) => {
59
- if (ext === 'render')
60
- return pagesOf(root, dir);
61
- const { CAPSULE, PROTOCOL, parse, declarationOf, readers, receiptShell, shell } = await sdk();
62
- const lines = readFileSync(join(root, CAPSULE), 'utf8').split('\n').filter((line) => line.startsWith(PROTOCOL));
63
- const field = (line, name) => ((got) => (got.kind === 'fact' ? got.value.fields[name] ?? '' : ''))(parse(line));
64
- const { regions, writes } = declarationOf(lines);
65
- const reader = new Set(lines.filter((line) => field(line, 'measure') === 'writes').map((line) => field(line, 'scope').slice('region/'.length)));
66
- const kind = (name) => (reader.has(name) ? 'reader' : writes[name] ?? '');
67
- const region = async (name, reads, role) => {
68
- let module;
69
- try {
70
- module = await import(__rewriteRelativeImportExtension(pathToFileURL(join(root, dir, `${name}${ext}`)).href));
71
- }
72
- catch (error) {
73
- process.stderr.write(`LOAD ${name} · module error · ${text(error)}\n`);
74
- return undefined;
75
- }
76
- const has = wanted(role).filter((key) => isFn(module, key));
77
- const exported = foreign(role).filter((key) => isFn(module, key));
78
- if (!has.length && exported.length) {
79
- process.stderr.write(`LOAD ${name} · role mismatch · ${role} exports ${exported.join('|')}\n`);
80
- return undefined;
81
- }
82
- if (!has.length) {
83
- process.stderr.write(`LOAD ${name} · missing export · ${wanted(role)[0]}\n`);
84
- return undefined;
85
- }
86
- const picked = role === 'reader' ? { observe: module['observe'], run: module['run'] } : { region: module[role] };
87
- return [name, { reads, ...picked }];
88
- };
89
- const loaded = async (role) => {
90
- const held = [];
91
- for (const [name, reads] of Object.entries(regions)) {
92
- if (kind(name) !== role)
93
- continue;
94
- const row = await region(name, reads, role);
95
- if (row)
96
- held.push(row);
97
- }
98
- return held.length ? Object.fromEntries(held) : undefined;
99
- };
100
- const render = await loaded('render');
101
- const receipt = await loaded('receipt');
102
- const reading = await loaded('reader');
103
- return { render: render && shell(render), receipt: receipt && receiptShell(receipt), ...(reading ? readers(reading) : {}) };
104
- };
105
- export const load = () => locate(process.argv[3] ?? '', process.argv[4] ?? '', process.argv[5] ?? '');
1
+ var I=Object.defineProperty;var n=(e,t)=>I(e,"name",{value:t,configurable:!0});var m=function(e,t){return typeof e=="string"&&/^\.\.?\//.test(e)?e.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i,function(i,o,d,p,f){return o?t?".jsx":".js":d&&(!p||!f)?i:d+p+"."+f.toLowerCase()+"js"}):e};import{readFileSync as P}from"node:fs";import{join as E}from"node:path";import{pathToFileURL as L}from"node:url";import{pinExport as R,provide as W}from"./provide.js";const j=n(e=>e==="reader"?["observe","run"]:[e],"wanted"),q=n(e=>["render","receipt","observe","run"].filter(t=>!j(e).includes(t)),"foreign"),U=n((e,t)=>typeof e[t]=="function","isFn"),z=n(e=>(e instanceof Error?e.message:String(e)).replace(/\s+/g," ").trim(),"text"),k=n(async()=>{W();const[e,t]=[R("topos","./wire"),R("topos","./capsule")];if(!e||!t)throw new Error("REFUSE\xB7pin dep/topos names no wire or capsule");const[i,o]=await Promise.all([import(m(L(e).href)),import(m(L(t).href))]);return{CAPSULE:String(i.CAPSULE??""),PROTOCOL:String(i.PROTOCOL??""),parse:i.parse,declarationOf:o.declarationOf,readers:o.readers,receiptShell:o.receiptShell,shell:o.shell}},"sdk"),B=n(async(e,t)=>{const{CAPSULE:i,PROTOCOL:o,declarationOf:d,shell:p}=await k(),f=P(E(e,i),"utf8").split(`
2
+ `).filter(a=>a.startsWith(o)),{regions:S}=d(f),g=await import(m(L(E(e,t)).href)),O=g.render??g.default?.render;if(typeof O!="function")return{};const u=Object.fromEntries(Object.entries(S).map(([a,v])=>[a,{reads:v,region:n(y=>O({...y,region:a}),"region")}]));return u&&Object.keys(u).length?{render:p(u)}:{}},"pagesOf"),G=n(async(e,t,i)=>{if(i==="render")return B(e,t);const{CAPSULE:o,PROTOCOL:d,parse:p,declarationOf:f,readers:S,receiptShell:g,shell:O}=await k(),u=P(E(e,o),"utf8").split(`
3
+ `).filter(r=>r.startsWith(d)),a=n((r,l)=>(s=>s.kind==="fact"?s.value.fields[l]??"":"")(p(r)),"field"),{regions:v,writes:y}=f(u),T=new Set(u.filter(r=>a(r,"measure")==="writes").map(r=>a(r,"scope").slice(7))),F=n(r=>T.has(r)?"reader":y[r]??"","kind"),_=n(async(r,l,s)=>{let c;try{c=await import(m(L(E(e,t,`${r}${i}`)).href))}catch(w){process.stderr.write(`LOAD ${r} \xB7 module error \xB7 ${z(w)}
4
+ `);return}const h=j(s).filter(w=>U(c,w)),C=q(s).filter(w=>U(c,w));if(!h.length&&C.length){process.stderr.write(`LOAD ${r} \xB7 role mismatch \xB7 ${s} exports ${C.join("|")}
5
+ `);return}if(!h.length){process.stderr.write(`LOAD ${r} \xB7 missing export \xB7 ${j(s)[0]}
6
+ `);return}const D=s==="reader"?{observe:c.observe,run:c.run}:{region:c[s]};return[r,{reads:l,...D}]},"region"),$=n(async r=>{const l=[];for(const[s,c]of Object.entries(v)){if(F(s)!==r)continue;const h=await _(s,c,r);h&&l.push(h)}return l.length?Object.fromEntries(l):void 0},"loaded"),b=await $("render"),x=await $("receipt"),A=await $("reader");return{render:b&&O(b),receipt:x&&g(x),...A?S(A):{}}},"locate"),Q=n(()=>G(process.argv[3]??"",process.argv[4]??"",process.argv[5]??""),"load");export{Q as load,G as locate};
@@ -1,32 +1 @@
1
- import fs from 'node:fs';
2
- /** The opened descriptor is bounded before allocation/read. Growth after fstat
3
- * is detected with one sentinel byte; no oversized buffer reaches decode/hash.
4
- */
5
- export function readBoundedFile(path, remaining, coordinate) {
6
- if (!Number.isSafeInteger(remaining) || remaining < 0)
7
- throw Error('REFUSE·native file missing byte budget ' + coordinate);
8
- const fd = fs.openSync(path, 'r');
9
- try {
10
- const stat = fs.fstatSync(fd);
11
- if (!stat.isFile() || !Number.isSafeInteger(stat.size) || stat.size > remaining)
12
- throw Error('REFUSE·native file byte budget before read ' + coordinate);
13
- const bytes = Buffer.alloc(stat.size);
14
- let offset = 0;
15
- while (offset < bytes.length) {
16
- const count = fs.readSync(fd, bytes, offset, bytes.length - offset, null);
17
- if (!count)
18
- throw Error('REFUSE·native file changed during read ' + coordinate);
19
- offset += count;
20
- }
21
- if (fs.readSync(fd, Buffer.alloc(1), 0, 1, null) !== 0)
22
- throw Error('REFUSE·native file grew during read ' + coordinate);
23
- return bytes;
24
- }
25
- finally {
26
- fs.closeSync(fd);
27
- }
28
- }
29
- export function readBoundedBatch(paths, budget) {
30
- let bytes = 0;
31
- return paths.map(file => { const data = readBoundedFile(file.path, budget - bytes, file.coordinate); bytes += data.length; return data; });
32
- }
1
+ var u=Object.defineProperty;var a=(o,e)=>u(o,"name",{value:e,configurable:!0});import f from"node:fs";function c(o,e,t){if(!Number.isSafeInteger(e)||e<0)throw Error("REFUSE\xB7native file missing byte budget "+t);const r=f.openSync(o,"r");try{const n=f.fstatSync(r);if(!n.isFile()||!Number.isSafeInteger(n.size)||n.size>e)throw Error("REFUSE\xB7native file byte budget before read "+t);const i=Buffer.alloc(n.size);let l=0;for(;l<i.length;){const s=f.readSync(r,i,l,i.length-l,null);if(!s)throw Error("REFUSE\xB7native file changed during read "+t);l+=s}if(f.readSync(r,Buffer.alloc(1),0,1,null)!==0)throw Error("REFUSE\xB7native file grew during read "+t);return i}finally{f.closeSync(r)}}a(c,"readBoundedFile");function h(o,e){let t=0;return o.map(r=>{const n=c(r.path,e-t,r.coordinate);return t+=n.length,n})}a(h,"readBoundedBatch");export{h as readBoundedBatch,c as readBoundedFile};
@@ -1,91 +1 @@
1
- import { spawn } from 'node:child_process';
2
- import { channel } from 'node:diagnostics_channel';
3
- import { isAbsolute } from 'node:path';
4
- import { checkCommandPort, commandRequest, commandResponse } from '@lapxo/topos/contract';
5
- import { requireEffects } from "../read-only.js";
6
- import { assertNativeRuntime } from "./runtime.js";
7
- import { scheduleDeadline } from "./deadline.js";
8
- const rejectedCommand = channel('bound.command.refusal');
9
- /** macOS prototype: bounded async invocation with POSIX child-group cancellation.
10
- * No acquisition or persistence on prospective paths; no partial success frames.
11
- * Host callbacks are trusted admission/verification operations, never signatures
12
- * or authority assertions supplied by the command's own output. */
13
- export async function executeLot(options) {
14
- const start = process.hrtime.bigint();
15
- const { host, signal, phase, accepts } = options;
16
- // Admitted semantic configuration is snapshotted for the whole invocation;
17
- // mutations of a caller's delivery object cannot alter checks mid-flight.
18
- const port = Object.freeze({ ...options.port, closure: Object.freeze([...options.port.closure]), environment: Object.freeze([...options.port.environment]) });
19
- const lot = Object.freeze({ ...options.lot, requests: Object.freeze([...options.lot.requests]) });
20
- const environment = Object.freeze({ ...options.environment });
21
- assertNativeRuntime(environment, []);
22
- const refuse = (why) => { throw Error(`REFUSE·port ${port.coordinate} ${why}`); };
23
- if (phase !== 'act')
24
- refuse('invocation forbidden during ' + phase);
25
- requireEffects('command ' + port.coordinate);
26
- if (signal?.aborted)
27
- refuse('interrupted before invocation');
28
- checkCommandPort(port, host);
29
- const deadline = start + BigInt(port.timeoutMs) * 1000000n;
30
- const resolved = host.resolve(port), invocation = Object.freeze({ ...resolved, args: Object.freeze([...resolved.args]) });
31
- if (!isAbsolute(invocation.command) || !isAbsolute(invocation.cwd))
32
- refuse('host invocation needs absolute coordinates');
33
- if (host.loaderClosure(port, invocation) !== 'verified')
34
- refuse('runtime-loader closure unmeasured');
35
- const names = Object.keys(environment).sort(), declared = [...new Set(port.environment)].sort();
36
- if (names.length !== declared.length || names.some((name, index) => name !== declared[index]) || names.some(name => typeof environment[name] !== 'string' || environment[name].includes('\0')))
37
- refuse('environment differs from declared handoff');
38
- const frame = commandRequest(port, lot);
39
- const envelope = invocation.hostEnvelope;
40
- if (envelope && (!Number.isSafeInteger(invocation.envelopeBytes) || invocation.envelopeBytes < 1 || envelope.byteLength > invocation.envelopeBytes))
41
- refuse('host envelope byte resources');
42
- if (process.hrtime.bigint() >= deadline)
43
- refuse('timeout before invocation');
44
- if (signal?.aborted)
45
- refuse('interrupted before invocation');
46
- const result = await new Promise((resolve, reject) => {
47
- const child = spawn(invocation.command, [...invocation.args], { cwd: invocation.cwd, env: { ...environment }, shell: false, detached: false, stdio: envelope ? ['pipe', 'pipe', 'pipe', 'pipe'] : ['pipe', 'pipe', 'pipe'] });
48
- const stdout = [], stderr = [];
49
- let bytes = 0, why, done = false;
50
- const stop = (cause) => { if (why)
51
- return; why = cause; try {
52
- child.kill('SIGKILL');
53
- }
54
- catch { } child.stdin.destroy(); if (envelope)
55
- child.stdio[3]?.destroy(); };
56
- const cancelTimer = scheduleDeadline(deadline, () => stop('timeout'));
57
- const abort = () => stop('interrupted');
58
- signal?.addEventListener('abort', abort, { once: true });
59
- if (signal?.aborted)
60
- abort();
61
- const collect = (target, chunk, channel) => { if (why)
62
- return; bytes += chunk.length; if (bytes > port.responseBytes) {
63
- stop('response-bytes exceeded');
64
- return;
65
- } if (channel === 'stderr' && port.stderr === 'reject') {
66
- rejectedCommand.publish({ coordinate: port.coordinate, stderr: chunk.subarray(0, Math.min(port.responseBytes, 1024)).toString('utf8') });
67
- stop('stderr forbidden');
68
- return;
69
- } target.push(chunk); };
70
- child.stdout.on('data', (chunk) => collect(stdout, chunk, 'stdout'));
71
- child.stderr.on('data', (chunk) => collect(stderr, chunk, 'stderr'));
72
- child.stdin.on('error', () => stop('request handoff failed'));
73
- const finish = (status, error) => { if (done)
74
- return; done = true; cancelTimer(); signal?.removeEventListener('abort', abort); if (error || why) {
75
- reject(Error(`REFUSE·port ${port.coordinate} ${why ?? error.message}`));
76
- return;
77
- } resolve({ status, interrupted: false, stdout: Buffer.concat(stdout).toString('utf8'), stderr: Buffer.concat(stderr).toString('utf8') }); };
78
- child.once('error', error => finish(null, error));
79
- child.once('close', status => finish(status));
80
- if (envelope) {
81
- const pipe = child.stdio[3];
82
- const header = Buffer.alloc(8);
83
- header.writeBigUInt64BE(BigInt(envelope.byteLength));
84
- pipe.on('error', () => stop('host envelope handoff failed'));
85
- pipe.end(Buffer.concat([header, Buffer.from(envelope)]));
86
- }
87
- child.stdin.end(frame);
88
- });
89
- const members = commandResponse(port, lot, result, accepts);
90
- return { members, stderr: result.stderr, invocations: 1 };
91
- }
1
+ var _=Object.defineProperty;var a=(n,p)=>_(n,"name",{value:p,configurable:!0});import{spawn as k}from"node:child_process";import{channel as F}from"node:diagnostics_channel";import{isAbsolute as C}from"node:path";import{library2_checkCommandPort as M,library2_commandRequest as N,library2_commandResponse as A}from"../../vendor/runtime.js";import{requireEffects as D}from"../read-only.js";import{assertNativeRuntime as G}from"./runtime.js";import{scheduleDeadline as K}from"./deadline.js";const T=F("bound.command.refusal");async function te(n){const p=process.hrtime.bigint(),{host:b,signal:m,phase:y,accepts:$}=n,t=Object.freeze({...n.port,closure:Object.freeze([...n.port.closure]),environment:Object.freeze([...n.port.environment])}),B=Object.freeze({...n.lot,requests:Object.freeze([...n.lot.requests])}),l=Object.freeze({...n.environment});G(l,[]);const i=a(c=>{throw Error(`REFUSE\xB7port ${t.coordinate} ${c}`)},"refuse");y!=="act"&&i("invocation forbidden during "+y),D("command "+t.coordinate),m?.aborted&&i("interrupted before invocation"),M(t,b);const j=p+BigInt(t.timeoutMs)*1000000n,E=b.resolve(t),r=Object.freeze({...E,args:Object.freeze([...E.args])});(!C(r.command)||!C(r.cwd))&&i("host invocation needs absolute coordinates"),b.loaderClosure(t,r)!=="verified"&&i("runtime-loader closure unmeasured");const h=Object.keys(l).sort(),w=[...new Set(t.environment)].sort();(h.length!==w.length||h.some((c,v)=>c!==w[v])||h.some(c=>typeof l[c]!="string"||l[c].includes("\0")))&&i("environment differs from declared handoff");const x=N(t,B),d=r.hostEnvelope;d&&(!Number.isSafeInteger(r.envelopeBytes)||r.envelopeBytes<1||d.byteLength>r.envelopeBytes)&&i("host envelope byte resources"),process.hrtime.bigint()>=j&&i("timeout before invocation"),m?.aborted&&i("interrupted before invocation");const O=await new Promise((c,v)=>{const o=k(r.command,[...r.args],{cwd:r.cwd,env:{...l},shell:!1,detached:!1,stdio:d?["pipe","pipe","pipe","pipe"]:["pipe","pipe","pipe"]}),z=[],S=[];let L=0,u,R=!1;const f=a(e=>{if(!u){u=e;try{o.kill("SIGKILL")}catch{}o.stdin.destroy(),d&&o.stdio[3]?.destroy()}},"stop"),P=K(j,()=>f("timeout")),g=a(()=>f("interrupted"),"abort");m?.addEventListener("abort",g,{once:!0}),m?.aborted&&g();const q=a((e,s,U)=>{if(!u){if(L+=s.length,L>t.responseBytes){f("response-bytes exceeded");return}if(U==="stderr"&&t.stderr==="reject"){T.publish({coordinate:t.coordinate,stderr:s.subarray(0,Math.min(t.responseBytes,1024)).toString("utf8")}),f("stderr forbidden");return}e.push(s)}},"collect");o.stdout.on("data",e=>q(z,e,"stdout")),o.stderr.on("data",e=>q(S,e,"stderr")),o.stdin.on("error",()=>f("request handoff failed"));const I=a((e,s)=>{if(!R){if(R=!0,P(),m?.removeEventListener("abort",g),s||u){v(Error(`REFUSE\xB7port ${t.coordinate} ${u??s.message}`));return}c({status:e,interrupted:!1,stdout:Buffer.concat(z).toString("utf8"),stderr:Buffer.concat(S).toString("utf8")})}},"finish");if(o.once("error",e=>I(null,e)),o.once("close",e=>I(e)),d){const e=o.stdio[3],s=Buffer.alloc(8);s.writeBigUInt64BE(BigInt(d.byteLength)),e.on("error",()=>f("host envelope handoff failed")),e.end(Buffer.concat([s,Buffer.from(d)]))}o.stdin.end(x)});return{members:A(t,B,O,$),stderr:O.stderr,invocations:1}}a(te,"executeLot");export{te as executeLot};
@@ -1,88 +1,2 @@
1
- /** Sealed Root control code: one fresh custodian connection per request. */
2
- import { parentPort, workerData } from 'node:worker_threads';
3
- import { createConnection } from 'node:net';
4
- const { endpoint, deadlineMs, shared, inputBytes, outputBytes } = workerData;
5
- const port = parentPort;
6
- if (!port)
7
- throw Error('REFUSE·Root control transport parent absent');
8
- const header = new Int32Array(shared, 0, 8), input = new Uint8Array(shared, 32, inputBytes), output = new Uint8Array(shared, 32 + inputBytes, outputBytes);
9
- const initial = Math.min(endpoint.timeoutMs, deadlineMs - Date.now());
10
- const deadline = process.hrtime.bigint() + BigInt(Math.max(0, initial)) * 1000000n;
11
- const remaining = () => Math.max(0, Number((deadline - process.hrtime.bigint() + 999999n) / 1000000n));
12
- let busy = false, red, active;
13
- function finish(state, bytes) {
14
- if (bytes.byteLength > outputBytes) {
15
- state = 3;
16
- bytes = Buffer.from('REFUSE·reply bytes').subarray(0, outputBytes);
17
- }
18
- output.set(bytes);
19
- Atomics.store(header, 3, bytes.byteLength);
20
- Atomics.store(header, 0, state);
21
- Atomics.notify(header, 0);
22
- }
23
- // Timer slices never widen the original monotonic deadline.
24
- let timer;
25
- function arm() {
26
- const left = remaining();
27
- if (left > 0) {
28
- timer = setTimeout(arm, Math.min(left, 2147483647));
29
- timer.unref();
30
- return;
31
- }
32
- red ?? (red = 'REFUSE·parent deadline');
33
- active?.destroy(Error(red));
34
- if (Atomics.load(header, 0) === 1)
35
- finish(3, Buffer.from(red).subarray(0, outputBytes));
36
- port.close();
37
- }
38
- arm();
39
- port.on('close', () => { clearTimeout(timer); active?.destroy(); });
40
- port.on('message', async (message) => {
41
- if (busy || red || message !== Atomics.load(header, 1) || Atomics.load(header, 0) !== 1) {
42
- red ?? (red = 'REFUSE·sequence/state');
43
- finish(3, Buffer.from(red).subarray(0, outputBytes));
44
- return;
45
- }
46
- busy = true;
47
- try {
48
- const length = Atomics.load(header, 2);
49
- if (length < 1 || length > inputBytes)
50
- throw Error('REFUSE·request bytes');
51
- if (remaining() <= 0)
52
- throw Error('REFUSE·parent deadline');
53
- const frame = Buffer.from(input.slice(0, length));
54
- const bytes = await new Promise((resolve, reject) => {
55
- const socket = createConnection(endpoint.socket);
56
- active = socket;
57
- const chunks = [];
58
- let size = 0, done = false;
59
- const end = (error) => { if (done)
60
- return; done = true; if (error)
61
- reject(error);
62
- else
63
- resolve(Buffer.concat(chunks)); };
64
- socket.once('connect', () => socket.write(Buffer.concat([frame, Buffer.from('\n')])));
65
- socket.on('data', chunk => { size += chunk.length; if (size > outputBytes)
66
- socket.destroy(Error('REFUSE·reply bytes'));
67
- else
68
- chunks.push(chunk); });
69
- socket.once('error', end);
70
- socket.once('close', () => end());
71
- });
72
- active = undefined;
73
- if (!bytes.length)
74
- throw Error('REFUSE·interrupted before reply');
75
- if (remaining() <= 0)
76
- throw Error('REFUSE·parent deadline');
77
- busy = false;
78
- finish(2, bytes);
79
- }
80
- catch (error) {
81
- active = undefined;
82
- red ?? (red = error instanceof Error ? error.message : String(error));
83
- busy = false;
84
- finish(3, Buffer.from(red).subarray(0, outputBytes));
85
- }
86
- });
87
- Atomics.store(header, 5, 1);
88
- Atomics.notify(header, 5);
1
+ var F=Object.defineProperty;var a=(r,e)=>F(r,"name",{value:e,configurable:!0});import{parentPort as M,workerData as k}from"node:worker_threads";import{createConnection as q}from"node:net";const{endpoint:A,deadlineMs:x,shared:l,inputBytes:d,outputBytes:s}=k,c=M;if(!c)throw Error("REFUSE\xB7Root control transport parent absent");const o=new Int32Array(l,0,8),D=new Uint8Array(l,32,d),I=new Uint8Array(l,32+d,s),L=Math.min(A.timeoutMs,x-Date.now()),P=process.hrtime.bigint()+BigInt(Math.max(0,L))*1000000n,E=a(()=>Math.max(0,Number((P-process.hrtime.bigint()+999999n)/1000000n)),"remaining");let u=!1,t,f;function m(r,e){e.byteLength>s&&(r=3,e=Buffer.from("REFUSE\xB7reply bytes").subarray(0,s)),I.set(e),Atomics.store(o,3,e.byteLength),Atomics.store(o,0,r),Atomics.notify(o,0)}a(m,"finish");let y;function B(){const r=E();if(r>0){y=setTimeout(B,Math.min(r,2147483647)),y.unref();return}t??(t="REFUSE\xB7parent deadline"),f?.destroy(Error(t)),Atomics.load(o,0)===1&&m(3,Buffer.from(t).subarray(0,s)),c.close()}a(B,"arm"),B(),c.on("close",()=>{clearTimeout(y),f?.destroy()}),c.on("message",async r=>{if(u||t||r!==Atomics.load(o,1)||Atomics.load(o,0)!==1){t??(t="REFUSE\xB7sequence/state"),m(3,Buffer.from(t).subarray(0,s));return}u=!0;try{const e=Atomics.load(o,2);if(e<1||e>d)throw Error("REFUSE\xB7request bytes");if(E()<=0)throw Error("REFUSE\xB7parent deadline");const U=Buffer.from(D.slice(0,e)),h=await new Promise((R,S)=>{const n=q(A.socket);f=n;const p=[];let b=0,w=!1;const g=a(i=>{w||(w=!0,i?S(i):R(Buffer.concat(p)))},"end");n.once("connect",()=>n.write(Buffer.concat([U,Buffer.from(`
2
+ `)]))),n.on("data",i=>{b+=i.length,b>s?n.destroy(Error("REFUSE\xB7reply bytes")):p.push(i)}),n.once("error",g),n.once("close",()=>g())});if(f=void 0,!h.length)throw Error("REFUSE\xB7interrupted before reply");if(E()<=0)throw Error("REFUSE\xB7parent deadline");u=!1,m(2,h)}catch(e){f=void 0,t??(t=e instanceof Error?e.message:String(e)),u=!1,m(3,Buffer.from(t).subarray(0,s))}}),Atomics.store(o,5,1),Atomics.notify(o,5);
@@ -1,110 +1 @@
1
- import { sha256 as digest } from "../hash.js";
2
- /** Root-fixed synchronous transport. It retains no grants or command results. */
3
- import { Worker, isMainThread } from 'node:worker_threads';
4
- import { readBoundedFile } from "./bounded-file.js";
5
- import { remainingDeadlineMs } from "./deadline.js";
6
- const FixedWorker = Worker;
7
- const HEADER_BYTES = 32;
8
- let controlWorkerOccupied = false;
9
- export function createFixedControlBridge(inputBinding) {
10
- // @1 is one main-thread control worker per OS process, never a WorldWorker grant.
11
- if (!isMainThread)
12
- throw Error('REFUSE·Root control transport requires main-thread realm');
13
- const refuse = (why) => { throw Error('REFUSE·Root control transport ' + why); };
14
- const integer = (value) => Number.isSafeInteger(value) && value > 0;
15
- const sourceBytes = inputBinding.workerBytes;
16
- const workerSelection = Object.freeze({ ...inputBinding.worker });
17
- if (!(sourceBytes instanceof Uint8Array) || !workerSelection.coordinate || !integer(workerSelection.bytes) || sourceBytes.byteLength !== workerSelection.bytes)
18
- refuse('selected worker bytes/type bound differs before copy');
19
- const binding = Object.freeze({ ...inputBinding,
20
- endpoint: Object.freeze({ ...inputBinding.endpoint }), resources: Object.freeze({ ...inputBinding.resources }),
21
- runtime: Object.freeze({ ...inputBinding.runtime }), worker: workerSelection,
22
- workerBytes: Buffer.from(sourceBytes) });
23
- const { endpoint, resources, runtime, worker: selected } = binding;
24
- if (!selected.coordinate || !integer(selected.bytes) || binding.workerBytes.byteLength !== selected.bytes || digest(binding.workerBytes) !== selected.digest)
25
- refuse('selected worker bytes differ');
26
- if (runtime.path !== process.execPath || !integer(runtime.bytes) || digest(readBoundedFile(runtime.path, runtime.bytes, runtime.path)) !== runtime.digest)
27
- refuse('selected runtime bytes differ');
28
- if (![endpoint.timeoutMs, endpoint.responseBytes, endpoint.deadlineMs, binding.deadlineMs].every(integer) || binding.deadlineMs > endpoint.deadlineMs)
29
- refuse('parent time/byte resource absent or widened');
30
- // host-control-transport@1 encodes each byte length in a signed Int32 cell.
31
- // Refuse an incompatible declaration; never wrap or clamp its bound.
32
- if (resources.inputBytes > 2147483647 || resources.outputBytes > 2147483647)
33
- refuse('host-control-transport@1 signed Int32 length representation');
34
- const sharedBytes = HEADER_BYTES + resources.inputBytes + resources.outputBytes;
35
- if (!Object.values(resources).every(integer) || !Number.isSafeInteger(sharedBytes) || resources.inputBytes > endpoint.responseBytes || resources.outputBytes > endpoint.responseBytes || sharedBytes > resources.sharedBytes)
36
- refuse('shared/input/output resource absent or exceeded');
37
- const remainingMs = Math.min(endpoint.timeoutMs, binding.deadlineMs - Date.now());
38
- if (remainingMs <= 0)
39
- refuse('parent deadline');
40
- const deadline = process.hrtime.bigint() + BigInt(remainingMs) * 1000000n;
41
- const remaining = remainingDeadlineMs.bind(undefined, deadline);
42
- if (controlWorkerOccupied)
43
- refuse('one live main-thread control worker resource exhausted');
44
- let shared;
45
- try {
46
- shared = new SharedArrayBuffer(sharedBytes);
47
- }
48
- catch {
49
- return refuse('shared resource allocation');
50
- }
51
- const header = new Int32Array(shared, 0, 8), input = new Uint8Array(shared, HEADER_BYTES, resources.inputBytes), output = new Uint8Array(shared, HEADER_BYTES + resources.inputBytes, resources.outputBytes);
52
- // Independently sealed bytes are immutable here. A payload cannot select a path or verifier.
53
- const code = Buffer.from(binding.workerBytes).toString('base64');
54
- controlWorkerOccupied = true;
55
- let worker;
56
- try {
57
- worker = new FixedWorker(new URL('data:text/javascript;base64,' + code), { execArgv: [], env: {}, workerData: { endpoint: { ...endpoint }, deadlineMs: binding.deadlineMs, shared, inputBytes: resources.inputBytes, outputBytes: resources.outputBytes } });
58
- }
59
- catch {
60
- controlWorkerOccupied = false;
61
- return refuse('worker construction interrupted');
62
- }
63
- let termination;
64
- const stop = () => termination ?? (termination = worker.terminate().finally(() => { controlWorkerOccupied = false; }));
65
- worker.once('exit', () => { controlWorkerOccupied = false; });
66
- worker.unref();
67
- let red, closed = false, sequence = 0;
68
- const fail = (why) => { red ?? (red = 'REFUSE·Root control transport ' + why); void stop(); throw Error(red); };
69
- // Error events still need a listener when a synchronous wait prevents JS callbacks.
70
- worker.on('error', error => { red ?? (red = 'REFUSE·Root control transport worker interrupted: ' + error.message); });
71
- if (Atomics.load(header, 5) !== 1 && Atomics.wait(header, 5, 0, remaining()) === 'timed-out')
72
- fail('startup deadline/interruption');
73
- return {
74
- exchange(frame) {
75
- if (red)
76
- throw Error(red);
77
- if (closed)
78
- fail('closed');
79
- if (remaining() <= 0)
80
- fail('parent deadline');
81
- if (typeof frame !== 'string' || !frame.length || Buffer.byteLength(frame, 'utf8') > resources.inputBytes)
82
- fail('request bytes before copy');
83
- const bytes = Buffer.from(frame, 'utf8');
84
- if (Atomics.load(header, 0) !== 0 || sequence >= 2147483647)
85
- fail('overlapping request/sequence');
86
- input.set(bytes);
87
- Atomics.store(header, 2, bytes.length);
88
- Atomics.store(header, 1, ++sequence);
89
- Atomics.store(header, 0, 1);
90
- worker.postMessage(sequence);
91
- if (Atomics.wait(header, 0, 1, remaining()) === 'timed-out' || remaining() <= 0)
92
- fail('deadline/interruption');
93
- const state = Atomics.load(header, 0), length = Atomics.load(header, 3);
94
- if (length < 1 || length > resources.outputBytes)
95
- fail('reply bytes');
96
- let reply;
97
- try {
98
- reply = new TextDecoder('utf-8', { fatal: true }).decode(output.slice(0, length));
99
- }
100
- catch {
101
- return fail('reply encoding');
102
- }
103
- if (state !== 2)
104
- fail(reply);
105
- Atomics.store(header, 0, 0);
106
- return reply;
107
- },
108
- async close() { closed = true; await stop(); }
109
- };
110
- }
1
+ var D=Object.defineProperty;var f=(n,r)=>D(n,"name",{value:r,configurable:!0});import{sha256 as U}from"../hash.js";import{Worker as I,isMainThread as L}from"node:worker_threads";import{readBoundedFile as T}from"./bounded-file.js";import{remainingDeadlineMs as W}from"./deadline.js";const N=I,E=32;let p=!1;function G(n){if(!L)throw Error("REFUSE\xB7Root control transport requires main-thread realm");const r=f(t=>{throw Error("REFUSE\xB7Root control transport "+t)},"refuse"),l=f(t=>Number.isSafeInteger(t)&&t>0,"integer"),w=n.workerBytes,m=Object.freeze({...n.worker});(!(w instanceof Uint8Array)||!m.coordinate||!l(m.bytes)||w.byteLength!==m.bytes)&&r("selected worker bytes/type bound differs before copy");const i=Object.freeze({...n,endpoint:Object.freeze({...n.endpoint}),resources:Object.freeze({...n.resources}),runtime:Object.freeze({...n.runtime}),worker:m,workerBytes:Buffer.from(w)}),{endpoint:a,resources:e,runtime:d,worker:b}=i;(!b.coordinate||!l(b.bytes)||i.workerBytes.byteLength!==b.bytes||U(i.workerBytes)!==b.digest)&&r("selected worker bytes differ"),(d.path!==process.execPath||!l(d.bytes)||U(T(d.path,d.bytes,d.path))!==d.digest)&&r("selected runtime bytes differ"),(![a.timeoutMs,a.responseBytes,a.deadlineMs,i.deadlineMs].every(l)||i.deadlineMs>a.deadlineMs)&&r("parent time/byte resource absent or widened"),(e.inputBytes>2147483647||e.outputBytes>2147483647)&&r("host-control-transport@1 signed Int32 length representation");const B=E+e.inputBytes+e.outputBytes;(!Object.values(e).every(l)||!Number.isSafeInteger(B)||e.inputBytes>a.responseBytes||e.outputBytes>a.responseBytes||B>e.sharedBytes)&&r("shared/input/output resource absent or exceeded");const M=Math.min(a.timeoutMs,i.deadlineMs-Date.now());M<=0&&r("parent deadline");const j=process.hrtime.bigint()+BigInt(M)*1000000n,h=W.bind(void 0,j);p&&r("one live main-thread control worker resource exhausted");let y;try{y=new SharedArrayBuffer(B)}catch{return r("shared resource allocation")}const o=new Int32Array(y,0,8),F=new Uint8Array(y,E,e.inputBytes),O=new Uint8Array(y,E+e.inputBytes,e.outputBytes),q=Buffer.from(i.workerBytes).toString("base64");p=!0;let u;try{u=new N(new URL("data:text/javascript;base64,"+q),{execArgv:[],env:{},workerData:{endpoint:{...a},deadlineMs:i.deadlineMs,shared:y,inputBytes:e.inputBytes,outputBytes:e.outputBytes}})}catch{return p=!1,r("worker construction interrupted")}let x;const R=f(()=>x??(x=u.terminate().finally(()=>{p=!1})),"stop");u.once("exit",()=>{p=!1}),u.unref();let c,S=!1,g=0;const s=f(t=>{throw c??(c="REFUSE\xB7Root control transport "+t),R(),Error(c)},"fail");return u.on("error",t=>{c??(c="REFUSE\xB7Root control transport worker interrupted: "+t.message)}),Atomics.load(o,5)!==1&&Atomics.wait(o,5,0,h())==="timed-out"&&s("startup deadline/interruption"),{exchange(t){if(c)throw Error(c);S&&s("closed"),h()<=0&&s("parent deadline"),(typeof t!="string"||!t.length||Buffer.byteLength(t,"utf8")>e.inputBytes)&&s("request bytes before copy");const v=Buffer.from(t,"utf8");(Atomics.load(o,0)!==0||g>=2147483647)&&s("overlapping request/sequence"),F.set(v),Atomics.store(o,2,v.length),Atomics.store(o,1,++g),Atomics.store(o,0,1),u.postMessage(g),(Atomics.wait(o,0,1,h())==="timed-out"||h()<=0)&&s("deadline/interruption");const z=Atomics.load(o,0),k=Atomics.load(o,3);(k<1||k>e.outputBytes)&&s("reply bytes");let A;try{A=new TextDecoder("utf-8",{fatal:!0}).decode(O.slice(0,k))}catch{return s("reply encoding")}return z!==2&&s(A),Atomics.store(o,0,0),A},async close(){S=!0,await R()}}}f(G,"createFixedControlBridge");export{G as createFixedControlBridge};
@@ -1,32 +1 @@
1
- /** Optional Root-selected control transport. This is fixed host machinery,
2
- * never a world's worker permission or a change to producer message/work caps.
3
- * One retained control worker belongs only to the main thread of each process;
4
- * other Node realms keep the existing bounded fixed-client transport. */
5
- import { parsedLine as parse } from "../../fold/claims.js";
6
- import { authenticateSignedPort } from "./reading.js";
7
- const fail = (why) => { throw Error('REFUSE·host control ' + why); };
8
- const keys = (value) => value && typeof value === 'object' && !Array.isArray(value) ? Object.keys(value).sort().join(',') : '';
9
- const digest = (value) => typeof value === 'string' && /^sha256:[a-f0-9]{64}$/.test(value);
10
- export function selectedHostControl(root, parent) {
11
- const coordinate = [parent.port.coordinate, 'control'].join('/'), rows = root.live.filter(line => { const got = parse(line); return got.kind === 'fact' && got.value.fields.scope === coordinate; });
12
- if (!rows.length)
13
- return undefined;
14
- const selected = authenticateSignedPort(root, coordinate), profile = selected.profile, parsed = parse(selected.line);
15
- if (parsed.kind !== 'fact' || parsed.value.fields.by !== root.signer.id)
16
- fail('selection is not Root signed ' + coordinate);
17
- if (keys(profile) !== 'coordinate,limits,profile,realm,runtime,selector,worker' || profile.profile !== 'host-control-transport@1' || profile.realm !== 'main-thread' || profile.coordinate !== coordinate || keys(profile.selector) !== 'implementation,port' || profile.selector.port !== parent.port.coordinate || profile.selector.implementation !== parent.port.implementation)
18
- fail('profile selector differs ' + coordinate);
19
- if (keys(profile.worker) !== 'bytes,coordinate,digest' || !/^native\/executor\/(?:dist|src)\/host\/native\/control-bridge-worker\.(?:js|ts)$/.test(profile.worker.coordinate) || !digest(profile.worker.digest) || !Number.isSafeInteger(profile.worker.bytes) || profile.worker.bytes < 1)
20
- fail('fixed worker binding absent ' + coordinate);
21
- const runtime = parent.selection.members.find(member => member.coordinate === parent.selection.runtime);
22
- if (keys(profile.runtime) !== 'coordinate,digest' || !runtime || profile.runtime.coordinate !== runtime.coordinate || profile.runtime.digest !== runtime.digest)
23
- fail('fixed runtime selection differs ' + coordinate);
24
- if (keys(profile.limits) !== 'inFlight,sharedBytes,workersPerProcess' || profile.limits.workersPerProcess !== 1 || profile.limits.inFlight !== 1 || !Number.isSafeInteger(profile.limits.sharedBytes) || profile.limits.sharedBytes < 32)
25
- fail('named resource invalid ' + coordinate);
26
- if (!Number.isSafeInteger(parent.port.responseBytes) || parent.port.responseBytes < 1 || parent.port.responseBytes > 2147483647)
27
- fail('message cap outside declared signed Int32 representation ' + coordinate);
28
- const required = 32 + 2 * parent.port.responseBytes;
29
- if (!Number.isSafeInteger(required) || profile.limits.sharedBytes < required)
30
- fail('shared resource cannot preserve message caps ' + coordinate);
31
- return Object.freeze({ line: selected.line, profile });
32
- }
1
+ var g=Object.defineProperty;var s=(r,t)=>g(r,"name",{value:t,configurable:!0});import{parsedLine as p}from"../../fold/claims.js";import{authenticateSignedPort as u}from"./reading.js";const o=s(r=>{throw Error("REFUSE\xB7host control "+r)},"fail"),n=s(r=>r&&typeof r=="object"&&!Array.isArray(r)?Object.keys(r).sort().join(","):"","keys"),y=s(r=>typeof r=="string"&&/^sha256:[a-f0-9]{64}$/.test(r),"digest");function B(r,t){const i=[t.port.coordinate,"control"].join("/");if(!r.live.filter(l=>{const m=p(l);return m.kind==="fact"&&m.value.fields.scope===i}).length)return;const c=u(r,i),e=c.profile,a=p(c.line);(a.kind!=="fact"||a.value.fields.by!==r.signer.id)&&o("selection is not Root signed "+i),(n(e)!=="coordinate,limits,profile,realm,runtime,selector,worker"||e.profile!=="host-control-transport@1"||e.realm!=="main-thread"||e.coordinate!==i||n(e.selector)!=="implementation,port"||e.selector.port!==t.port.coordinate||e.selector.implementation!==t.port.implementation)&&o("profile selector differs "+i),(n(e.worker)!=="bytes,coordinate,digest"||!/^native\/executor\/(?:dist|src)\/host\/native\/control-bridge-worker\.(?:js|ts)$/.test(e.worker.coordinate)||!y(e.worker.digest)||!Number.isSafeInteger(e.worker.bytes)||e.worker.bytes<1)&&o("fixed worker binding absent "+i);const d=t.selection.members.find(l=>l.coordinate===t.selection.runtime);(n(e.runtime)!=="coordinate,digest"||!d||e.runtime.coordinate!==d.coordinate||e.runtime.digest!==d.digest)&&o("fixed runtime selection differs "+i),(n(e.limits)!=="inFlight,sharedBytes,workersPerProcess"||e.limits.workersPerProcess!==1||e.limits.inFlight!==1||!Number.isSafeInteger(e.limits.sharedBytes)||e.limits.sharedBytes<32)&&o("named resource invalid "+i),(!Number.isSafeInteger(t.port.responseBytes)||t.port.responseBytes<1||t.port.responseBytes>2147483647)&&o("message cap outside declared signed Int32 representation "+i);const f=32+2*t.port.responseBytes;return(!Number.isSafeInteger(f)||e.limits.sharedBytes<f)&&o("shared resource cannot preserve message caps "+i),Object.freeze({line:c.line,profile:e})}s(B,"selectedHostControl");export{B as selectedHostControl};