@prestack/thread 0.1.3 → 0.1.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 (270) hide show
  1. package/CHANGELOG.md +563 -0
  2. package/dist/core/actor.d.ts +1 -1
  3. package/dist/core/actor.js +5 -4
  4. package/dist/core/actor.ts +5 -4
  5. package/dist/core/attach.d.ts +0 -7
  6. package/dist/core/attach.js +38 -67
  7. package/dist/core/attach.ts +34 -80
  8. package/dist/core/chain.js +13 -17
  9. package/dist/core/chain.ts +11 -15
  10. package/dist/core/condition.d.ts +4 -4
  11. package/dist/core/condition.js +2 -2
  12. package/dist/core/descend.d.ts +4 -2
  13. package/dist/core/descend.js +91 -23
  14. package/dist/core/descend.ts +90 -21
  15. package/dist/core/errors/catalogue.d.ts +52 -0
  16. package/dist/{operations/errors/errors.js → core/errors/catalogue.js} +1 -1
  17. package/dist/{operations/errors/errors.ts → core/errors/catalogue.ts} +1 -1
  18. package/dist/{operations/errors/catalog.d.ts → core/errors/error.d.ts} +2 -3
  19. package/dist/{operations/errors/catalog.js → core/errors/error.js} +1 -16
  20. package/dist/{operations/errors/catalog.ts → core/errors/error.ts} +3 -19
  21. package/dist/core/errors/namespaces.d.ts +3 -0
  22. package/dist/core/errors/namespaces.js +15 -0
  23. package/dist/core/errors/namespaces.ts +19 -0
  24. package/dist/core/finding.js +2 -2
  25. package/dist/core/finding.ts +1 -2
  26. package/dist/core/init.d.ts +1 -1
  27. package/dist/core/init.js +51 -5
  28. package/dist/core/init.ts +58 -5
  29. package/dist/core/invoked.d.ts +1 -1
  30. package/dist/core/invoked.js +3 -1
  31. package/dist/core/invoked.ts +5 -2
  32. package/dist/core/round.js +36 -22
  33. package/dist/core/round.ts +26 -9
  34. package/dist/core/rows.d.ts +4 -0
  35. package/dist/core/rows.js +31 -2
  36. package/dist/core/rows.ts +33 -1
  37. package/dist/core/scope.d.ts +1 -0
  38. package/dist/core/scope.js +1 -0
  39. package/dist/core/scope.ts +2 -0
  40. package/dist/core/thread.d.ts +3 -3
  41. package/dist/core/tree.d.ts +4 -1
  42. package/dist/core/tree.js +26 -6
  43. package/dist/core/tree.ts +29 -6
  44. package/dist/core/types.d.ts +3 -3
  45. package/dist/core/types.js +3 -1
  46. package/dist/core/types.ts +2 -1
  47. package/dist/derivation/attach.js +3 -2
  48. package/dist/derivation/attach.ts +2 -1
  49. package/dist/derivation/generators/build.js +6 -6
  50. package/dist/derivation/generators/build.ts +1 -1
  51. package/dist/derivation/generators/check.js +1 -1
  52. package/dist/derivation/generators/determinism.js +1 -1
  53. package/dist/derivation/generators/release.js +60 -47
  54. package/dist/derivation/generators/release.ts +66 -43
  55. package/dist/derivation/release.d.ts +7 -3
  56. package/dist/derivation/release.js +14 -1
  57. package/dist/derivation/release.ts +22 -2
  58. package/dist/integrations/attach.d.ts +4 -0
  59. package/dist/integrations/attach.js +37 -56
  60. package/dist/integrations/attach.ts +54 -60
  61. package/dist/integrations/cli.d.ts +37 -0
  62. package/dist/integrations/cli.js +440 -0
  63. package/dist/integrations/cli.ts +543 -0
  64. package/dist/{core/files.d.ts → integrations/disk.d.ts} +16 -24
  65. package/dist/integrations/disk.js +435 -0
  66. package/dist/integrations/disk.ts +465 -0
  67. package/dist/integrations/generators/read.js +6 -6
  68. package/dist/integrations/generators/read.ts +4 -4
  69. package/dist/integrations/generators/vault.js +10 -11
  70. package/dist/integrations/generators/vault.ts +4 -5
  71. package/dist/{operations/http.d.ts → integrations/https.d.ts} +10 -6
  72. package/dist/integrations/https.js +170 -0
  73. package/dist/integrations/https.ts +216 -0
  74. package/dist/integrations/pglite.d.ts +26 -0
  75. package/dist/integrations/pglite.js +143 -0
  76. package/dist/integrations/pglite.ts +206 -0
  77. package/dist/integrations/secret.d.ts +8 -2
  78. package/dist/integrations/secret.js +3 -5
  79. package/dist/integrations/secret.ts +10 -6
  80. package/dist/integrations/target.d.ts +155 -14
  81. package/dist/integrations/target.js +171 -50
  82. package/dist/integrations/target.ts +377 -61
  83. package/dist/integrations/vault.d.ts +1 -0
  84. package/dist/integrations/vault.js +20 -5
  85. package/dist/integrations/vault.ts +17 -5
  86. package/dist/integrity/attach.js +8 -77
  87. package/dist/integrity/attach.ts +7 -76
  88. package/dist/integrity/check-threads.js +5 -5
  89. package/dist/integrity/check-threads.ts +2 -2
  90. package/dist/integrity/constraint.d.ts +0 -1
  91. package/dist/integrity/constraint.js +267 -366
  92. package/dist/integrity/constraint.ts +267 -375
  93. package/dist/integrity/generators/compiler.js +14 -17
  94. package/dist/integrity/generators/compiler.ts +10 -15
  95. package/dist/integrity/generators/gate.js +15 -15
  96. package/dist/integrity/generators/gate.ts +10 -10
  97. package/dist/integrity/generators/issues.js +7 -7
  98. package/dist/integrity/generators/publish.js +39 -57
  99. package/dist/integrity/generators/publish.ts +44 -55
  100. package/dist/integrity/generators/trial.d.ts +3 -3
  101. package/dist/integrity/generators/trial.js +133 -165
  102. package/dist/integrity/generators/trial.ts +145 -169
  103. package/dist/integrity/lint.js +9 -13
  104. package/dist/integrity/lint.ts +7 -10
  105. package/dist/integrity/lsp/bin.js +5 -4
  106. package/dist/integrity/lsp/bin.ts +3 -1
  107. package/dist/integrity/lsp/diagnostics.js +3 -3
  108. package/dist/integrity/lsp/diagnostics.ts +1 -1
  109. package/dist/integrity/lsp/lint.js +19 -30
  110. package/dist/integrity/lsp/lint.ts +16 -32
  111. package/dist/integrity/lsp/server.js +3 -3
  112. package/dist/integrity/slopnator.d.ts +3 -13
  113. package/dist/integrity/slopnator.js +59 -38
  114. package/dist/integrity/slopnator.ts +54 -38
  115. package/dist/integrity/slopnators/sl_broken_lineage.js +2 -2
  116. package/dist/integrity/slopnators/sl_declared_elsewhere.js +1 -1
  117. package/dist/integrity/slopnators/sl_derived_drift.js +4 -4
  118. package/dist/integrity/slopnators/sl_derived_drift.ts +1 -1
  119. package/dist/integrity/slopnators/sl_door_in_a_node.js +220 -0
  120. package/dist/integrity/slopnators/sl_door_in_a_node.ts +250 -0
  121. package/dist/integrity/slopnators/sl_inert_island.js +10 -8
  122. package/dist/integrity/slopnators/sl_inert_island.ts +8 -6
  123. package/dist/integrity/slopnators/sl_missing_row.js +6 -51
  124. package/dist/integrity/slopnators/sl_missing_row.ts +4 -50
  125. package/dist/integrity/slopnators/sl_no_barrel.js +1 -1
  126. package/dist/integrity/slopnators/sl_no_child_process.js +36 -18
  127. package/dist/integrity/slopnators/sl_no_child_process.ts +44 -18
  128. package/dist/integrity/slopnators/sl_no_dispatch_bypass.js +80 -149
  129. package/dist/integrity/slopnators/sl_no_dispatch_bypass.ts +95 -147
  130. package/dist/integrity/slopnators/sl_no_import_cycle.js +15 -4
  131. package/dist/integrity/slopnators/sl_no_import_cycle.ts +20 -4
  132. package/dist/integrity/slopnators/sl_no_such_command.js +2 -2
  133. package/dist/integrity/slopnators/sl_no_such_command.ts +1 -1
  134. package/dist/integrity/slopnators/sl_outside_an_operation.js +20 -0
  135. package/dist/integrity/slopnators/sl_outside_an_operation.ts +25 -0
  136. package/dist/integrity/slopnators/sl_screen_holds_only_declared.d.ts +2 -0
  137. package/dist/integrity/slopnators/sl_screen_holds_only_declared.js +61 -0
  138. package/dist/integrity/slopnators/sl_screen_holds_only_declared.ts +70 -0
  139. package/dist/integrity/slopnators/sl_secret_literal.js +1 -33
  140. package/dist/integrity/slopnators/sl_secret_literal.ts +1 -33
  141. package/dist/integrity/slopnators/sl_stale_path.js +5 -22
  142. package/dist/integrity/slopnators/sl_stale_path.ts +4 -20
  143. package/dist/integrity/slopnators/sl_twice_meant.js +1 -1
  144. package/dist/integrity/slopnators/sl_twice_parented.js +29 -5
  145. package/dist/integrity/slopnators/sl_twice_parented.ts +28 -4
  146. package/dist/integrity/slopnators/sl_undeclared_crossing.d.ts +4 -1
  147. package/dist/integrity/slopnators/sl_undeclared_crossing.js +426 -62
  148. package/dist/integrity/slopnators/sl_undeclared_crossing.ts +454 -70
  149. package/dist/integrity/slopnators/sl_undeclared_destination.js +59 -25
  150. package/dist/integrity/slopnators/sl_undeclared_destination.ts +69 -21
  151. package/dist/integrity/slopnators/sl_undeclared_list.d.ts +2 -0
  152. package/dist/integrity/slopnators/sl_undeclared_list.js +363 -0
  153. package/dist/integrity/slopnators/sl_undeclared_list.ts +358 -0
  154. package/dist/integrity/slopnators/sl_undeclared_node.js +1 -11
  155. package/dist/integrity/slopnators/sl_undeclared_node.ts +0 -10
  156. package/dist/integrity/slopnators/sl_undeclared_operation.js +111 -29
  157. package/dist/integrity/slopnators/sl_undeclared_operation.ts +134 -26
  158. package/dist/integrity/slopnators/sl_undeclared_variant.js +2 -2
  159. package/dist/integrity/slopnators/sl_undeclared_webhook.d.ts +2 -0
  160. package/dist/integrity/slopnators/sl_undeclared_webhook.js +90 -0
  161. package/dist/integrity/slopnators/sl_undeclared_webhook.ts +108 -0
  162. package/dist/integrity/slopnators/sl_underived_route.d.ts +2 -0
  163. package/dist/integrity/slopnators/sl_underived_route.js +61 -0
  164. package/dist/integrity/slopnators/sl_underived_route.ts +72 -0
  165. package/dist/integrity/slopnators/sl_ungoverned_island.js +3 -3
  166. package/dist/integrity/slopnators/sl_ungoverned_island.ts +1 -1
  167. package/dist/integrity/slopnators/sl_unlisted_program.d.ts +2 -0
  168. package/dist/integrity/slopnators/sl_unlisted_program.js +39 -0
  169. package/dist/integrity/slopnators/sl_unlisted_program.ts +40 -0
  170. package/dist/integrity/slopnators/sl_unnamed_island.js +1 -1
  171. package/dist/integrity/slopnators/sl_unpaired_member.js +3 -3
  172. package/dist/integrity/slopnators/sl_unpaired_member.ts +1 -1
  173. package/dist/integrity/slopnators/sl_unpaired_slopnator.js +4 -4
  174. package/dist/integrity/slopnators/sl_unpaired_slopnator.ts +1 -1
  175. package/dist/integrity/slopnators/sl_unprefixed_member.js +5 -31
  176. package/dist/integrity/slopnators/sl_unprefixed_member.ts +2 -28
  177. package/dist/integrity/slopnators/sl_unthreaded.js +11 -7
  178. package/dist/integrity/slopnators/sl_unthreaded.ts +6 -7
  179. package/dist/integrity/slopnators/sl_untranslated_key.js +2 -25
  180. package/dist/integrity/slopnators/sl_untranslated_key.ts +1 -25
  181. package/dist/integrity/slopnators/sl_unvaulted_secret.d.ts +2 -0
  182. package/dist/integrity/slopnators/sl_unvaulted_secret.js +107 -0
  183. package/dist/integrity/slopnators/sl_unvaulted_secret.ts +132 -0
  184. package/dist/integrity/slopnators/sl_unversioned.js +6 -4
  185. package/dist/integrity/slopnators/sl_unversioned.ts +5 -3
  186. package/dist/integrity/sweep.d.ts +1 -0
  187. package/dist/integrity/sweep.js +105 -42
  188. package/dist/integrity/sweep.ts +115 -42
  189. package/dist/knowledge/attach.js +4 -3
  190. package/dist/knowledge/attach.ts +3 -2
  191. package/dist/knowledge/generators/docsgen.js +8 -6
  192. package/dist/knowledge/generators/docsgen.ts +4 -2
  193. package/dist/knowledge/generators/glossary.js +4 -4
  194. package/dist/knowledge/generators/glossary.ts +1 -1
  195. package/dist/operations/access.d.ts +23 -0
  196. package/dist/operations/access.js +28 -1
  197. package/dist/operations/access.ts +43 -0
  198. package/dist/operations/attach.js +33 -113
  199. package/dist/operations/attach.ts +51 -117
  200. package/dist/operations/capabilities.d.ts +14 -5
  201. package/dist/operations/capabilities.js +13 -4
  202. package/dist/operations/capabilities.ts +27 -8
  203. package/dist/operations/dispatch.d.ts +8 -2
  204. package/dist/operations/dispatch.js +128 -185
  205. package/dist/operations/dispatch.ts +138 -191
  206. package/dist/operations/error-results.d.ts +1 -1
  207. package/dist/operations/error-results.js +2 -2
  208. package/dist/operations/error-results.ts +2 -2
  209. package/dist/operations/generators/capability.d.ts +11 -1
  210. package/dist/operations/generators/capability.js +481 -224
  211. package/dist/operations/generators/capability.ts +562 -233
  212. package/dist/operations/generators/compile.js +66 -39
  213. package/dist/operations/generators/compile.ts +78 -39
  214. package/dist/operations/generators/permits.js +11 -12
  215. package/dist/operations/generators/permits.ts +3 -4
  216. package/dist/operations/generators/seed.d.ts +1 -0
  217. package/dist/operations/generators/seed.js +19 -17
  218. package/dist/operations/generators/seed.ts +22 -14
  219. package/dist/operations/generators/trial.d.ts +6 -8
  220. package/dist/operations/generators/trial.js +200 -70
  221. package/dist/operations/generators/trial.ts +230 -93
  222. package/dist/operations/nodes.d.ts +15 -16
  223. package/dist/operations/nodes.ts +16 -17
  224. package/dist/operations/operations.d.ts +40 -4
  225. package/dist/operations/operations.js +1 -0
  226. package/dist/operations/operations.ts +61 -4
  227. package/dist/operations/result.d.ts +2 -2
  228. package/dist/operations/result.ts +2 -2
  229. package/package.json +1 -4
  230. package/dist/core/answers.d.ts +0 -171
  231. package/dist/core/answers.js +0 -789
  232. package/dist/core/answers.ts +0 -999
  233. package/dist/core/client.d.ts +0 -63
  234. package/dist/core/client.js +0 -249
  235. package/dist/core/client.ts +0 -289
  236. package/dist/core/files.js +0 -171
  237. package/dist/core/files.ts +0 -222
  238. package/dist/integrations/module.d.ts +0 -6
  239. package/dist/integrations/module.js +0 -153
  240. package/dist/integrations/module.ts +0 -191
  241. package/dist/integrity/slopnators/sl_unkept_rows.js +0 -104
  242. package/dist/integrity/slopnators/sl_unkept_rows.ts +0 -111
  243. package/dist/integrity/slopnators/sl_unseamed_reach.js +0 -73
  244. package/dist/integrity/slopnators/sl_unseamed_reach.ts +0 -79
  245. package/dist/operations/cost.d.ts +0 -14
  246. package/dist/operations/cost.js +0 -37
  247. package/dist/operations/cost.ts +0 -58
  248. package/dist/operations/errors/errors.d.ts +0 -52
  249. package/dist/operations/http.js +0 -124
  250. package/dist/operations/http.ts +0 -169
  251. package/dist/operations/memory.d.ts +0 -104
  252. package/dist/operations/memory.js +0 -365
  253. package/dist/operations/memory.ts +0 -470
  254. package/dist/operations/metering.d.ts +0 -55
  255. package/dist/operations/metering.js +0 -247
  256. package/dist/operations/metering.ts +0 -328
  257. package/dist/operations/queue-policy.d.ts +0 -3
  258. package/dist/operations/queue-policy.js +0 -8
  259. package/dist/operations/queue-policy.ts +0 -10
  260. package/dist/operations/seams.d.ts +0 -239
  261. package/dist/operations/seams.js +0 -45
  262. package/dist/operations/seams.ts +0 -364
  263. package/dist/operations/store.d.ts +0 -14
  264. package/dist/operations/store.js +0 -1
  265. package/dist/operations/store.ts +0 -29
  266. /package/dist/{operations → core}/errors/classes.d.ts +0 -0
  267. /package/dist/{operations → core}/errors/classes.js +0 -0
  268. /package/dist/{operations → core}/errors/classes.ts +0 -0
  269. /package/dist/integrity/slopnators/{sl_unkept_rows.d.ts → sl_door_in_a_node.d.ts} +0 -0
  270. /package/dist/integrity/slopnators/{sl_unseamed_reach.d.ts → sl_outside_an_operation.d.ts} +0 -0
@@ -1,16 +1,17 @@
1
- import { existsSync, readFileSync, writeFileSync } from "../../core/files.js";
1
+ import { existsSync, readFileSync, writeFileSync } from '../../integrations/disk.js';
2
2
  import { dirname, relative, resolve } from 'node:path';
3
3
  import { argv, stdout } from 'node:process';
4
4
  import { fileURLToPath } from 'node:url';
5
- import { ROOT, descend, under } from "../../core/descend.js";
6
- import { runChecker } from "../../core/finding.js";
7
- import { exit, invoked } from "../../core/invoked.js";
8
- import { SUPPLIES, supplies } from "../seams.js";
5
+ import ts from 'typescript';
6
+ import { ROOT, descend, under } from '../../core/descend.js';
7
+ import { runChecker } from '../../core/finding.js';
8
+ import { exit, invoked } from '../../core/invoked.js';
9
+ import { PREFIXES } from '../../core/thread.js';
9
10
  const COMPILES = 'thread compile';
10
11
  const OPERATIONS_ID = 'operations';
11
12
  const NODES_ID = 'nodes';
12
13
  const NAMED = /\btype:\s*'((?:[^'\\]|\\.)*)'/;
13
- import { quoted } from "../../core/rows.js";
14
+ import { quoted } from '../../core/rows.js';
14
15
  export { quoted };
15
16
  const slash = (one) => one.split('\\').join('/');
16
17
  export const beside = (from, to) => {
@@ -30,6 +31,7 @@ const FRESH = {
30
31
  const SHIPPED = new Set(Object.keys(FRESH));
31
32
  const isType = (pass, id) => under(pass, id, NODES_ID);
32
33
  const isOurs = (pass, id) => under(pass, id, OPERATIONS_ID);
34
+ const isOperation = (pass, id) => Object.values(PREFIXES).some((one) => id.startsWith(one)) && isOurs(pass, id);
33
35
  export function types(pass) {
34
36
  const found = new Set(SHIPPED);
35
37
  const named = new Set();
@@ -39,7 +41,7 @@ export function types(pass) {
39
41
  named.add(one.row.id);
40
42
  }
41
43
  for (const one of pass.coined.keys()) {
42
- if (one !== NODES_ID && !supplies(one) && !named.has(one) && under(pass, one, NODES_ID))
44
+ if (one !== NODES_ID && !named.has(one) && under(pass, one, NODES_ID))
43
45
  found.add(one);
44
46
  }
45
47
  return found;
@@ -49,6 +51,7 @@ export function builtFrom(row, known) {
49
51
  return found.length === 1 ? found[0] : undefined;
50
52
  }
51
53
  const reaches = (pass, one) => pass.stated.has(one) && !under(pass, one, NODES_ID);
54
+ const waiting = (held) => held !== undefined && ts.isNumericLiteral(held) ? Number(held.text.replace(/_/g, '')) : undefined;
52
55
  export function operations(pass, known) {
53
56
  const found = new Map();
54
57
  const held = (id) => {
@@ -62,15 +65,16 @@ export function operations(pass, known) {
62
65
  takes: [],
63
66
  body: undefined,
64
67
  wants: new Set(stated?.takes ?? []),
65
- requires: asked.filter((one) => supplies(one) || reaches(pass, one)),
66
- stray: asked.filter((one) => !supplies(one) && !reaches(pass, one)),
68
+ requires: asked.filter((one) => reaches(pass, one)),
69
+ stray: asked.filter((one) => !reaches(pass, one)),
70
+ timeout: waiting(stated?.fields.get('timeout')),
67
71
  };
68
72
  found.set(id, made);
69
73
  return made;
70
74
  };
71
75
  for (const [at, threadings] of pass.threaded) {
72
76
  for (const one of threadings) {
73
- if (one.by === OPERATIONS_ID || !isOurs(pass, one.by) || isType(pass, one.by))
77
+ if (!isOperation(pass, one.by))
74
78
  continue;
75
79
  if (one.list === 'derives' && one.row.by === COMPILES)
76
80
  held(one.by).body = at;
@@ -89,14 +93,14 @@ export const problem = (id, one) => {
89
93
  if (one.nodes.length === 0)
90
94
  return `\`${id}\` is an operation that seeds no nodes, and an operation is its nodes`;
91
95
  if (one.takes.length === 0) {
92
- return (`\`${id}\` names nothing it takes, and an operation takes one thing — its input, its estimate and the row it touches. ` +
96
+ return (`\`${id}\` names nothing it takes, and an operation takes one thing — its input and the row it touches. ` +
93
97
  `Write \`takes: ['${id}_takes']\` on its row beside the file that row seeds`);
94
98
  }
95
99
  if (one.takes.length > 1)
96
100
  return `\`${id}\` takes ${String(one.takes.length)} things, and it takes one`;
97
101
  if (one.stray.length > 0) {
98
102
  return (`\`${id}\` requires ${one.stray.join(', ')}, and dispatch can supply neither a capability nor a target by that ` +
99
- `name. What an operation may require is ${SUPPLIES.join(', ')}, or a target this app declares — ` +
103
+ `name. What an operation may require is a target this app declares — ` +
100
104
  'declare one with `thread create target`');
101
105
  }
102
106
  return undefined;
@@ -104,12 +108,10 @@ export const problem = (id, one) => {
104
108
  const takesFile = (at) => [
105
109
  "import { z } from 'zod'",
106
110
  '',
107
- `import type { Row } from ${quoted(runtime(at, '../seams.ts'))}`,
111
+ 'import type { Row } from ' + quoted(runtime(at, '../../integrations/target.ts')),
108
112
  '',
109
113
  'export const input = z.unknown()',
110
114
  '',
111
- 'export const estimate = 1',
112
- '',
113
115
  'export const resource = (): Row | null => null',
114
116
  '',
115
117
  'export type In = z.infer<typeof input>',
@@ -147,9 +149,9 @@ export function nodeText(at, name, takes, from, requires, body) {
147
149
  const nodeFile = (at, row, takes, from, requires, body) => nodeText(at, row.id ?? '', takes, from, requires, body);
148
150
  const ownType = (at, id) => [
149
151
  `import type { Held } from ${quoted(runtime(at, '../nodes.ts'))}`,
150
- `import type { Handed, Supplied } from ${quoted(runtime(at, '../seams.ts'))}`,
152
+ 'import type { Opened } from ' + quoted(runtime(at, '../operations.ts')),
151
153
  '',
152
- 'export const node = <I, R extends Supplied = Supplied>(block: (hands: Handed<R>, held: Held<I>) => Promise<unknown>) => ({',
154
+ 'export const node = <I, R extends string = never>(block: (opened: Opened<R>, held: Held<I>) => Promise<unknown>) => ({',
153
155
  ` type: ${quoted(id)} as const,`,
154
156
  '})',
155
157
  '',
@@ -1,13 +1,14 @@
1
- import { existsSync, readFileSync, writeFileSync } from '../../core/files.ts'
1
+ import { existsSync, readFileSync, writeFileSync } from '../../integrations/disk.ts'
2
2
  import { dirname, relative, resolve } from 'node:path'
3
3
  import { argv, stdout } from 'node:process'
4
4
  import { fileURLToPath } from 'node:url'
5
5
 
6
+ import ts from 'typescript'
7
+
6
8
  import { ROOT, descend, under, type Pass, type Row } from '../../core/descend.ts'
7
9
  import { runChecker, type Finding } from '../../core/finding.ts'
8
10
  import { exit, invoked } from '../../core/invoked.ts'
9
-
10
- import { SUPPLIES, supplies, type Supplied } from '../seams.ts'
11
+ import { PREFIXES } from '../../core/thread.ts'
11
12
 
12
13
  const COMPILES = 'thread compile'
13
14
 
@@ -47,6 +48,9 @@ const isType = (pass: Pass, id: string): boolean => under(pass, id, NODES_ID)
47
48
 
48
49
  const isOurs = (pass: Pass, id: string): boolean => under(pass, id, OPERATIONS_ID)
49
50
 
51
+ const isOperation = (pass: Pass, id: string): boolean =>
52
+ Object.values(PREFIXES).some((one) => id.startsWith(one)) && isOurs(pass, id)
53
+
50
54
  export function types(pass: Pass): ReadonlySet<string> {
51
55
  const found = new Set<string>(SHIPPED)
52
56
  const named = new Set<string>()
@@ -55,7 +59,7 @@ export function types(pass: Pass): ReadonlySet<string> {
55
59
  }
56
60
 
57
61
  for (const one of pass.coined.keys()) {
58
- if (one !== NODES_ID && !supplies(one) && !named.has(one) && under(pass, one, NODES_ID)) found.add(one)
62
+ if (one !== NODES_ID && !named.has(one) && under(pass, one, NODES_ID)) found.add(one)
59
63
  }
60
64
  return found
61
65
  }
@@ -72,10 +76,15 @@ interface Woven {
72
76
 
73
77
  readonly requires: readonly string[]
74
78
  readonly stray: readonly string[]
79
+
80
+ readonly timeout?: number
75
81
  }
76
82
 
77
83
  const reaches = (pass: Pass, one: string): boolean => pass.stated.has(one) && !under(pass, one, NODES_ID)
78
84
 
85
+ const waiting = (held: ts.Expression | undefined): number | undefined =>
86
+ held !== undefined && ts.isNumericLiteral(held) ? Number(held.text.replace(/_/g, '')) : undefined
87
+
79
88
  export function operations(pass: Pass, known: ReadonlySet<string>): ReadonlyMap<string, Woven> {
80
89
  const found = new Map<string, Woven & { nodes: Row[]; takes: Row[]; body?: string; wants: ReadonlySet<string> }>()
81
90
  const held = (id: string): { nodes: Row[]; takes: Row[]; body?: string; wants: ReadonlySet<string> } => {
@@ -88,8 +97,9 @@ export function operations(pass: Pass, known: ReadonlySet<string>): ReadonlyMap<
88
97
  takes: [] as Row[],
89
98
  body: undefined as string | undefined,
90
99
  wants: new Set(stated?.takes ?? []),
91
- requires: asked.filter((one) => supplies(one) || reaches(pass, one)),
92
- stray: asked.filter((one) => !supplies(one) && !reaches(pass, one)),
100
+ requires: asked.filter((one) => reaches(pass, one)),
101
+ stray: asked.filter((one) => !reaches(pass, one)),
102
+ timeout: waiting(stated?.fields.get('timeout')),
93
103
  }
94
104
  found.set(id, made)
95
105
  return made
@@ -97,7 +107,7 @@ export function operations(pass: Pass, known: ReadonlySet<string>): ReadonlyMap<
97
107
 
98
108
  for (const [at, threadings] of pass.threaded) {
99
109
  for (const one of threadings) {
100
- if (one.by === OPERATIONS_ID || !isOurs(pass, one.by) || isType(pass, one.by)) continue
110
+ if (!isOperation(pass, one.by)) continue
101
111
 
102
112
  if (one.list === 'derives' && one.row.by === COMPILES) held(one.by).body = at
103
113
  else if (one.list === 'seeds') {
@@ -114,7 +124,7 @@ export const problem = (id: string, one: Woven): string | undefined => {
114
124
  if (one.nodes.length === 0) return `\`${id}\` is an operation that seeds no nodes, and an operation is its nodes`
115
125
  if (one.takes.length === 0) {
116
126
  return (
117
- `\`${id}\` names nothing it takes, and an operation takes one thing — its input, its estimate and the row it touches. ` +
127
+ `\`${id}\` names nothing it takes, and an operation takes one thing — its input and the row it touches. ` +
118
128
  `Write \`takes: ['${id}_takes']\` on its row beside the file that row seeds`
119
129
  )
120
130
  }
@@ -122,7 +132,7 @@ export const problem = (id: string, one: Woven): string | undefined => {
122
132
  if (one.stray.length > 0) {
123
133
  return (
124
134
  `\`${id}\` requires ${one.stray.join(', ')}, and dispatch can supply neither a capability nor a target by that ` +
125
- `name. What an operation may require is ${SUPPLIES.join(', ')}, or a target this app declares — ` +
135
+ `name. What an operation may require is a target this app declares — ` +
126
136
  'declare one with `thread create target`'
127
137
  )
128
138
  }
@@ -133,12 +143,10 @@ const takesFile = (at: string): string =>
133
143
  [
134
144
  "import { z } from 'zod'",
135
145
  '',
136
- `import type { Row } from ${quoted(runtime(at, '../seams.ts'))}`,
146
+ 'import type { Row } from ' + quoted(runtime(at, '../../integrations/target.ts')),
137
147
  '',
138
148
  'export const input = z.unknown()',
139
149
  '',
140
- 'export const estimate = 1',
141
- '',
142
150
  'export const resource = (): Row | null => null',
143
151
  '',
144
152
  'export type In = z.infer<typeof input>',
@@ -191,9 +199,9 @@ const nodeFile = (at: string, row: Row, takes: string, from: string, requires: r
191
199
  const ownType = (at: string, id: string): string =>
192
200
  [
193
201
  `import type { Held } from ${quoted(runtime(at, '../nodes.ts'))}`,
194
- `import type { Handed, Supplied } from ${quoted(runtime(at, '../seams.ts'))}`,
202
+ 'import type { Opened } from ' + quoted(runtime(at, '../operations.ts')),
195
203
  '',
196
- 'export const node = <I, R extends Supplied = Supplied>(block: (hands: Handed<R>, held: Held<I>) => Promise<unknown>) => ({',
204
+ 'export const node = <I, R extends string = never>(block: (opened: Opened<R>, held: Held<I>) => Promise<unknown>) => ({',
197
205
  ` type: ${quoted(id)} as const,`,
198
206
  '})',
199
207
  '',
@@ -1,8 +1,9 @@
1
1
  import { type Actor } from '../../core/actor.ts';
2
- import { type ScriptedAnswer } from '../http.ts';
2
+ import { type Call, type Row, type Target } from '../../integrations/target.ts';
3
+ import { type Scripted } from '../../integrations/https.ts';
3
4
  import { TRIALS_AT, type Operation } from '../operations.ts';
4
5
  import type { Result } from '../result.ts';
5
- import type { Row, Seams } from '../seams.ts';
6
+ import type { Vault } from '../../integrations/secret.ts';
6
7
  export { TRIALS_AT };
7
8
  export interface Scenario {
8
9
  readonly id: string;
@@ -13,8 +14,9 @@ export interface Scenario {
13
14
  readonly entity: string;
14
15
  readonly row: Row;
15
16
  }[];
16
- readonly answers?: Readonly<Record<string, ScriptedAnswer>>;
17
+ readonly answers?: Readonly<Record<string, Scripted>>;
17
18
  readonly views?: Readonly<Record<string, readonly unknown[]>>;
19
+ readonly files?: Readonly<Record<string, string>>;
18
20
  }
19
21
  export interface Trial {
20
22
  readonly operation: string;
@@ -23,7 +25,6 @@ export interface Trial {
23
25
  readonly measured: boolean;
24
26
  readonly result: unknown;
25
27
  readonly kept: unknown;
26
- readonly observed: unknown;
27
28
  readonly called: unknown;
28
29
  }
29
30
  export interface Outcome {
@@ -40,10 +41,7 @@ export interface Run {
40
41
  readonly failed: number;
41
42
  readonly measured: number;
42
43
  }
43
- export declare function runScenario(one: Operation<unknown>, scenario: Scenario, dispatch: (operation: Operation<unknown>, input: unknown, actor: Actor, seams: Seams) => Promise<Result<unknown>>, stood?: Readonly<Record<string, string>>, gate?: NonNullable<Seams['permits']>, known?: {
44
- readonly entities: ReadonlyMap<string, ReadonlySet<string>>;
45
- readonly keys: ReadonlyMap<string, string>;
46
- }): Promise<Trial>;
44
+ export declare function runScenario(one: Operation<unknown>, scenario: Scenario, dispatch: (operation: Operation<unknown>, input: unknown, actor: Actor, opened: Readonly<Record<string, unknown>>, opening: Vault) => Promise<Result<unknown>>, stood?: Readonly<Record<string, string>>, targets?: readonly Target[], calls?: readonly Call[]): Promise<Trial | string>;
47
45
  export declare function scenariosFor(one: Operation<unknown>, takes: string, granted?: readonly string[]): Promise<readonly Scenario[] | string>;
48
46
  export declare function compared(was: readonly Trial[], now: readonly Trial[]): Run;
49
47
  export declare function unproven(run: Run): readonly string[];
@@ -6,21 +6,21 @@ var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExte
6
6
  }
7
7
  return path;
8
8
  };
9
- import { existsSync, readFileSync, writeFileSync } from "../../core/files.js";
9
+ import { existsSync, readFileSync, writeFileSync } from '../../integrations/disk.js';
10
10
  import { resolve } from 'node:path';
11
11
  import { argv, stdout } from 'node:process';
12
12
  import { pathToFileURL } from 'node:url';
13
- import { ANONYMOUS } from "../../core/actor.js";
14
- import { ROOT, TREE, descend } from "../../core/descend.js";
15
- import { exit, invoked } from "../../core/invoked.js";
16
- import { recordingHttp } from "../http.js";
17
- import { inMemorySeams } from "../memory.js";
18
- import { TRIALS_AT } from "../operations.js";
19
- import { PERMITS_AT } from "../access.js";
20
- import { operations, types } from "./seed.js";
13
+ import { ANONYMOUS } from '../../core/actor.js';
14
+ import { ROOT, descend } from '../../core/descend.js';
15
+ import { exit, invoked } from '../../core/invoked.js';
16
+ import { reachOf, standsHere } from '../../integrations/target.js';
17
+ import { recordingHttp } from '../../integrations/https.js';
18
+ import { standing } from '../../integrations/pglite.js';
19
+ import { TRIALS_AT } from '../operations.js';
20
+ import { PERMITS_AT } from '../access.js';
21
+ import { operations, types } from './seed.js';
21
22
  export { TRIALS_AT };
22
23
  const DISPATCH_AT = 'operations/_dispatch.ts';
23
- const ENTITIES_AT = `${TREE}entities.ts`;
24
24
  const ACCESS_AT = '_threads/access.ts';
25
25
  const WHEN = 1_767_225_600_000;
26
26
  const compare = (a, b) => (a < b ? -1 : a > b ? 1 : 0);
@@ -51,68 +51,191 @@ const asking = (scenario) => ({
51
51
  ...(scenario.actor ?? ANONYMOUS),
52
52
  run: 'trial',
53
53
  replay: `trial_${scenario.id}`,
54
+ moment: new Date(WHEN).toISOString(),
54
55
  });
55
- function seamsFor(one, scenario, stood, gate, known) {
56
- const made = inMemorySeams({
57
- secrets: stood,
58
- ...(scenario.views === undefined ? {} : { views: scenario.views }),
59
- ...(known === undefined ? {} : { entities: known.entities, keys: known.keys }),
60
- });
61
- const found = made.seams;
62
- if (gate !== undefined)
63
- found['permits'] = gate;
64
- found['clock'] = { now: () => WHEN };
65
- let drawn = 0;
66
- found['random'] = {
67
- next: () => {
68
- drawn += 1;
69
- return drawn / 100;
56
+ function programmed(word, names) {
57
+ const made = {};
58
+ for (const order of names) {
59
+ made[order] = () => {
60
+ throw new Error(word + ' was asked to run ' + order + ', and this scenario says nothing about what that did. A trial stands a ' +
61
+ 'disk up out of the files its scenario states, and there is nowhere yet to state what a program or a package answered, so ' +
62
+ 'a scenario cannot give either an order. This says that rather than inventing a result for one.');
63
+ };
64
+ }
65
+ return made;
66
+ }
67
+ function packaged(word, names, script, into) {
68
+ const spent = new Map();
69
+ const made = {};
70
+ for (const name of names) {
71
+ made[name] = async (given) => {
72
+ const key = word + ' ' + name;
73
+ into.push({ target: word, method: 'CALL', path: name, headers: {}, body: JSON.stringify(stable(given ?? null)) });
74
+ const held = script[key];
75
+ if (held === undefined) {
76
+ throw new Error('this scenario scripts no answer for "' + key + '", so nothing here knows what ' + word + ' would hand back. Add ' +
77
+ 'that key to the scenario\'s answers, with what the call returns written as JSON in its body.');
78
+ }
79
+ const each = Array.isArray(held) ? held : undefined;
80
+ const at = spent.get(key) ?? 0;
81
+ spent.set(key, at + 1);
82
+ const answer = each === undefined ? held : each[at];
83
+ if (answer === undefined) {
84
+ throw new Error('this scenario scripts ' + String(each?.length ?? 0) + ' answer(s) for "' + key + '" and it has now been called ' +
85
+ String(at + 1) + ' time(s). A node that calls the same thing again needs one answer per call, in the order they come back.');
86
+ }
87
+ if (answer.status !== 200)
88
+ throw new Error(answer.body ?? word + ' ' + name + ' failed with ' + String(answer.status));
89
+ return answer.body === undefined ? undefined : JSON.parse(answer.body);
90
+ };
91
+ }
92
+ return made;
93
+ }
94
+ function disked(files) {
95
+ const held = new Map(Object.entries(files));
96
+ const under = (at) => [...held.keys()].some((one) => one.startsWith(at + '/'));
97
+ const gone = (at) => new Error(at + ' is not in this scenario, and a trial answers a file order against the files its scenario states');
98
+ return {
99
+ kind: (at) => (held.has(at) ? 'file' : under(at) ? 'folder' : 'none'),
100
+ read: (at) => {
101
+ const said = held.get(at);
102
+ if (said === undefined)
103
+ throw gone(at);
104
+ return said;
105
+ },
106
+ write: (at, said) => {
107
+ held.set(at, said);
108
+ },
109
+ list: (at) => {
110
+ const inside = new Map();
111
+ for (const one of held.keys()) {
112
+ if (!one.startsWith(at + '/'))
113
+ continue;
114
+ const rest = one.slice(at.length + 1);
115
+ const cut = rest.indexOf('/');
116
+ const name = cut === -1 ? rest : rest.slice(0, cut);
117
+ inside.set(name, { name, kind: cut === -1 ? 'file' : 'folder' });
118
+ }
119
+ return [...inside.values()];
120
+ },
121
+ stat: (at) => {
122
+ const said = held.get(at);
123
+ if (said === undefined)
124
+ throw gone(at);
125
+ return { size: said.length, modified: 0 };
70
126
  },
71
- uuid: () => {
72
- drawn += 1;
73
- return `00000000-0000-4000-8000-${String(drawn).padStart(12, '0')}`;
127
+ remove: (at) => {
128
+ held.delete(at);
74
129
  },
130
+ real: (at) => at,
75
131
  };
132
+ }
133
+ function vaulting(stood) {
134
+ return {
135
+ async resolve(refs) {
136
+ const found = {};
137
+ for (const ref of refs) {
138
+ const value = stood[ref];
139
+ if (value === undefined) {
140
+ throw new Error(ref + ' is not a secret this app declares, so a trial has nothing to stand in for it. Declare it with ' +
141
+ '`thread create secret`, or stop requiring the target that asks for it.');
142
+ }
143
+ found[ref] = value;
144
+ }
145
+ return found;
146
+ },
147
+ };
148
+ }
149
+ async function standingStore(row) {
150
+ const said = row.derives?.find((each) => each.id === row.id + '_store')?.at;
151
+ if (said === undefined) {
152
+ return row.id + ' keeps rows and its row derives no store, so a trial has nothing to stand up. Run `thread schema`';
153
+ }
154
+ const at = resolve(ROOT, said);
155
+ if (!existsSync(at))
156
+ return said + ' is not there, so ' + row.id + ' cannot be stood up. Run `thread schema`';
157
+ const made = (await import(__rewriteRelativeImportExtension(pathToFileURL(at).href)));
158
+ const held = made.kept;
159
+ if (held === undefined) {
160
+ return said + ' says nothing about what ' + row.id + ' keeps or with what schema. Run `thread schema`';
161
+ }
162
+ return standing(held);
163
+ }
164
+ async function openedFor(one, scenario, stood, targets = [], calls = []) {
76
165
  const recorder = recordingHttp(scenario.answers ?? {});
77
- found['http'] = recorder;
78
- return { seams: made.seams, sink: made.sink, kept: () => made.store, calls: () => recorder.calls };
166
+ const packages = [];
167
+ const opening = vaulting(stood);
168
+ const opened = {};
169
+ const stores = [];
170
+ const answering = (row) => calls.filter((each) => each.of === row.id && row.takes.includes(each.id)).map((each) => each.id.replace(/^cl_/, ''));
171
+ for (const word of one.requires) {
172
+ const row = targets.find((each) => each.id === word);
173
+ if (row === undefined || !standsHere(row, targets))
174
+ continue;
175
+ const reach = reachOf(row.id);
176
+ if (reach === 'disk')
177
+ opened[word] = disked(scenario.files ?? {});
178
+ else if (reach === 'cli')
179
+ opened[word] = programmed(word, answering(row));
180
+ else if (reach === 'https')
181
+ opened[word] = recorder;
182
+ else if (reach === 'sdk')
183
+ opened[word] = packaged(word, answering(row), scenario.answers ?? {}, packages);
184
+ else {
185
+ const held = await standingStore(row);
186
+ if (typeof held === 'string')
187
+ return held;
188
+ opened[word] = held.store;
189
+ stores.push(held);
190
+ }
191
+ }
192
+ return { opened, opening, stores, calls: () => [...recorder['calls'], ...packages] };
79
193
  }
80
- function entered(sink) {
81
- return sink.logs.some((one) => {
82
- const data = one.data;
83
- return data !== undefined && 'observability' in data;
84
- });
194
+ const BEFORE = new Set(['denied', 'invalid']);
195
+ function entered(result) {
196
+ if (typeof result !== 'object' || result === null)
197
+ return false;
198
+ const held = result;
199
+ if (held.ok === true)
200
+ return true;
201
+ return held.ok === false && !BEFORE.has(String(held.code));
85
202
  }
86
203
  function denied(result) {
87
204
  if (typeof result !== 'object' || result === null)
88
205
  return false;
89
206
  const held = result;
90
- return held.ok === false && held.code === 'denied';
207
+ return held.ok === false && BEFORE.has(String(held.code));
91
208
  }
92
- export async function runScenario(one, scenario, dispatch, stood = {}, gate, known) {
93
- const made = seamsFor(one, scenario, stood, gate, known);
209
+ export async function runScenario(one, scenario, dispatch, stood = {}, targets = [], calls = []) {
210
+ const made = await openedFor(one, scenario, stood, targets, calls);
211
+ if (typeof made === 'string')
212
+ return made;
94
213
  const actor = asking(scenario);
95
- for (const seeded of scenario.rows ?? []) {
96
- const held = made.seams.store;
97
- if (held === undefined)
98
- throw new Error(String(seeded.entity) + " cannot be seeded: these seams keep no rows");
99
- await held.insert(actor, seeded.entity, seeded.row);
214
+ for (const each of scenario.rows ?? []) {
215
+ const held = made.stores.find((store) => store.keeps.includes(each.entity));
216
+ if (held === undefined) {
217
+ return ('`' + one.id + '` is tried with a row of "' + each.entity + '" and no target it requires keeps that entity, ' +
218
+ 'so there is nowhere to put it. Require the target that keeps it, or take that row off the scenario');
219
+ }
220
+ await held.seed(actor, [each]);
100
221
  }
101
222
  let result;
102
223
  try {
103
- result = await dispatch(one, scenario.input, actor, made.seams);
224
+ result = await dispatch(one, scenario['input'], actor, made.opened, made.opening);
104
225
  }
105
226
  catch (thrown) {
106
227
  result = { threw: thrown instanceof Error ? thrown.message : String(thrown) };
107
228
  }
229
+ const kept = {};
230
+ for (const held of made.stores)
231
+ Object.assign(kept, await held.taken(actor));
108
232
  return {
109
233
  operation: one.id,
110
234
  scenario: scenario.id,
111
235
  refused: denied(result),
112
- measured: entered(made.sink),
236
+ measured: entered(result),
113
237
  result,
114
- kept: made.kept(),
115
- observed: made.sink,
238
+ kept,
116
239
  called: made.calls(),
117
240
  };
118
241
  }
@@ -157,14 +280,14 @@ export function compared(was, now) {
157
280
  `measured and no refusal was proven either. What it answered was ${JSON.stringify(one.result)}`);
158
281
  }
159
282
  if (one.refused && one.measured) {
160
- failures.push('this app\'s own gate denied the caller and the run REACHED THE OPERATION anyway — dispatch let a ' +
161
- 'denied request through to the compiled function, which is the gate not holding rather than a trial drifting');
283
+ failures.push('this run was refused before the operation could start and REACHED THE OPERATION anyway — dispatch let a ' +
284
+ 'refused request through to the compiled function, which is the gate not holding rather than a trial drifting');
162
285
  }
163
286
  if (older === undefined) {
164
287
  failures.push('no trial holds this scenario. Run `thread trials` and commit the result');
165
288
  }
166
289
  else {
167
- for (const part of ['result', 'kept', 'observed']) {
290
+ for (const part of ['result', 'kept', 'called']) {
168
291
  const then = printed(older[part]);
169
292
  const said = printed(one[part]);
170
293
  if (then === said)
@@ -219,7 +342,7 @@ export function unproven(run) {
219
342
  const how = [
220
343
  denied === 0
221
344
  ? ''
222
- : `${String(denied)} were denied by this app's own gate, which proves the gate turns somebody away and nothing whatever about the operation`,
345
+ : `${String(denied)} were refused before the operation could start — by the gate, or because what they were given is not what it takes — which proves the refusal and nothing whatever about the operation`,
223
346
  neither === 0 ? '' : `${String(neither)} neither reached it nor were denied`,
224
347
  ].filter((one) => one !== '');
225
348
  said.push(`\`${operation}\` is UNPROVEN — ${String(outcomes.length)} scenario(s) ran and not one of them reached it: ${how.join(', and ')}. ` +
@@ -227,7 +350,7 @@ export function unproven(run) {
227
350
  }
228
351
  return said;
229
352
  }
230
- async function standing() {
353
+ async function standIns() {
231
354
  const at = resolve(ROOT, '_threads/secrets.ts');
232
355
  if (!existsSync(at))
233
356
  return {};
@@ -243,10 +366,24 @@ async function standing() {
243
366
  }
244
367
  const found = {};
245
368
  for (const one of held) {
246
- found[one.id] = one.sends === undefined ? `https://${one.id.replace(/_/g, '-')}.invalid` : `${one.id}-stood-in-for`;
369
+ found[one.id] = one.sends === undefined && one.signs === undefined ? `https://${one.id.replace(/_/g, '-')}.invalid` : `${one.id}-stood-in-for`;
247
370
  }
248
371
  return found;
249
372
  }
373
+ async function reaching() {
374
+ const at = resolve(ROOT, '_threads/targets.ts');
375
+ if (!existsSync(at))
376
+ return { targets: [], calls: [] };
377
+ try {
378
+ const taken = (await import(__rewriteRelativeImportExtension(pathToFileURL(at).href)));
379
+ return { targets: taken.TARGETS ?? [], calls: taken.CALLS ?? [] };
380
+ }
381
+ catch (thrown) {
382
+ return (`_threads/targets.ts is there and could not be read: ${thrown instanceof Error ? thrown.message : String(thrown)}. ` +
383
+ 'Which disk a trial hands an operation is read from that sheet, so every trial would run with no disk at all and ' +
384
+ 'the failures would name the operation instead of the sheet');
385
+ }
386
+ }
250
387
  export async function record() {
251
388
  const pass = descend();
252
389
  const known = types(pass);
@@ -255,9 +392,13 @@ export async function record() {
255
392
  const declared = [...operations(pass, known)].filter(([, one]) => one.body !== undefined);
256
393
  if (declared.length === 0)
257
394
  return { trials: [], refusals: [] };
258
- const stood = await standing();
395
+ const stood = await standIns();
259
396
  if (typeof stood === 'string')
260
397
  return { trials: [], refusals: [stood] };
398
+ const reached = await reaching();
399
+ if (typeof reached === 'string')
400
+ return { trials: [], refusals: [reached] };
401
+ const { targets, calls } = reached;
261
402
  const at = resolve(ROOT, DISPATCH_AT);
262
403
  if (!existsSync(at)) {
263
404
  return {
@@ -272,18 +413,6 @@ export async function record() {
272
413
  if (dispatch === undefined) {
273
414
  return { trials: [], refusals: [`${DISPATCH_AT} exports no \`dispatch\`, so nothing here can run an operation`] };
274
415
  }
275
- const kept = resolve(ROOT, ENTITIES_AT);
276
- const entities = new Map();
277
- const keys = new Map();
278
- if (existsSync(kept)) {
279
- const sheet = (await import(__rewriteRelativeImportExtension(pathToFileURL(kept).href)));
280
- for (const one of sheet.ENTITIES ?? []) {
281
- entities.set(one.id, new Set([...(one.holds ?? []).map((each) => each.id), ...(one.links ?? []).map((each) => `${each.id}_id`)]));
282
- const named = (one.must ?? []).flatMap((each) => (each.unique?.length === 1 ? [each.unique[0]] : []));
283
- if (named.length === 1)
284
- keys.set(one.id, named[0]);
285
- }
286
- }
287
416
  const permitted = resolve(ROOT, ACCESS_AT);
288
417
  const may = new Map();
289
418
  if (existsSync(permitted)) {
@@ -304,9 +433,6 @@ export async function record() {
304
433
  refusals: [`${PERMITS_AT} exports no \`permits\`, so a trial cannot be answered by the gate this app derives`],
305
434
  };
306
435
  }
307
- const gate = permits === undefined
308
- ? undefined
309
- : { check: (actor, action, resource) => Promise.resolve(permits(actor, action, resource)) };
310
436
  for (const [id, one] of [...declared].sort((a, b) => compare(a[0], b[0]))) {
311
437
  if (one.body === undefined)
312
438
  continue;
@@ -332,7 +458,11 @@ export async function record() {
332
458
  continue;
333
459
  }
334
460
  for (const scenario of scenarios) {
335
- trials.push(await runScenario(operation, scenario, dispatch, stood, gate, { entities, keys }));
461
+ const ran = await runScenario(operation, scenario, dispatch, stood, targets, calls);
462
+ if (typeof ran === 'string')
463
+ refusals.push(ran);
464
+ else
465
+ trials.push(ran);
336
466
  }
337
467
  }
338
468
  return { trials, refusals };