@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,78 +1,442 @@
1
- import { descend, under } from "../../core/descend.js";
2
- import { here } from "./sl_unthreaded.js";
3
- const CROSSINGS = [
4
- { what: 'the network', finds: /(?<![.\w])fetch\s*\(/ },
5
- { what: 'the network', finds: /(?<![.\w])XMLHttpRequest\b/ },
6
- { what: 'the clock', finds: /\bDate\s*\.\s*now\s*\(/ },
7
- { what: 'the clock', finds: /\bperformance\s*\.\s*now\s*\(/ },
8
- { what: 'the clock', finds: /\bsetInterval\s*\(/ },
9
- { what: 'the clock', finds: /\bsetTimeout\s*\(/ },
10
- { what: 'the clock', finds: /\brequestAnimationFrame\s*\(/ },
11
- { what: 'randomness', finds: /\bMath\s*\.\s*random\s*\(/ },
12
- { what: 'randomness', finds: /\bcrypto\s*\.\s*randomUUID\s*\(/ },
13
- { what: 'the queue', finds: /\bqueue\s*\.\s*enqueue\s*\(/ },
14
- { what: 'a seam', finds: /\b(?:files|llm|http)\s*\.\s*(?:call|run|complete|put|signedUrl|remove|get)\s*\(/ },
15
- ];
16
- const OPERATIONS_ID = 'operations';
17
- const NODES_ID = 'nodes';
18
- let bodies;
19
- /*
20
- * The files this law is about, read off the tree rather than off a path.
21
- *
22
- * A crossing belongs in a declared node and nowhere else, so what the law has to know is which
23
- * files ARE declared nodes — every row in an operation's `seeds` that requires a node type. It
24
- * cannot be a path rule: an operation seeds its node bodies and its input file into the same
25
- * directory, so excepting the directory would except the input file too and leave a hole exactly
26
- * one file wide per operation. Measured 2026-08-27: with the directory excepted instead, this law
27
- * fired on `operations/wf_asks/asks_it.ts` — a node body calling the model its operation had
28
- * declared, which is the one place the law's own `instead` sends you.
29
- */
30
- function declared() {
31
- if (bodies !== undefined)
32
- return bodies;
33
- const pass = descend();
34
- const found = new Set();
35
- for (const [at, threadings] of pass.threaded) {
36
- for (const one of threadings) {
37
- if (one.list !== 'seeds')
1
+ import { resolve } from 'node:path';
2
+ import { fileURLToPath } from 'node:url';
3
+ import ts from 'typescript';
4
+ import { ROOT, THREAD_AT, configured, descend, hosting, sources, under } from '../../core/descend.js';
5
+ import { packageOf } from '../../core/attach.js';
6
+ import { PREFIXES } from '../../core/thread.js';
7
+ import { HOLDS, OPENS } from '../../integrations/target.js';
8
+ const FROM_SOURCE = import.meta.url.endsWith('.ts');
9
+ const OPENED_AT = fileURLToPath(new URL(FROM_SOURCE ? '../../operations/operations.ts' : '../../operations/operations.d.ts', import.meta.url));
10
+ const HANDED = 'Opened';
11
+ const EVERY = '';
12
+ const IMPORTED = 'import ';
13
+ const MADE = 'new ';
14
+ const ANYWHERE = 'globalThis';
15
+ const REQUIRED = 'require';
16
+ const RUNTIME = 'node:';
17
+ const SOURCE = /[.](?:ts|tsx|mts|cts)$/;
18
+ const ROOTED = ROOT + '/';
19
+ const here = (filename) => (filename.startsWith(ROOTED) ? filename.slice(ROOTED.length) : filename);
20
+ const plain = (specifier) => (specifier.startsWith(RUNTIME) ? specifier.slice(RUNTIME.length) : specifier);
21
+ const HANDS = Object.fromEntries(Object.keys(HOLDS).map((one) => [
22
+ one,
23
+ HOLDS[one].takes === 'entities' ? 'store' : HOLDS[one].takes === 'calls' ? 'target' : one,
24
+ ]));
25
+ function opening() {
26
+ const found = {};
27
+ const add = (hand, spellings) => {
28
+ found[hand] = [...(found[hand] ?? []), ...spellings];
29
+ };
30
+ for (const [reach, spellings] of Object.entries(OPENS))
31
+ add(HANDS[reach] ?? reach, spellings);
32
+ return found;
33
+ }
34
+ function doors() {
35
+ const modules = new Map();
36
+ const members = new Map();
37
+ const globals = new Map();
38
+ const properties = new Map();
39
+ const made = new Map();
40
+ for (const [hand, spellings] of Object.entries(opening())) {
41
+ for (const spelling of spellings) {
42
+ const door = { hand, spelling };
43
+ if (spelling.startsWith(IMPORTED)) {
44
+ const rest = spelling.slice(IMPORTED.length);
45
+ const dot = rest.lastIndexOf('.');
46
+ if (dot === -1 || rest.slice(dot).includes('/')) {
47
+ modules.set(plain(rest), door);
48
+ continue;
49
+ }
50
+ const specifier = plain(rest.slice(0, dot));
51
+ const held = members.get(specifier) ?? new Map();
52
+ held.set(rest.slice(dot + 1), door);
53
+ members.set(specifier, held);
38
54
  continue;
39
- if (!under(pass, one.by, OPERATIONS_ID) || under(pass, one.by, NODES_ID))
55
+ }
56
+ if (spelling.startsWith(MADE)) {
57
+ made.set(spelling.slice(MADE.length), door);
40
58
  continue;
41
- if ((one.row.requires ?? []).length === 0)
59
+ }
60
+ if (spelling.includes('.'))
61
+ properties.set(spelling, door);
62
+ else
63
+ globals.set(spelling, door);
64
+ }
65
+ }
66
+ return { modules, members, globals, properties, made };
67
+ }
68
+ const DOORS = doors();
69
+ const INTEGRATION = 'Integration';
70
+ const NEEDS = 'needs';
71
+ function needed(pass) {
72
+ const found = new Map();
73
+ const rows = [...pass.stated.values(), ...[...pass.threaded.values()].flat().map((one) => one.row)];
74
+ for (const row of rows) {
75
+ if (row.type !== INTEGRATION)
76
+ continue;
77
+ const held = row.fields.get(NEEDS);
78
+ if (held === undefined || !ts.isObjectLiteralExpression(held))
79
+ continue;
80
+ for (const one of held.properties) {
81
+ if (!ts.isPropertyAssignment(one))
42
82
  continue;
43
- found.add(at);
83
+ const name = ts.isStringLiteral(one.name) || ts.isIdentifier(one.name) ? one.name.text : undefined;
84
+ if (name !== undefined)
85
+ found.set(name, { hand: HANDS['sdk'] ?? 'sdk', spelling: IMPORTED + name });
44
86
  }
45
87
  }
46
- bodies = found;
47
88
  return found;
48
89
  }
49
- export const slopnator = {
50
- holds: (_node, at) => {
51
- if (declared().has(here(at.filename)))
52
- return false;
53
- for (const crossing of CROSSINGS) {
54
- if (!crossing.finds.test(at.text))
55
- continue;
56
- return (`${crossing.what} reached outside a declared node — nothing recorded this crossing, so it ` +
57
- 'cannot be replayed, retried, priced or unwound, and the page renders exactly the same either way');
90
+ const moduleOf = (door) => door.spelling.slice(IMPORTED.length, door.spelling.lastIndexOf('.'));
91
+ const stands = (door) => ', which the ' + door.hand + ' this app is handed stands in for';
92
+ let built;
93
+ function argumentsOf(type, checker) {
94
+ const found = [...(type.aliasTypeArguments ?? [])];
95
+ if ((type.flags & ts.TypeFlags.Object) !== 0 && (type.objectFlags & ts.ObjectFlags.Reference) !== 0) {
96
+ found.push(...checker.getTypeArguments(type));
97
+ }
98
+ return found;
99
+ }
100
+ function handed(type, checker, hand, into, seen, own) {
101
+ if (seen.has(type))
102
+ return;
103
+ seen.add(type);
104
+ if (type.isUnionOrIntersection()) {
105
+ for (const one of type.types)
106
+ handed(one, checker, hand, into, seen, own);
107
+ return;
108
+ }
109
+ for (const one of argumentsOf(type, checker))
110
+ handed(one, checker, hand, into, seen, own);
111
+ const index = checker.getIndexTypeOfType(type, ts.IndexKind.String);
112
+ if (index !== undefined)
113
+ handed(index, checker, hand, into, seen, own);
114
+ const symbol = type.getSymbol();
115
+ if (symbol === undefined || !own(symbol))
116
+ return;
117
+ if ((symbol.flags & (ts.SymbolFlags.Interface | ts.SymbolFlags.TypeLiteral | ts.SymbolFlags.Class)) === 0)
118
+ return;
119
+ if (!into.has(symbol))
120
+ into.set(symbol, hand);
121
+ }
122
+ function given(node, checker, into, own) {
123
+ const take = (one) => {
124
+ if (ts.isTypeReferenceNode(one)) {
125
+ const named = ts.isIdentifier(one.typeName) ? one.typeName : one.typeName.right;
126
+ const found = checker.getSymbolAtLocation(named);
127
+ const real = found !== undefined && (found.flags & ts.SymbolFlags.Alias) !== 0 ? checker.getAliasedSymbol(found) : found;
128
+ const shapes = ts.SymbolFlags.Interface | ts.SymbolFlags.Class | ts.SymbolFlags.TypeAlias;
129
+ if (real !== undefined && own(real) && (real.flags & shapes) !== 0 && !into.has(real))
130
+ into.set(real, EVERY);
131
+ }
132
+ ts.forEachChild(one, take);
133
+ };
134
+ const inside = (one) => {
135
+ if (ts.isMappedTypeNode(one)) {
136
+ if (one.type !== undefined)
137
+ take(one.type);
138
+ return;
139
+ }
140
+ ts.forEachChild(one, inside);
141
+ };
142
+ inside(node);
143
+ }
144
+ function compiled() {
145
+ const pass = descend();
146
+ if (built !== undefined && built.of === pass)
147
+ return built;
148
+ const options = configured().options;
149
+ const roots = [THREAD_AT, ...sources()].filter((one) => SOURCE.test(one)).map((one) => resolve(ROOT, one));
150
+ const program = ts.createProgram([...new Set([OPENED_AT, ...roots])], options, hosting(options));
151
+ const checker = program.getTypeChecker();
152
+ const hands = new Map();
153
+ const own = (symbol) => (symbol.getDeclarations() ?? []).some((one) => {
154
+ const from = one.getSourceFile();
155
+ return !program.isSourceFileDefaultLibrary(from) && !program.isSourceFileFromExternalLibrary(from);
156
+ });
157
+ const shape = program.getSourceFile(OPENED_AT);
158
+ for (const statement of shape?.statements ?? []) {
159
+ if (!ts.isTypeAliasDeclaration(statement) || statement.name.text !== HANDED)
160
+ continue;
161
+ const alias = checker.getSymbolAtLocation(statement.name);
162
+ if (alias !== undefined)
163
+ hands.set(alias, EVERY);
164
+ handed(checker.getTypeFromTypeNode(statement.type), checker, EVERY, hands, new Set(), own);
165
+ given(statement.type, checker, hands, own);
166
+ }
167
+ built = { of: pass, program, checker, hands, packages: needed(pass) };
168
+ return built;
169
+ }
170
+ const INTEGRATIONS_ID = 'integrations';
171
+ function integral(pass, path) {
172
+ for (const [at, threadings] of pass.threaded) {
173
+ if (at !== path)
174
+ continue;
175
+ for (const one of threadings) {
176
+ if (one.by === INTEGRATIONS_ID || under(pass, one.by, INTEGRATIONS_ID))
177
+ return true;
178
+ }
179
+ }
180
+ return false;
181
+ }
182
+ function operational(pass, path) {
183
+ const prefixes = Object.values(PREFIXES);
184
+ const owned = (one) => prefixes.some((prefix) => one.startsWith(prefix));
185
+ if (path.split('/').some((part) => owned(part)))
186
+ return true;
187
+ for (const [at, threadings] of pass.threaded) {
188
+ if (at !== path)
189
+ continue;
190
+ for (const one of threadings) {
191
+ if (owned(one.by) || owned(String(one.row.id ?? '')))
192
+ return true;
193
+ }
194
+ }
195
+ return false;
196
+ }
197
+ function handOf(type, checker, hands, seen) {
198
+ if (seen.has(type))
199
+ return undefined;
200
+ seen.add(type);
201
+ const named = type.aliasSymbol === undefined ? undefined : hands.get(type.aliasSymbol);
202
+ if (named !== undefined)
203
+ return named;
204
+ if (type.isUnionOrIntersection()) {
205
+ for (const one of type.types) {
206
+ const found = handOf(one, checker, hands, seen);
207
+ if (found !== undefined)
208
+ return found;
209
+ }
210
+ return undefined;
211
+ }
212
+ const symbol = type.getSymbol() ?? type.aliasSymbol;
213
+ const own = symbol === undefined ? undefined : hands.get(symbol);
214
+ if (own !== undefined)
215
+ return own;
216
+ for (const one of argumentsOf(type, checker)) {
217
+ const found = handOf(one, checker, hands, seen);
218
+ if (found !== undefined)
219
+ return found;
220
+ }
221
+ return undefined;
222
+ }
223
+ function implemented(file, built) {
224
+ const found = new Set();
225
+ const { checker, hands } = built;
226
+ const held = checker.getSymbolAtLocation(file);
227
+ if (held === undefined)
228
+ return found;
229
+ const carries = (type, depth, seen) => {
230
+ if (seen.has(type))
231
+ return;
232
+ seen.add(type);
233
+ const hand = handOf(type, checker, hands, new Set());
234
+ if (hand !== undefined)
235
+ found.add(hand);
236
+ if (depth === 0)
237
+ return;
238
+ for (const one of type.isUnionOrIntersection() ? type.types : [type]) {
239
+ for (const signature of one.getCallSignatures())
240
+ carries(signature.getReturnType(), depth - 1, seen);
241
+ for (const property of one.getProperties())
242
+ carries(checker.getTypeOfSymbolAtLocation(property, file), depth - 1, seen);
243
+ }
244
+ };
245
+ for (const exported of checker.getExportsOfModule(held)) {
246
+ const real = (exported.flags & ts.SymbolFlags.Alias) !== 0 ? checker.getAliasedSymbol(exported) : exported;
247
+ if ((real.flags & ts.SymbolFlags.Value) === 0)
248
+ continue;
249
+ carries(checker.getTypeOfSymbolAtLocation(real, file), 2, new Set());
250
+ }
251
+ return found;
252
+ }
253
+ export const DOORWAY = 'door';
254
+ export const HANDED_TO = 'hand';
255
+ function judged(path, built, operates, wanted) {
256
+ const file = built.program.getSourceFile(resolve(ROOT, path));
257
+ if (file === undefined)
258
+ return [];
259
+ const { checker, hands } = built;
260
+ let door;
261
+ let allowed;
262
+ const may = (hand) => {
263
+ if (door === undefined)
264
+ door = integral(built.of, path);
265
+ if (allowed === undefined)
266
+ allowed = implemented(file, built);
267
+ return allowed.has(EVERY) || allowed.has(hand) || door || (operates && hand === EVERY);
268
+ };
269
+ const found = [];
270
+ const line = (node) => file.getLineAndCharacterOfPosition(node.getStart(file)).line + 1;
271
+ const note = (node, said, kind) => {
272
+ found.push({ line: line(node), said: 'line ' + String(line(node)) + ': ' + said, kind });
273
+ };
274
+ const locals = new Map();
275
+ const spaces = new Map();
276
+ const global = (id) => {
277
+ const symbol = checker.getSymbolAtLocation(id);
278
+ if (symbol === undefined)
279
+ return true;
280
+ const declared = symbol.getDeclarations() ?? [];
281
+ return declared.every((one) => one.getSourceFile().isDeclarationFile);
282
+ };
283
+ const named = (node) => {
284
+ const parent = node.parent;
285
+ return parent !== undefined && parent.name === node;
286
+ };
287
+ const inType = (node) => {
288
+ let up = node;
289
+ while (up !== undefined) {
290
+ if (ts.isTypeQueryNode(up))
291
+ return true;
292
+ up = up.parent;
58
293
  }
59
294
  return false;
60
- },
295
+ };
296
+ const reaching = (specifier, node) => {
297
+ const door = DOORS.modules.get(plain(specifier)) ?? built.packages.get(packageOf(specifier));
298
+ if (door !== undefined && !may(door.hand))
299
+ note(node, 'imports ' + specifier + stands(door), DOORWAY);
300
+ };
301
+ const visit = (node) => {
302
+ if (ts.isImportDeclaration(node) && node.importClause?.isTypeOnly !== true && ts.isStringLiteral(node.moduleSpecifier)) {
303
+ reaching(node.moduleSpecifier.text, node);
304
+ const held = DOORS.members.get(plain(node.moduleSpecifier.text));
305
+ const bindings = node.importClause?.namedBindings;
306
+ if (held !== undefined && bindings !== undefined) {
307
+ if (ts.isNamespaceImport(bindings))
308
+ spaces.set(bindings.name.text, held);
309
+ else {
310
+ for (const element of bindings.elements) {
311
+ const door = held.get((element.propertyName ?? element.name).text);
312
+ if (door !== undefined)
313
+ locals.set(element.name.text, door);
314
+ }
315
+ }
316
+ }
317
+ }
318
+ if (ts.isExportDeclaration(node) && !node.isTypeOnly && node.moduleSpecifier !== undefined && ts.isStringLiteral(node.moduleSpecifier)) {
319
+ reaching(node.moduleSpecifier.text, node);
320
+ }
321
+ if (ts.isCallExpression(node)) {
322
+ const first = node.arguments[0];
323
+ const loads = node.expression.kind === ts.SyntaxKind.ImportKeyword || (ts.isIdentifier(node.expression) && node.expression.text === REQUIRED);
324
+ if (loads && first !== undefined && ts.isStringLiteral(first))
325
+ reaching(first.text, node);
326
+ const callee = node.expression;
327
+ if (ts.isPropertyAccessExpression(callee) || ts.isElementAccessExpression(callee)) {
328
+ const taken = callee.expression;
329
+ const hand = handOf(checker.getTypeAtLocation(taken), checker, hands, new Set()) ??
330
+ (ts.isPropertyAccessExpression(taken) || ts.isElementAccessExpression(taken)
331
+ ? handOf(checker.getTypeAtLocation(taken.expression), checker, hands, new Set())
332
+ : undefined);
333
+ if (hand !== undefined && !may(hand)) {
334
+ const what = ts.isPropertyAccessExpression(callee) ? callee.name.text : 'an order';
335
+ note(node, 'calls ' + what + ' on ' + (hand === EVERY ? 'what this app hands an operation' : 'the ' + hand + ' it is handed'), HANDED_TO);
336
+ }
337
+ }
338
+ }
339
+ if (ts.isNewExpression(node) && ts.isIdentifier(node.expression) && (node.arguments ?? []).length === 0) {
340
+ const door = DOORS.made.get(node.expression.text);
341
+ if (door !== undefined && !may(door.hand) && global(node.expression))
342
+ note(node, 'reaches ' + door.spelling + '()' + stands(door), DOORWAY);
343
+ }
344
+ if (ts.isPropertyAccessExpression(node) && !ts.isPartOfTypeNode(node)) {
345
+ const inner = ts.isPropertyAccessExpression(node.expression) && ts.isIdentifier(node.expression.expression) && node.expression.expression.text === ANYWHERE ? node.expression.name : undefined;
346
+ const object = inner ?? node.expression;
347
+ if (ts.isIdentifier(object)) {
348
+ const spaced = spaces.get(object.text)?.get(node.name.text);
349
+ if (spaced !== undefined && !may(spaced.hand))
350
+ note(node, 'reaches ' + node.name.text + ' from ' + moduleOf(spaced) + stands(spaced), DOORWAY);
351
+ const door = DOORS.properties.get(object.text + '.' + node.name.text);
352
+ if (door !== undefined && !may(door.hand) && (inner !== undefined || global(object)))
353
+ note(node, 'reaches ' + door.spelling + stands(door), DOORWAY);
354
+ if (object.text === ANYWHERE) {
355
+ const bare = DOORS.globals.get(node.name.text);
356
+ if (bare !== undefined && !may(bare.hand))
357
+ note(node, 'reaches ' + bare.spelling + stands(bare), DOORWAY);
358
+ }
359
+ }
360
+ }
361
+ if (ts.isIdentifier(node) && !named(node) && !ts.isPartOfTypeNode(node) && !inType(node)) {
362
+ const local = locals.get(node.text);
363
+ if (local !== undefined && !may(local.hand))
364
+ note(node, 'reaches ' + node.text + ' from ' + moduleOf(local) + stands(local), DOORWAY);
365
+ const door = DOORS.globals.get(node.text);
366
+ if (door !== undefined && !may(door.hand) && global(node))
367
+ note(node, 'reaches ' + door.spelling + stands(door), DOORWAY);
368
+ }
369
+ ts.forEachChild(node, visit);
370
+ };
371
+ visit(file);
372
+ return found.filter((one) => one.kind === wanted).sort((one, two) => one.line - two.line).map((one) => one.said);
373
+ }
374
+ export function judging(at, wanted, closing) {
375
+ const path = here(at.filename);
376
+ if (!SOURCE.test(path))
377
+ return false;
378
+ const held = compiled();
379
+ if (held.hands.size === 0 && resolve(ROOT, path) === OPENED_AT) {
380
+ return 'no type called ' + HANDED + ' can be read here, so nothing can tell what an operation was opened with from any other value, and a call on it outside an operation goes unmeasured';
381
+ }
382
+ const found = judged(path, held, operational(held.of, path), wanted);
383
+ if (found.length === 0)
384
+ return false;
385
+ return found.join('; ') + '. ' + closing;
386
+ }
387
+ export const slopnator = {
388
+ holds: (_node, at) => judging(at, DOORWAY, 'Each of these reaches past this process without going through a declared integration, and a reach this app never declared is one nothing can price, replay or unwind.'),
61
389
  proves: {
62
390
  wrong: [
63
- "const res = await fetch('https://api.stripe.com/v1/charges')",
64
- 'const at = Date.now()',
65
- 'const id = crypto.randomUUID()',
66
- "await queue.enqueue('job', payload)",
67
- "await files.put('receipt.pdf', bytes)",
68
- 'const t = setInterval(() => refetch(), 5000)',
69
- 'const t = setTimeout(function again() { refetch(); setTimeout(again, 5000) }, 5000)',
70
- 'window.requestAnimationFrame(step)',
391
+ "import { spawnSync } from 'node:child_process'\n\nexport const ran = spawnSync('git', ['status'])",
392
+ "import * as processes from 'node:child_process'\n\nexport const ran = processes.spawnSync('git', ['status'])",
393
+ {
394
+ files: [
395
+ { at: 'threads/thread.ts', source: "export const THREAD: Threaded = {\n declares: [\n { id: 'wf_thing', at: 'operations/wf_thing.ts' },\n ],\n}\n" },
396
+ { at: 'operations/wf_thing.ts', source: "export const asked = await fetch('https://example.com')" },
397
+ ],
398
+ at: 'operations/wf_thing.ts',
399
+ },
400
+ "import { PGlite } from '@electric-sql/pglite'\n\nexport const db = new PGlite()",
401
+ {
402
+ files: [
403
+ { at: 'thread/core/thread.ts', source: 'export interface Threaded {\n readonly id?: string\n readonly at?: string\n readonly describes?: string\n readonly declares?: readonly Threaded[]\n}\n' },
404
+ { at: 'threads/thread.ts', source: "import type { Threaded } from '../thread/core/thread.ts'\n\nexport interface Integration extends Threaded {\n readonly needs?: Readonly<Record<string, string>>\n}\n\nexport const THREAD: Threaded = {\n declares: [\n { id: 'wf_pad', at: 'operations/wf_pad.ts' },\n ],\n}\n\nexport const INTEGRATIONS: readonly Integration[] = [\n { id: 'in_pad', describes: 'Pads text.', needs: { 'left-pad': '^1.3.0' } },\n]\n" },
405
+ { at: 'operations/wf_pad.ts', source: "import pad from 'left-pad'\n\nexport const padded = pad('a', 3)\n" },
406
+ ],
407
+ at: 'operations/wf_pad.ts',
408
+ },
71
409
  ],
72
410
  right: [
73
- "const node = { id: 'st_charge', kind: 'action', action: 'payment.charge', declares: { seams: ['http'] } }",
74
- 'const rows = useRead(recentOrders)',
75
- "const { data } = useRead(recentOrders, { refresh: 'every-5s' })",
411
+ 'export const began = Date.now()',
412
+ 'export const later = setTimeout(() => again(), 5000)',
413
+ "import { randomBytes } from 'node:crypto'\n\nexport const nonce = randomBytes(6).toString('hex')",
414
+ "import { join } from 'node:path'\n\nexport const at = join('a', 'b')",
415
+ "import { createHash } from 'node:crypto'\n\nexport const sum = createHash('sha256').update('a').digest('hex')",
416
+ 'export const when = new Date(1767225600000)',
417
+ {
418
+ files: [
419
+ { at: 'thread/operations/operations.ts', source: 'export interface Http {\n call(address: string): Promise<void>\n}\n\nexport type Opened<R extends string = never> = { readonly [K in R]: Http }\n' },
420
+ { at: 'threads/_probe_door.ts', source: "import type { Http } from '../thread/operations/operations.ts'\n\nexport const made: Http = {\n call: async (address) => {\n await fetch(address)\n },\n}\n" },
421
+ ],
422
+ at: 'threads/_probe_door.ts',
423
+ },
424
+ {
425
+ files: [
426
+ { at: 'thread/operations/operations.ts', source: 'export interface Store {\n insert(row: unknown): Promise<void>\n}\n\nexport type Opened<R extends string = never> = { readonly [K in R]: Store }\n' },
427
+ { at: 'threads/_probe_handed.ts', source: "import type { Opened } from '../thread/operations/operations.ts'\n\nexport const carried = (opened: Opened<'pg_main'>, run: (given: Opened<'pg_main'>) => Promise<void>): Promise<void> => run(opened)\n" },
428
+ ],
429
+ at: 'threads/_probe_handed.ts',
430
+ },
431
+ {
432
+ files: [
433
+ { at: 'thread/core/thread.ts', source: 'export interface Threaded {\n readonly id?: string\n readonly at?: string\n readonly describes?: string\n readonly declares?: readonly Threaded[]\n}\n' },
434
+ { at: 'thread/operations/operations.ts', source: 'export type Calling = { readonly [call: string]: (given?: unknown) => Promise<unknown> }\n\nexport type Opened<R extends string = never> = { readonly [K in R]: Calling }\n' },
435
+ { at: 'threads/thread.ts', source: "import type { Threaded } from '../thread/core/thread.ts'\n\nexport interface Integration extends Threaded {\n readonly needs?: Readonly<Record<string, string>>\n}\n\nexport const THREAD: Threaded = {\n declares: [\n { id: 'sdk_pad_calls', at: 'src/sdk/sdk_pad.ts' },\n ],\n}\n\nexport const INTEGRATIONS: readonly Integration[] = [\n { id: 'in_pad', describes: 'Pads text.', needs: { 'left-pad': '^1.3.0' } },\n]\n" },
436
+ { at: 'src/sdk/sdk_pad.ts', source: "import type { Calling } from '../../thread/operations/operations.ts'\nimport pad from 'left-pad'\n\nexport const calls: Calling = {\n padded: async (given) => pad(String(given), 3),\n}\n" },
437
+ ],
438
+ at: 'src/sdk/sdk_pad.ts',
439
+ },
76
440
  ],
77
441
  },
78
442
  };