@prestack/thread 0.1.3 → 0.1.4

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 (265) hide show
  1. package/CHANGELOG.md +105 -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 +37 -65
  7. package/dist/core/attach.ts +32 -76
  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 +89 -21
  14. package/dist/core/descend.ts +86 -17
  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 +0 -1
  27. package/dist/core/init.js +48 -4
  28. package/dist/core/init.ts +54 -4
  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/thread.d.ts +3 -3
  38. package/dist/core/tree.d.ts +4 -1
  39. package/dist/core/tree.js +26 -6
  40. package/dist/core/tree.ts +29 -6
  41. package/dist/core/types.d.ts +3 -3
  42. package/dist/core/types.js +3 -1
  43. package/dist/core/types.ts +2 -1
  44. package/dist/derivation/attach.js +1 -1
  45. package/dist/derivation/generators/build.js +6 -6
  46. package/dist/derivation/generators/build.ts +1 -1
  47. package/dist/derivation/generators/check.js +1 -1
  48. package/dist/derivation/generators/determinism.js +1 -1
  49. package/dist/derivation/generators/release.js +60 -47
  50. package/dist/derivation/generators/release.ts +66 -43
  51. package/dist/derivation/release.d.ts +7 -3
  52. package/dist/derivation/release.js +14 -1
  53. package/dist/derivation/release.ts +22 -2
  54. package/dist/integrations/attach.d.ts +4 -0
  55. package/dist/integrations/attach.js +33 -54
  56. package/dist/integrations/attach.ts +50 -57
  57. package/dist/integrations/cli.d.ts +37 -0
  58. package/dist/integrations/cli.js +440 -0
  59. package/dist/integrations/cli.ts +543 -0
  60. package/dist/{core/files.d.ts → integrations/disk.d.ts} +16 -24
  61. package/dist/integrations/disk.js +435 -0
  62. package/dist/integrations/disk.ts +465 -0
  63. package/dist/integrations/generators/read.js +6 -6
  64. package/dist/integrations/generators/read.ts +4 -4
  65. package/dist/integrations/generators/vault.js +10 -11
  66. package/dist/integrations/generators/vault.ts +4 -5
  67. package/dist/{operations/http.d.ts → integrations/https.d.ts} +10 -6
  68. package/dist/integrations/https.js +170 -0
  69. package/dist/integrations/https.ts +216 -0
  70. package/dist/integrations/pglite.d.ts +26 -0
  71. package/dist/integrations/pglite.js +143 -0
  72. package/dist/integrations/pglite.ts +206 -0
  73. package/dist/integrations/secret.d.ts +8 -2
  74. package/dist/integrations/secret.js +3 -5
  75. package/dist/integrations/secret.ts +10 -6
  76. package/dist/integrations/target.d.ts +155 -14
  77. package/dist/integrations/target.js +171 -50
  78. package/dist/integrations/target.ts +377 -61
  79. package/dist/integrations/vault.d.ts +1 -0
  80. package/dist/integrations/vault.js +20 -5
  81. package/dist/integrations/vault.ts +17 -5
  82. package/dist/integrity/attach.js +3 -73
  83. package/dist/integrity/attach.ts +1 -71
  84. package/dist/integrity/check-threads.js +5 -5
  85. package/dist/integrity/check-threads.ts +2 -2
  86. package/dist/integrity/constraint.d.ts +0 -1
  87. package/dist/integrity/constraint.js +267 -366
  88. package/dist/integrity/constraint.ts +267 -375
  89. package/dist/integrity/generators/compiler.js +14 -17
  90. package/dist/integrity/generators/compiler.ts +10 -15
  91. package/dist/integrity/generators/gate.js +15 -15
  92. package/dist/integrity/generators/gate.ts +10 -10
  93. package/dist/integrity/generators/issues.js +7 -7
  94. package/dist/integrity/generators/publish.js +39 -57
  95. package/dist/integrity/generators/publish.ts +44 -55
  96. package/dist/integrity/generators/trial.d.ts +3 -3
  97. package/dist/integrity/generators/trial.js +133 -165
  98. package/dist/integrity/generators/trial.ts +145 -169
  99. package/dist/integrity/lint.js +9 -13
  100. package/dist/integrity/lint.ts +7 -10
  101. package/dist/integrity/lsp/bin.js +5 -4
  102. package/dist/integrity/lsp/bin.ts +3 -1
  103. package/dist/integrity/lsp/diagnostics.js +3 -3
  104. package/dist/integrity/lsp/diagnostics.ts +1 -1
  105. package/dist/integrity/lsp/lint.js +19 -30
  106. package/dist/integrity/lsp/lint.ts +16 -32
  107. package/dist/integrity/lsp/server.js +3 -3
  108. package/dist/integrity/slopnator.d.ts +3 -13
  109. package/dist/integrity/slopnator.js +58 -37
  110. package/dist/integrity/slopnator.ts +53 -37
  111. package/dist/integrity/slopnators/sl_broken_lineage.js +2 -2
  112. package/dist/integrity/slopnators/sl_declared_elsewhere.js +1 -1
  113. package/dist/integrity/slopnators/sl_derived_drift.js +4 -4
  114. package/dist/integrity/slopnators/sl_derived_drift.ts +1 -1
  115. package/dist/integrity/slopnators/sl_door_in_a_node.js +220 -0
  116. package/dist/integrity/slopnators/sl_door_in_a_node.ts +250 -0
  117. package/dist/integrity/slopnators/sl_inert_island.js +10 -8
  118. package/dist/integrity/slopnators/sl_inert_island.ts +8 -6
  119. package/dist/integrity/slopnators/sl_missing_row.js +6 -51
  120. package/dist/integrity/slopnators/sl_missing_row.ts +4 -50
  121. package/dist/integrity/slopnators/sl_no_barrel.js +1 -1
  122. package/dist/integrity/slopnators/sl_no_child_process.js +36 -18
  123. package/dist/integrity/slopnators/sl_no_child_process.ts +44 -18
  124. package/dist/integrity/slopnators/sl_no_dispatch_bypass.js +80 -149
  125. package/dist/integrity/slopnators/sl_no_dispatch_bypass.ts +95 -147
  126. package/dist/integrity/slopnators/sl_no_import_cycle.js +15 -4
  127. package/dist/integrity/slopnators/sl_no_import_cycle.ts +20 -4
  128. package/dist/integrity/slopnators/sl_no_such_command.js +2 -2
  129. package/dist/integrity/slopnators/sl_no_such_command.ts +1 -1
  130. package/dist/integrity/slopnators/sl_outside_an_operation.js +20 -0
  131. package/dist/integrity/slopnators/sl_outside_an_operation.ts +25 -0
  132. package/dist/integrity/slopnators/sl_screen_holds_only_declared.d.ts +2 -0
  133. package/dist/integrity/slopnators/sl_screen_holds_only_declared.js +61 -0
  134. package/dist/integrity/slopnators/sl_screen_holds_only_declared.ts +70 -0
  135. package/dist/integrity/slopnators/sl_secret_literal.js +1 -33
  136. package/dist/integrity/slopnators/sl_secret_literal.ts +1 -33
  137. package/dist/integrity/slopnators/sl_stale_path.js +5 -22
  138. package/dist/integrity/slopnators/sl_stale_path.ts +4 -20
  139. package/dist/integrity/slopnators/sl_twice_meant.js +1 -1
  140. package/dist/integrity/slopnators/sl_twice_parented.js +29 -5
  141. package/dist/integrity/slopnators/sl_twice_parented.ts +28 -4
  142. package/dist/integrity/slopnators/sl_undeclared_crossing.d.ts +4 -1
  143. package/dist/integrity/slopnators/sl_undeclared_crossing.js +426 -62
  144. package/dist/integrity/slopnators/sl_undeclared_crossing.ts +454 -70
  145. package/dist/integrity/slopnators/sl_undeclared_destination.js +59 -25
  146. package/dist/integrity/slopnators/sl_undeclared_destination.ts +69 -21
  147. package/dist/integrity/slopnators/sl_undeclared_list.d.ts +2 -0
  148. package/dist/integrity/slopnators/sl_undeclared_list.js +363 -0
  149. package/dist/integrity/slopnators/sl_undeclared_list.ts +358 -0
  150. package/dist/integrity/slopnators/sl_undeclared_node.js +1 -11
  151. package/dist/integrity/slopnators/sl_undeclared_node.ts +0 -10
  152. package/dist/integrity/slopnators/sl_undeclared_operation.js +111 -29
  153. package/dist/integrity/slopnators/sl_undeclared_operation.ts +134 -26
  154. package/dist/integrity/slopnators/sl_undeclared_variant.js +2 -2
  155. package/dist/integrity/slopnators/sl_undeclared_webhook.d.ts +2 -0
  156. package/dist/integrity/slopnators/sl_undeclared_webhook.js +90 -0
  157. package/dist/integrity/slopnators/sl_undeclared_webhook.ts +108 -0
  158. package/dist/integrity/slopnators/sl_underived_route.d.ts +2 -0
  159. package/dist/integrity/slopnators/sl_underived_route.js +61 -0
  160. package/dist/integrity/slopnators/sl_underived_route.ts +72 -0
  161. package/dist/integrity/slopnators/sl_ungoverned_island.js +3 -3
  162. package/dist/integrity/slopnators/sl_ungoverned_island.ts +1 -1
  163. package/dist/integrity/slopnators/sl_unlisted_program.d.ts +2 -0
  164. package/dist/integrity/slopnators/sl_unlisted_program.js +39 -0
  165. package/dist/integrity/slopnators/sl_unlisted_program.ts +40 -0
  166. package/dist/integrity/slopnators/sl_unnamed_island.js +1 -1
  167. package/dist/integrity/slopnators/sl_unpaired_member.js +3 -3
  168. package/dist/integrity/slopnators/sl_unpaired_member.ts +1 -1
  169. package/dist/integrity/slopnators/sl_unpaired_slopnator.js +4 -4
  170. package/dist/integrity/slopnators/sl_unpaired_slopnator.ts +1 -1
  171. package/dist/integrity/slopnators/sl_unprefixed_member.js +5 -31
  172. package/dist/integrity/slopnators/sl_unprefixed_member.ts +2 -28
  173. package/dist/integrity/slopnators/sl_unthreaded.js +11 -7
  174. package/dist/integrity/slopnators/sl_unthreaded.ts +6 -7
  175. package/dist/integrity/slopnators/sl_untranslated_key.js +2 -25
  176. package/dist/integrity/slopnators/sl_untranslated_key.ts +1 -25
  177. package/dist/integrity/slopnators/sl_unvaulted_secret.d.ts +2 -0
  178. package/dist/integrity/slopnators/sl_unvaulted_secret.js +107 -0
  179. package/dist/integrity/slopnators/sl_unvaulted_secret.ts +132 -0
  180. package/dist/integrity/slopnators/sl_unversioned.js +6 -4
  181. package/dist/integrity/slopnators/sl_unversioned.ts +5 -3
  182. package/dist/integrity/sweep.d.ts +1 -0
  183. package/dist/integrity/sweep.js +105 -42
  184. package/dist/integrity/sweep.ts +115 -42
  185. package/dist/knowledge/attach.js +1 -1
  186. package/dist/knowledge/generators/docsgen.js +8 -6
  187. package/dist/knowledge/generators/docsgen.ts +4 -2
  188. package/dist/knowledge/generators/glossary.js +4 -4
  189. package/dist/knowledge/generators/glossary.ts +1 -1
  190. package/dist/operations/access.d.ts +23 -0
  191. package/dist/operations/access.js +28 -1
  192. package/dist/operations/access.ts +43 -0
  193. package/dist/operations/attach.js +28 -109
  194. package/dist/operations/attach.ts +46 -113
  195. package/dist/operations/capabilities.d.ts +14 -5
  196. package/dist/operations/capabilities.js +13 -4
  197. package/dist/operations/capabilities.ts +27 -8
  198. package/dist/operations/dispatch.d.ts +8 -2
  199. package/dist/operations/dispatch.js +128 -185
  200. package/dist/operations/dispatch.ts +138 -191
  201. package/dist/operations/error-results.d.ts +1 -1
  202. package/dist/operations/error-results.js +2 -2
  203. package/dist/operations/error-results.ts +2 -2
  204. package/dist/operations/generators/capability.d.ts +11 -1
  205. package/dist/operations/generators/capability.js +481 -224
  206. package/dist/operations/generators/capability.ts +562 -233
  207. package/dist/operations/generators/compile.js +66 -39
  208. package/dist/operations/generators/compile.ts +78 -39
  209. package/dist/operations/generators/permits.js +11 -12
  210. package/dist/operations/generators/permits.ts +3 -4
  211. package/dist/operations/generators/seed.d.ts +1 -0
  212. package/dist/operations/generators/seed.js +19 -17
  213. package/dist/operations/generators/seed.ts +22 -14
  214. package/dist/operations/generators/trial.d.ts +6 -8
  215. package/dist/operations/generators/trial.js +200 -70
  216. package/dist/operations/generators/trial.ts +230 -93
  217. package/dist/operations/nodes.d.ts +15 -16
  218. package/dist/operations/nodes.ts +16 -17
  219. package/dist/operations/operations.d.ts +40 -4
  220. package/dist/operations/operations.js +1 -0
  221. package/dist/operations/operations.ts +61 -4
  222. package/dist/operations/result.d.ts +2 -2
  223. package/dist/operations/result.ts +2 -2
  224. package/package.json +1 -4
  225. package/dist/core/answers.d.ts +0 -171
  226. package/dist/core/answers.js +0 -789
  227. package/dist/core/answers.ts +0 -999
  228. package/dist/core/client.d.ts +0 -63
  229. package/dist/core/client.js +0 -249
  230. package/dist/core/client.ts +0 -289
  231. package/dist/core/files.js +0 -171
  232. package/dist/core/files.ts +0 -222
  233. package/dist/integrations/module.d.ts +0 -6
  234. package/dist/integrations/module.js +0 -153
  235. package/dist/integrations/module.ts +0 -191
  236. package/dist/integrity/slopnators/sl_unkept_rows.js +0 -104
  237. package/dist/integrity/slopnators/sl_unkept_rows.ts +0 -111
  238. package/dist/integrity/slopnators/sl_unseamed_reach.js +0 -73
  239. package/dist/integrity/slopnators/sl_unseamed_reach.ts +0 -79
  240. package/dist/operations/cost.d.ts +0 -14
  241. package/dist/operations/cost.js +0 -37
  242. package/dist/operations/cost.ts +0 -58
  243. package/dist/operations/errors/errors.d.ts +0 -52
  244. package/dist/operations/http.js +0 -124
  245. package/dist/operations/http.ts +0 -169
  246. package/dist/operations/memory.d.ts +0 -104
  247. package/dist/operations/memory.js +0 -365
  248. package/dist/operations/memory.ts +0 -470
  249. package/dist/operations/metering.d.ts +0 -55
  250. package/dist/operations/metering.js +0 -247
  251. package/dist/operations/metering.ts +0 -328
  252. package/dist/operations/queue-policy.d.ts +0 -3
  253. package/dist/operations/queue-policy.js +0 -8
  254. package/dist/operations/queue-policy.ts +0 -10
  255. package/dist/operations/seams.d.ts +0 -239
  256. package/dist/operations/seams.js +0 -45
  257. package/dist/operations/seams.ts +0 -364
  258. package/dist/operations/store.d.ts +0 -14
  259. package/dist/operations/store.js +0 -1
  260. package/dist/operations/store.ts +0 -29
  261. /package/dist/{operations → core}/errors/classes.d.ts +0 -0
  262. /package/dist/{operations → core}/errors/classes.js +0 -0
  263. /package/dist/{operations → core}/errors/classes.ts +0 -0
  264. /package/dist/integrity/slopnators/{sl_unkept_rows.d.ts → sl_door_in_a_node.d.ts} +0 -0
  265. /package/dist/integrity/slopnators/{sl_unseamed_reach.d.ts → sl_outside_an_operation.d.ts} +0 -0
@@ -1,10 +1,10 @@
1
- import { spawnSync } from 'node:child_process';
1
+ import { starts } from '../integrations/cli.js';
2
2
  import { join } from 'node:path';
3
3
  import { execPath } from 'node:process';
4
4
  import { fileURLToPath } from 'node:url';
5
- import { existsSync, readFileSync, readdirSync } from "./files.js";
6
- import { valuesIn } from "./rows.js";
7
- import { findTree } from "./tree.js";
5
+ import { existsSync, readFileSync, readdirSync } from '../integrations/disk.js';
6
+ import { valuesIn } from './rows.js';
7
+ import { findTree } from './tree.js';
8
8
  const STATING = 'derivation.ts';
9
9
  export async function generators(tree = findTree()) {
10
10
  if (!existsSync(tree.dir))
@@ -39,12 +39,8 @@ export const REFUSED_BY = 'round: refused';
39
39
  export function batched(at, commands, within) {
40
40
  if (commands.length === 0)
41
41
  return [];
42
- const out = spawnSync(execPath, [...LOADER, RUNNER, ...new Set(commands)], {
43
- cwd: at,
44
- encoding: 'utf8',
45
- ...(within === undefined ? {} : { timeout: within, killSignal: 'SIGKILL' }),
46
- });
47
- const lines = `${out.stdout ?? ''}${out.stderr ?? ''}`.split('\n');
42
+ const out = starts(execPath, [...LOADER, RUNNER, ...new Set(commands)], { at, shows: 'errors', within });
43
+ const lines = out.out.split('\n');
48
44
  const found = new Map();
49
45
  let held = [];
50
46
  for (const line of lines) {
@@ -58,12 +54,12 @@ export function batched(at, commands, within) {
58
54
  held = [];
59
55
  }
60
56
  const orphaned = held.join('\n').trim();
61
- const died = out.error !== undefined
62
- ? ` The runner itself failed to start or was stopped: ${out.error.message}.`
63
- : out.signal !== null && out.signal !== undefined
64
- ? ` The runner was killed by ${out.signal}, so everything after the command it was running is unreported rather than refused.`
65
- : found.size === 0 && out.status !== 0
66
- ? ` The runner exited ${String(out.status)} having reported nothing, so it died before the first command finished.`
57
+ const died = out.why !== ''
58
+ ? ` The runner itself failed to start or was stopped: ${out.why}.`
59
+ : out.killed !== null
60
+ ? ` The runner was killed by ${out.killed} after ${String(within ?? 0)} ms. The last "settle: running" line above names the command it was inside, and everything after that one is unreported rather than refused.`
61
+ : found.size === 0 && !out.ok
62
+ ? ` The runner exited ${String(out.code)} having reported nothing, so it died before the first command finished.`
67
63
  : '';
68
64
  let firstUnreached = true;
69
65
  return commands.map((one) => {
@@ -93,7 +89,7 @@ export function settling(at, all, skip = []) {
93
89
  let before;
94
90
  while (round < ROUNDS) {
95
91
  round += 1;
96
- const asking = round === 1 ? mine.map((one) => word(one.runs)) : batch(at, mine.map((one) => word(one.runs))).refused;
92
+ const asking = batch(at, mine.map((one) => word(one.runs))).refused;
97
93
  const unhappy = mine.filter((one) => asking.includes(word(one.runs)));
98
94
  if (unhappy.length === 0)
99
95
  return { ran, unsettled: [], rounds: round };
@@ -1,10 +1,10 @@
1
- import { spawnSync } from 'node:child_process'
1
+ import { starts } from '../integrations/cli.ts'
2
2
  import { join } from 'node:path'
3
3
  import { execPath } from 'node:process'
4
4
  import { fileURLToPath } from 'node:url'
5
5
 
6
6
  import type { Row } from './descend.ts'
7
- import { existsSync, readFileSync, readdirSync } from './files.ts'
7
+ import { existsSync, readFileSync, readdirSync } from '../integrations/disk.ts'
8
8
  import { valuesIn } from './rows.ts'
9
9
  import { findTree, type Tree } from './tree.ts'
10
10
 
@@ -58,12 +58,8 @@ export function batched(
58
58
  ): readonly { readonly command: string; readonly ok: boolean; readonly said: string }[] {
59
59
  if (commands.length === 0) return []
60
60
 
61
- const out = spawnSync(execPath, [...LOADER, RUNNER, ...new Set(commands)], {
62
- cwd: at,
63
- encoding: 'utf8',
64
- ...(within === undefined ? {} : { timeout: within, killSignal: 'SIGKILL' as const }),
65
- })
66
- const lines = `${out.stdout ?? ''}${out.stderr ?? ''}`.split('\n')
61
+ const out = starts(execPath, [...LOADER, RUNNER, ...new Set(commands)], { at, shows: 'errors', within })
62
+ const lines = out.out.split('\n')
67
63
 
68
64
  const found = new Map<string, { readonly command: string; readonly ok: boolean; readonly said: string }>()
69
65
  let held: string[] = []
@@ -80,12 +76,12 @@ export function batched(
80
76
 
81
77
  const orphaned = held.join('\n').trim()
82
78
  const died =
83
- out.error !== undefined
84
- ? ` The runner itself failed to start or was stopped: ${out.error.message}.`
85
- : out.signal !== null && out.signal !== undefined
86
- ? ` The runner was killed by ${out.signal}, so everything after the command it was running is unreported rather than refused.`
87
- : found.size === 0 && out.status !== 0
88
- ? ` The runner exited ${String(out.status)} having reported nothing, so it died before the first command finished.`
79
+ out.why !== ''
80
+ ? ` The runner itself failed to start or was stopped: ${out.why}.`
81
+ : out.killed !== null
82
+ ? ` The runner was killed by ${out.killed} after ${String(within ?? 0)} ms. The last "settle: running" line above names the command it was inside, and everything after that one is unreported rather than refused.`
83
+ : found.size === 0 && !out.ok
84
+ ? ` The runner exited ${String(out.code)} having reported nothing, so it died before the first command finished.`
89
85
  : ''
90
86
  let firstUnreached = true
91
87
  return commands.map((one) => {
@@ -127,7 +123,7 @@ export function settling(
127
123
  while (round < ROUNDS) {
128
124
  round += 1
129
125
 
130
- const asking = round === 1 ? mine.map((one) => word(one.runs)) : batch(at, mine.map((one) => word(one.runs))).refused
126
+ const asking = batch(at, mine.map((one) => word(one.runs))).refused
131
127
  const unhappy = mine.filter((one) => asking.includes(word(one.runs)))
132
128
  if (unhappy.length === 0) return { ran, unsettled: [], rounds: round }
133
129
 
@@ -1,8 +1,8 @@
1
1
  import { type Actor } from './actor.ts';
2
- export declare const COMPARE_OPS: readonly ["eq", "neq", "lt", "lte", "gt", "gte", "in", "has"];
2
+ export declare const COMPARE_OPS: readonly ['eq', 'neq', 'lt', 'lte', 'gt', 'gte', 'in', 'has'];
3
3
  export type CompareOp = (typeof COMPARE_OPS)[number];
4
- export declare const AGGREGATIONS: readonly ["count", "sum", "min", "max", "avg"];
5
- export declare const ACTOR_FACTS: readonly ["id", "roles", "entitlements", ...("run" | "address" | "device" | "replay")[]];
4
+ export declare const AGGREGATIONS: readonly ['count', 'sum', 'min', 'max', 'avg'];
5
+ export declare const ACTOR_FACTS: readonly ["id", "roles", "entitlements", ...("address" | "device" | "moment" | "replay" | "run")[]];
6
6
  export declare const AGO: RegExp;
7
7
  export type Operand = {
8
8
  readonly value: string | number | boolean | null;
@@ -41,7 +41,7 @@ export declare function walkOperands(condition: Condition, take: (one: Operand)
41
41
  export declare function mentionsActor(condition: Condition): boolean;
42
42
  export declare function agoSeconds(ago: string): number | undefined;
43
43
  export declare function satisfies(condition: Condition, actor: Actor): boolean;
44
- export declare const CONDITION_ANSWERS: readonly ["is", "than", "field", "value", "actor", "ago", "of", "how", "over", "op", "when", "joins"];
44
+ export declare const CONDITION_ANSWERS: readonly ['is', 'than', 'field', 'value', 'actor', 'ago', 'of', 'how', 'over', 'op', 'when', 'joins'];
45
45
  export declare function conditioned(ask: (key: string, question: string) => Promise<string>, say: (line: string) => void, subject: {
46
46
  readonly id: string;
47
47
  readonly fields: readonly string[];
@@ -1,4 +1,4 @@
1
- import { ENVIRONMENT } from "./actor.js";
1
+ import { ENVIRONMENT } from './actor.js';
2
2
  export const COMPARE_OPS = ['eq', 'neq', 'lt', 'lte', 'gt', 'gte', 'in', 'has'];
3
3
  export const AGGREGATIONS = ['count', 'sum', 'min', 'max', 'avg'];
4
4
  export const ACTOR_FACTS = ['id', 'roles', 'entitlements', ...ENVIRONMENT.map(([fact]) => fact)];
@@ -182,7 +182,7 @@ export async function conditioned(ask, say, subject, all) {
182
182
  const first = held[0];
183
183
  return held.length === 1 ? first : joiner === 'all' ? { all: held } : { any: held };
184
184
  }
185
- import { quoted } from "./rows.js";
185
+ import { quoted } from './rows.js';
186
186
  export function said(one) {
187
187
  if (typeof one === 'string')
188
188
  return quoted(one);
@@ -4,7 +4,7 @@ export declare const ROOT: string;
4
4
  export declare const TREE: string;
5
5
  export declare const THREAD_AT: string;
6
6
  export declare function unkept(at: string): boolean;
7
- export declare const LISTS: readonly ["declares", "derives", "seeds"];
7
+ export declare const LISTS: readonly ['declares', 'derives', 'seeds'];
8
8
  export declare const ROW_LISTS: readonly ["declares", "derives", "seeds", "holds"];
9
9
  export interface Row {
10
10
  readonly at?: string;
@@ -59,6 +59,7 @@ export declare function shapeFields(named: string, inside?: string): readonly {
59
59
  readonly map: boolean;
60
60
  readonly worded: boolean;
61
61
  readonly guide: string;
62
+ readonly record: readonly string[];
62
63
  }[];
63
64
  export interface Field {
64
65
  readonly name: string;
@@ -68,9 +69,10 @@ export interface Field {
68
69
  readonly boolean: boolean;
69
70
  readonly worded: boolean;
70
71
  readonly guide: string;
72
+ readonly record: readonly string[];
71
73
  }
72
74
  export declare function listShape(at: string, list: string): string | undefined;
73
- export declare function listFields(at: string, list: string): readonly Field[];
75
+ export declare function listFields(at: string, list: string, within?: number): readonly Field[];
74
76
  export declare function rowShape(at: string, list: string): string | undefined;
75
77
  interface Held {
76
78
  readonly name: string;
@@ -1,10 +1,10 @@
1
- import { spawnSync } from 'node:child_process';
2
- import { attached, existsSync, kind, readdirSync, readFileSync, realpathSync, statSync, writeFileSync } from "./files.js";
1
+ import { starts } from '../integrations/cli.js';
2
+ import { attached, beyond, existsSync, here as answering, kind, readdirSync, readFileSync, realpathSync, statSync, writeFileSync } from '../integrations/disk.js';
3
3
  import { dirname, join, relative, resolve } from 'node:path';
4
4
  import ts from 'typescript';
5
- import { ignored } from "./init.js";
6
- import { NAMED, ORGANISATION } from "./scope.js";
7
- import { findTree } from "./tree.js";
5
+ import { ignored } from './init.js';
6
+ import { NAMED, ORGANISATION } from './scope.js';
7
+ import { findTree } from './tree.js';
8
8
  const BUILT = new Set(ignored()
9
9
  .split('\n')
10
10
  .map((one) => one.trim())
@@ -32,7 +32,7 @@ function ownIgnores() {
32
32
  if (readIgnores)
33
33
  return ignores;
34
34
  readIgnores = true;
35
- if (!attached() && spawnSync('git', ['rev-parse', '--is-inside-work-tree'], { cwd: ROOT, stdio: 'ignore' }).status === 0) {
35
+ if (!attached() && starts('git', ['rev-parse', '--is-inside-work-tree'], { at: ROOT }).ok) {
36
36
  return ignores;
37
37
  }
38
38
  try {
@@ -55,7 +55,7 @@ export function unkept(at) {
55
55
  return held;
56
56
  const own = ownIgnores();
57
57
  const found = own === undefined
58
- ? spawnSync('git', ['check-ignore', '-q', '--', at], { cwd: ROOT, stdio: 'ignore' }).status === 0
58
+ ? starts('git', ['check-ignore', '-q', '--', at], { at: ROOT }).ok
59
59
  : own.some((one) => at === one || at.startsWith(`${one}/`) || at.split('/').includes(one));
60
60
  asked.set(at, found);
61
61
  return found;
@@ -198,7 +198,7 @@ function listing(at, take, skip, only, deep) {
198
198
  function parsing(from) {
199
199
  return {
200
200
  useCaseSensitiveFileNames: true,
201
- fileExists: (at) => existsSync(at),
201
+ fileExists: (at) => !beyond(at) && existsSync(at),
202
202
  readFile: (at) => {
203
203
  try {
204
204
  return readFileSync(at);
@@ -237,23 +237,34 @@ export function configured() {
237
237
  }
238
238
  return { options: { ...FALLBACK, ...INVARIANT }, from: '' };
239
239
  }
240
+ const libraries = new Map();
241
+ const DEPENDENCIES = '/node_' + 'modules/';
240
242
  export function hosting(options) {
241
243
  const host = ts.createCompilerHost(options, true);
242
244
  const lib = host.getDefaultLibLocation?.();
245
+ const settings = JSON.stringify(options);
243
246
  const toolchain = (at) => lib !== undefined && (at === lib || at.startsWith(`${lib}/`));
247
+ const seen = (at) => (beyond(at) ? 'none' : kind(at));
244
248
  return {
245
249
  ...host,
246
- fileExists: (at) => (toolchain(at) ? host.fileExists(at) : kind(at) === 'file'),
247
- directoryExists: (at) => (toolchain(at) ? (host.directoryExists?.(at) ?? false) : kind(at) === 'folder'),
248
- readFile: (at) => (toolchain(at) ? host.readFile(at) : kind(at) === 'file' ? readFileSync(at) : undefined),
250
+ fileExists: (at) => (toolchain(at) ? host.fileExists(at) : seen(at) === 'file'),
251
+ directoryExists: (at) => (toolchain(at) ? (host.directoryExists?.(at) ?? false) : seen(at) === 'folder'),
252
+ readFile: (at) => (toolchain(at) ? host.readFile(at) : seen(at) === 'file' ? readFileSync(at) : undefined),
249
253
  getSourceFile: (at, version) => {
250
- if (toolchain(at))
251
- return host.getSourceFile(at, version);
252
- if (kind(at) !== 'file')
253
- return undefined;
254
- return ts.createSourceFile(at, readFileSync(at), version, true);
254
+ const lasting = toolchain(at) ? true : at.includes(DEPENDENCIES);
255
+ const key = settings + at + JSON.stringify(version);
256
+ if (lasting && libraries.has(key))
257
+ return libraries.get(key);
258
+ const made = toolchain(at)
259
+ ? host.getSourceFile(at, version)
260
+ : seen(at) === 'file'
261
+ ? ts.createSourceFile(at, readFileSync(at), version, true)
262
+ : undefined;
263
+ if (lasting)
264
+ libraries.set(key, made);
265
+ return made;
255
266
  },
256
- getDirectories: (at) => kind(at) === 'folder' ? readdirSync(at, { withFileTypes: true }).filter((one) => one.isDirectory()).map((one) => one.name) : [],
267
+ getDirectories: (at) => seen(at) === 'folder' ? readdirSync(at, { withFileTypes: true }).filter((one) => one.isDirectory()).map((one) => one.name) : [],
257
268
  realpath: (at) => (toolchain(at) ? (host.realpath?.(at) ?? at) : realpathSync(at)),
258
269
  writeFile: (at, said) => {
259
270
  writeFileSync(at, said);
@@ -328,8 +339,13 @@ export function read(at) {
328
339
  source.set(at, text);
329
340
  return text;
330
341
  }
342
+ const shapes = new WeakMap();
331
343
  export function shapesNamed() {
332
- const { program, checker } = compiled();
344
+ const held = compiled();
345
+ const known = shapes.get(held);
346
+ if (known !== undefined)
347
+ return known;
348
+ const { program, checker } = held;
333
349
  const shape = threadedType(checker, program);
334
350
  const found = new Map();
335
351
  if (shape === undefined)
@@ -368,6 +384,7 @@ export function shapesNamed() {
368
384
  found.set(named, [...where, at]);
369
385
  }
370
386
  }
387
+ shapes.set(held, found);
371
388
  return found;
372
389
  }
373
390
  export function threading() {
@@ -408,13 +425,14 @@ export function shapeFields(named, inside) {
408
425
  map: mapped(kind),
409
426
  worded: (kind.isUnion() ? kind.types : [kind]).some((each) => (each.flags & ts.TypeFlags.StringLike) !== 0),
410
427
  guide: ts.displayPartsToString(one.getDocumentationComment(checker)).trim(),
428
+ record: recordOf(checker, kind),
411
429
  };
412
430
  });
413
431
  }
414
432
  }
415
433
  return [];
416
434
  }
417
- function elementAt(at, list) {
435
+ function elementAt(at, list, within) {
418
436
  const { program, checker } = compiled();
419
437
  const file = program.getSourceFile(resolve(ROOT, at));
420
438
  if (file === undefined)
@@ -424,6 +442,8 @@ function elementAt(at, list) {
424
442
  const consider = (node) => {
425
443
  if (held !== undefined)
426
444
  return;
445
+ if (within !== undefined && node.getStart() !== within)
446
+ return;
427
447
  const wanted = checker.getContextualType(node) ?? checker.getTypeAtLocation(node);
428
448
  const kinds = (wanted.isUnionOrIntersection() ? wanted.types : [wanted])
429
449
  .filter((one) => checker.getIndexTypeOfType(one, ts.IndexKind.Number) !== undefined)
@@ -455,8 +475,8 @@ export function listShape(at, list) {
455
475
  const held = elementAt(at, list);
456
476
  return held === undefined ? undefined : compiled().checker.typeToString(held.type);
457
477
  }
458
- export function listFields(at, list) {
459
- const held = elementAt(at, list);
478
+ export function listFields(at, list, within) {
479
+ const held = elementAt(at, list, within);
460
480
  if (held === undefined)
461
481
  return [];
462
482
  const { checker } = compiled();
@@ -480,12 +500,29 @@ export function listFields(at, list) {
480
500
  boolean: flagged(kind),
481
501
  worded: worded(kind),
482
502
  guide: ts.displayPartsToString(one.getDocumentationComment(checker)).trim(),
503
+ record: recordOf(checker, kind),
483
504
  };
484
505
  });
485
506
  }
486
507
  function elementOf(checker, type) {
487
508
  return checker.getIndexTypeOfType(type, ts.IndexKind.Number) ?? type;
488
509
  }
510
+ function recordOf(checker, type) {
511
+ for (const each of type.isUnion() ? type.types : [type]) {
512
+ if ((each.flags & ts.TypeFlags.StringLike) !== 0 || checker.getIndexTypeOfType(each, ts.IndexKind.Number) === undefined)
513
+ continue;
514
+ const element = elementOf(checker, each);
515
+ if ((element.flags & ts.TypeFlags.Object) === 0)
516
+ return [];
517
+ const fields = checker.getPropertiesOfType(element);
518
+ const worded = fields.every((one) => {
519
+ const kind = checker.getNonNullableType(checker.getTypeOfSymbol(one));
520
+ return (kind.isUnion() ? kind.types : [kind]).every((part) => (part.flags & ts.TypeFlags.StringLike) !== 0);
521
+ });
522
+ return worded ? fields.map((one) => one.getName()) : [];
523
+ }
524
+ return [];
525
+ }
489
526
  export function rowShape(at, list) {
490
527
  const { program, checker } = compiled();
491
528
  const file = program.getSourceFile(resolve(ROOT, at));
@@ -666,7 +703,38 @@ let passed;
666
703
  export function reached() {
667
704
  return [THREAD_AT, ...descend().threaded.keys()];
668
705
  }
706
+ let world = answering();
707
+ const worlds = new WeakMap();
708
+ function kept() {
709
+ const was = { passed, built, scanned, commands, ignores, readIgnores, source: new Map(source), asked: new Map(asked) };
710
+ return () => {
711
+ passed = was.passed;
712
+ built = was.built;
713
+ scanned = was.scanned;
714
+ commands = was.commands;
715
+ ignores = was.ignores;
716
+ readIgnores = was.readIgnores;
717
+ source.clear();
718
+ for (const [at, text] of was.source)
719
+ source.set(at, text);
720
+ asked.clear();
721
+ for (const [at, ignore] of was.asked)
722
+ asked.set(at, ignore);
723
+ };
724
+ }
669
725
  export function forget() {
726
+ const now = answering();
727
+ if (now === world)
728
+ libraries.clear();
729
+ else {
730
+ worlds.set(world, kept());
731
+ world = now;
732
+ const back = worlds.get(now);
733
+ if (back !== undefined) {
734
+ back();
735
+ return;
736
+ }
737
+ }
670
738
  passed = undefined;
671
739
  built = undefined;
672
740
  scanned = undefined;
@@ -1,5 +1,5 @@
1
- import { spawnSync } from 'node:child_process'
2
- import { attached, existsSync, kind, readdirSync, readFileSync, realpathSync, statSync, writeFileSync } from './files.ts'
1
+ import { starts } from '../integrations/cli.ts'
2
+ import { attached, beyond, existsSync, here as answering, kind, readdirSync, readFileSync, realpathSync, statSync, writeFileSync, type Kind } from '../integrations/disk.ts'
3
3
  import { dirname, join, relative, resolve } from 'node:path'
4
4
 
5
5
  import ts from 'typescript'
@@ -41,7 +41,7 @@ let readIgnores = false
41
41
  function ownIgnores(): readonly string[] | undefined {
42
42
  if (readIgnores) return ignores
43
43
  readIgnores = true
44
- if (!attached() && spawnSync('git', ['rev-parse', '--is-inside-work-tree'], { cwd: ROOT, stdio: 'ignore' }).status === 0) {
44
+ if (!attached() && starts('git', ['rev-parse', '--is-inside-work-tree'], { at: ROOT }).ok) {
45
45
  return ignores
46
46
  }
47
47
  try {
@@ -65,7 +65,7 @@ export function unkept(at: string): boolean {
65
65
  const own = ownIgnores()
66
66
  const found =
67
67
  own === undefined
68
- ? spawnSync('git', ['check-ignore', '-q', '--', at], { cwd: ROOT, stdio: 'ignore' }).status === 0
68
+ ? starts('git', ['check-ignore', '-q', '--', at], { at: ROOT }).ok
69
69
  : own.some((one) => at === one || at.startsWith(`${one}/`) || at.split('/').includes(one))
70
70
  asked.set(at, found)
71
71
  return found
@@ -252,7 +252,7 @@ function listing(at: string, take: readonly string[], skip: readonly string[], o
252
252
  function parsing(from: string): ts.ParseConfigHost {
253
253
  return {
254
254
  useCaseSensitiveFileNames: true,
255
- fileExists: (at) => existsSync(at),
255
+ fileExists: (at) => !beyond(at) && existsSync(at),
256
256
  readFile: (at) => {
257
257
  try {
258
258
  return readFileSync(at)
@@ -301,22 +301,35 @@ export function configured(): { readonly options: ts.CompilerOptions; readonly f
301
301
  return { options: { ...FALLBACK, ...INVARIANT }, from: '' }
302
302
  }
303
303
 
304
+ const libraries = new Map<string, ReturnType<ts.CompilerHost['getSourceFile']>>()
305
+
306
+ const DEPENDENCIES = '/node_' + 'modules/'
307
+
304
308
  export function hosting(options: ts.CompilerOptions): ts.CompilerHost {
305
309
  const host = ts.createCompilerHost(options, true)
306
310
  const lib = host.getDefaultLibLocation?.()
311
+ const settings = JSON.stringify(options)
307
312
  const toolchain = (at: string): boolean => lib !== undefined && (at === lib || at.startsWith(`${lib}/`))
313
+ const seen = (at: string): Kind => (beyond(at) ? 'none' : kind(at))
308
314
  return {
309
315
  ...host,
310
- fileExists: (at) => (toolchain(at) ? host.fileExists(at) : kind(at) === 'file'),
311
- directoryExists: (at) => (toolchain(at) ? (host.directoryExists?.(at) ?? false) : kind(at) === 'folder'),
312
- readFile: (at) => (toolchain(at) ? host.readFile(at) : kind(at) === 'file' ? readFileSync(at) : undefined),
316
+ fileExists: (at) => (toolchain(at) ? host.fileExists(at) : seen(at) === 'file'),
317
+ directoryExists: (at) => (toolchain(at) ? (host.directoryExists?.(at) ?? false) : seen(at) === 'folder'),
318
+ readFile: (at) => (toolchain(at) ? host.readFile(at) : seen(at) === 'file' ? readFileSync(at) : undefined),
313
319
  getSourceFile: (at, version) => {
314
- if (toolchain(at)) return host.getSourceFile(at, version)
315
- if (kind(at) !== 'file') return undefined
316
- return ts.createSourceFile(at, readFileSync(at), version, true)
320
+ const lasting = toolchain(at) ? true : at.includes(DEPENDENCIES)
321
+ const key = settings + at + JSON.stringify(version)
322
+ if (lasting && libraries.has(key)) return libraries.get(key)
323
+ const made = toolchain(at)
324
+ ? host.getSourceFile(at, version)
325
+ : seen(at) === 'file'
326
+ ? ts.createSourceFile(at, readFileSync(at), version, true)
327
+ : undefined
328
+ if (lasting) libraries.set(key, made)
329
+ return made
317
330
  },
318
331
  getDirectories: (at) =>
319
- kind(at) === 'folder' ? readdirSync(at, { withFileTypes: true }).filter((one) => one.isDirectory()).map((one) => one.name) : [],
332
+ seen(at) === 'folder' ? readdirSync(at, { withFileTypes: true }).filter((one) => one.isDirectory()).map((one) => one.name) : [],
320
333
  realpath: (at) => (toolchain(at) ? (host.realpath?.(at) ?? at) : realpathSync(at)),
321
334
  writeFile: (at, said) => {
322
335
  writeFileSync(at, said)
@@ -389,8 +402,13 @@ export function read(at: string): string | undefined {
389
402
  return text
390
403
  }
391
404
 
405
+ const shapes = new WeakMap<object, ReadonlyMap<string, readonly string[]>>()
406
+
392
407
  export function shapesNamed(): ReadonlyMap<string, readonly string[]> {
393
- const { program, checker } = compiled()
408
+ const held = compiled()
409
+ const known = shapes.get(held)
410
+ if (known !== undefined) return known
411
+ const { program, checker } = held
394
412
  const shape = threadedType(checker, program)
395
413
  const found = new Map<string, string[]>()
396
414
  if (shape === undefined) return found
@@ -419,6 +437,7 @@ export function shapesNamed(): ReadonlyMap<string, readonly string[]> {
419
437
  found.set(named, [...where, at])
420
438
  }
421
439
  }
440
+ shapes.set(held, found)
422
441
  return found
423
442
  }
424
443
 
@@ -429,7 +448,7 @@ export function threading(): ReadonlySet<string> {
429
448
  export function shapeFields(
430
449
  named: string,
431
450
  inside?: string,
432
- ): readonly { readonly name: string; readonly required: boolean; readonly list: boolean; readonly map: boolean; readonly worded: boolean; readonly guide: string }[] {
451
+ ): readonly { readonly name: string; readonly required: boolean; readonly list: boolean; readonly map: boolean; readonly worded: boolean; readonly guide: string; readonly record: readonly string[] }[] {
433
452
  const { program, checker } = compiled()
434
453
  for (const at of [...sources(), ...shipped()]) {
435
454
  if (!SOURCE.test(at)) continue
@@ -466,6 +485,7 @@ export function shapeFields(
466
485
  map: mapped(kind),
467
486
  worded: (kind.isUnion() ? kind.types : [kind]).some((each) => (each.flags & ts.TypeFlags.StringLike) !== 0),
468
487
  guide: ts.displayPartsToString(one.getDocumentationComment(checker)).trim(),
488
+ record: recordOf(checker, kind),
469
489
  }
470
490
  })
471
491
  }
@@ -481,9 +501,10 @@ export interface Field {
481
501
  readonly boolean: boolean
482
502
  readonly worded: boolean
483
503
  readonly guide: string
504
+ readonly record: readonly string[]
484
505
  }
485
506
 
486
- function elementAt(at: string, list: string): { readonly type: ts.Type; readonly node: ts.Node } | undefined {
507
+ function elementAt(at: string, list: string, within?: number): { readonly type: ts.Type; readonly node: ts.Node } | undefined {
487
508
  const { program, checker } = compiled()
488
509
  const file = program.getSourceFile(resolve(ROOT, at))
489
510
  if (file === undefined) return undefined
@@ -493,6 +514,7 @@ function elementAt(at: string, list: string): { readonly type: ts.Type; readonly
493
514
 
494
515
  const consider = (node: ts.ArrayLiteralExpression): void => {
495
516
  if (held !== undefined) return
517
+ if (within !== undefined && node.getStart() !== within) return
496
518
  const wanted = checker.getContextualType(node) ?? checker.getTypeAtLocation(node)
497
519
  const kinds = (wanted.isUnionOrIntersection() ? wanted.types : [wanted])
498
520
  .filter((one) => checker.getIndexTypeOfType(one, ts.IndexKind.Number) !== undefined)
@@ -525,8 +547,8 @@ export function listShape(at: string, list: string): string | undefined {
525
547
  return held === undefined ? undefined : compiled().checker.typeToString(held.type)
526
548
  }
527
549
 
528
- export function listFields(at: string, list: string): readonly Field[] {
529
- const held = elementAt(at, list)
550
+ export function listFields(at: string, list: string, within?: number): readonly Field[] {
551
+ const held = elementAt(at, list, within)
530
552
  if (held === undefined) return []
531
553
  const { checker } = compiled()
532
554
  const bare = (kind: ts.Type): readonly ts.Type[] =>
@@ -557,6 +579,7 @@ export function listFields(at: string, list: string): readonly Field[] {
557
579
  boolean: flagged(kind),
558
580
  worded: worded(kind),
559
581
  guide: ts.displayPartsToString(one.getDocumentationComment(checker)).trim(),
582
+ record: recordOf(checker, kind),
560
583
  }
561
584
  })
562
585
  }
@@ -565,6 +588,21 @@ function elementOf(checker: ts.TypeChecker, type: ts.Type): ts.Type {
565
588
  return checker.getIndexTypeOfType(type, ts.IndexKind.Number) ?? type
566
589
  }
567
590
 
591
+ function recordOf(checker: ts.TypeChecker, type: ts.Type): readonly string[] {
592
+ for (const each of type.isUnion() ? type.types : [type]) {
593
+ if ((each.flags & ts.TypeFlags.StringLike) !== 0 || checker.getIndexTypeOfType(each, ts.IndexKind.Number) === undefined) continue
594
+ const element = elementOf(checker, each)
595
+ if ((element.flags & ts.TypeFlags.Object) === 0) return []
596
+ const fields = checker.getPropertiesOfType(element)
597
+ const worded = fields.every((one) => {
598
+ const kind = checker.getNonNullableType(checker.getTypeOfSymbol(one))
599
+ return (kind.isUnion() ? kind.types : [kind]).every((part) => (part.flags & ts.TypeFlags.StringLike) !== 0)
600
+ })
601
+ return worded ? fields.map((one) => one.getName()) : []
602
+ }
603
+ return []
604
+ }
605
+
568
606
  export function rowShape(at: string, list: string): string | undefined {
569
607
  const { program, checker } = compiled()
570
608
  const file = program.getSourceFile(resolve(ROOT, at))
@@ -751,7 +789,38 @@ export function reached(): readonly string[] {
751
789
  return [THREAD_AT, ...descend().threaded.keys()]
752
790
  }
753
791
 
792
+ let world: object = answering()
793
+
794
+ const worlds = new WeakMap<object, () => void>()
795
+
796
+ function kept(): () => void {
797
+ const was = { passed, built, scanned, commands, ignores, readIgnores, source: new Map(source), asked: new Map(asked) }
798
+ return () => {
799
+ passed = was.passed
800
+ built = was.built
801
+ scanned = was.scanned
802
+ commands = was.commands
803
+ ignores = was.ignores
804
+ readIgnores = was.readIgnores
805
+ source.clear()
806
+ for (const [at, text] of was.source) source.set(at, text)
807
+ asked.clear()
808
+ for (const [at, ignore] of was.asked) asked.set(at, ignore)
809
+ }
810
+ }
811
+
754
812
  export function forget(): void {
813
+ const now = answering()
814
+ if (now === world) libraries.clear()
815
+ else {
816
+ worlds.set(world, kept())
817
+ world = now
818
+ const back = worlds.get(now)
819
+ if (back !== undefined) {
820
+ back()
821
+ return
822
+ }
823
+ }
755
824
  passed = undefined
756
825
  built = undefined
757
826
  scanned = undefined