@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
@@ -0,0 +1,250 @@
1
+ import { resolve } from 'node:path'
2
+
3
+ import ts from 'typescript'
4
+
5
+ import { ROOT, configured, hosting } from '../../core/descend.ts'
6
+ import { kind, readFileSync } from '../../integrations/disk.ts'
7
+ import { OPENS } from '../../integrations/target.ts'
8
+
9
+ import type { Slopnator } from '../slopnator.ts'
10
+ import { here } from './sl_unthreaded.ts'
11
+
12
+ const IMPORTED = 'import '
13
+
14
+ const MADE = 'new '
15
+
16
+ const RUNTIME = 'node:'
17
+
18
+ const ANYWHERE = 'globalThis'
19
+
20
+ const REQUIRED = 'require'
21
+
22
+ const DECLARED = /\.d\.([cm]?)ts$/
23
+
24
+ const READABLE = /\.(?:[cm]?[jt]s|[jt]sx)$/
25
+
26
+ const plain = (specifier: string): string => (specifier.startsWith(RUNTIME) ? specifier.slice(RUNTIME.length) : specifier)
27
+
28
+ interface Door {
29
+ readonly reach: string
30
+ readonly spelling: string
31
+ }
32
+
33
+ interface Doors {
34
+ readonly modules: ReadonlyMap<string, Door>
35
+ readonly globals: ReadonlyMap<string, Door>
36
+ readonly properties: ReadonlyMap<string, Door>
37
+ }
38
+
39
+ function doors(): Doors {
40
+ const modules = new Map<string, Door>()
41
+ const globals = new Map<string, Door>()
42
+ const properties = new Map<string, Door>()
43
+ for (const [reach, spellings] of Object.entries(OPENS)) {
44
+ for (const spelling of spellings) {
45
+ const door = { reach, spelling }
46
+ if (spelling.startsWith(IMPORTED)) {
47
+ const rest = spelling.slice(IMPORTED.length)
48
+ const dot = rest.lastIndexOf('.')
49
+ modules.set(plain(dot === -1 || rest.slice(dot).includes('/') ? rest : rest.slice(0, dot)), door)
50
+ continue
51
+ }
52
+ if (spelling.startsWith(MADE)) {
53
+ globals.set(spelling.slice(MADE.length), door)
54
+ continue
55
+ }
56
+ if (spelling.includes('.')) properties.set(spelling, door)
57
+ else globals.set(spelling, door)
58
+ }
59
+ }
60
+ return { modules, globals, properties }
61
+ }
62
+
63
+ const DOORS = doors()
64
+
65
+ interface Brought {
66
+ readonly specifier: string
67
+ readonly line: number
68
+ }
69
+
70
+ const files = new Map<string, ts.SourceFile>()
71
+
72
+ function parsed(at: string, text: string): ts.SourceFile {
73
+ const held = files.get(text)
74
+ if (held !== undefined) return held
75
+ const file = ts.createSourceFile(at, text, ts.ScriptTarget.Latest, true, at.endsWith('x') ? ts.ScriptKind.TSX : ts.ScriptKind.TS)
76
+ files.set(text, file)
77
+ return file
78
+ }
79
+
80
+ function brought(file: ts.SourceFile): readonly Brought[] {
81
+ const found: Brought[] = []
82
+ const line = (node: ts.Node): number => file.getLineAndCharacterOfPosition(node.getStart(file)).line + 1
83
+ const visit = (node: ts.Node): void => {
84
+ if (ts.isImportDeclaration(node) && node.importClause?.isTypeOnly !== true && ts.isStringLiteral(node.moduleSpecifier)) {
85
+ found.push({ specifier: node.moduleSpecifier.text, line: line(node) })
86
+ }
87
+ if (ts.isExportDeclaration(node) && !node.isTypeOnly && node.moduleSpecifier !== undefined && ts.isStringLiteral(node.moduleSpecifier)) {
88
+ found.push({ specifier: node.moduleSpecifier.text, line: line(node) })
89
+ }
90
+ if (ts.isCallExpression(node)) {
91
+ const first = node.arguments[0]
92
+ const loads = node.expression.kind === ts.SyntaxKind.ImportKeyword || (ts.isIdentifier(node.expression) && node.expression.text === REQUIRED)
93
+ if (loads && first !== undefined && ts.isStringLiteral(first)) found.push({ specifier: first.text, line: line(node) })
94
+ }
95
+ ts.forEachChild(node, visit)
96
+ }
97
+ visit(file)
98
+ return found
99
+ }
100
+
101
+ const binds = (node: ts.Identifier): boolean => {
102
+ const parent = node.parent
103
+ return (
104
+ (ts.isVariableDeclaration(parent) ||
105
+ ts.isParameter(parent) ||
106
+ ts.isBindingElement(parent) ||
107
+ ts.isFunctionDeclaration(parent) ||
108
+ ts.isClassDeclaration(parent) ||
109
+ ts.isImportSpecifier(parent) ||
110
+ ts.isImportClause(parent) ||
111
+ ts.isNamespaceImport(parent)) &&
112
+ parent.name === node
113
+ )
114
+ }
115
+
116
+ const named = (node: ts.Node): boolean => (node.parent as { readonly name?: ts.Node } | undefined)?.name === node
117
+
118
+ const opened = new Map<string, Door | undefined>()
119
+
120
+ function opening(at: string, text: string): Door | undefined {
121
+ if (opened.has(text)) return opened.get(text)
122
+ const file = parsed(at, text)
123
+ let found: Door | undefined
124
+ for (const one of brought(file)) found ??= DOORS.modules.get(plain(one.specifier))
125
+ const bound = new Set<string>()
126
+ const collect = (node: ts.Node): void => {
127
+ if (ts.isIdentifier(node) && binds(node)) bound.add(node.text)
128
+ ts.forEachChild(node, collect)
129
+ }
130
+ collect(file)
131
+ const visit = (node: ts.Node): void => {
132
+ if (found !== undefined) return
133
+ if (ts.isPropertyAccessExpression(node) && ts.isIdentifier(node.expression) && !ts.isPartOfTypeNode(node)) {
134
+ const object = node.expression.text
135
+ found = DOORS.properties.get(object + '.' + node.name.text) ?? (object === ANYWHERE ? DOORS.globals.get(node.name.text) : undefined)
136
+ }
137
+ if (found === undefined && ts.isIdentifier(node) && !named(node) && !ts.isPartOfTypeNode(node) && !bound.has(node.text)) {
138
+ found = DOORS.globals.get(node.text)
139
+ }
140
+ ts.forEachChild(node, visit)
141
+ }
142
+ visit(file)
143
+ opened.set(text, found)
144
+ return found
145
+ }
146
+
147
+ let options: ts.CompilerOptions | undefined
148
+
149
+ let host: ts.CompilerHost | undefined
150
+
151
+ function resolvedOf(specifier: string, from: string): string | undefined {
152
+ options ??= configured().options
153
+ host ??= hosting(options)
154
+ const found = ts.resolveModuleName(specifier, from, options, host).resolvedModule
155
+ if (found === undefined) return undefined
156
+ const at = found.resolvedFileName
157
+ if (!DECLARED.test(at)) return at
158
+ const emitted = at.replace(DECLARED, '.$1js')
159
+ return kind(emitted) === 'file' ? emitted : undefined
160
+ }
161
+
162
+ const readable = (at: string): string | undefined => (READABLE.test(at) && kind(at) === 'file' ? readFileSync(at, 'utf8') : undefined)
163
+
164
+ const shown = (chain: readonly string[], door: Door): string =>
165
+ chain.map((one) => '`' + one + '`').join(', which imports ') +
166
+ (door.spelling.startsWith(IMPORTED) ? ', which imports ' + door.spelling.slice(IMPORTED.length) : ', which reaches ' + door.spelling) +
167
+ ' — the ' +
168
+ door.reach +
169
+ ' reach, and no target declares it. Nothing prices that reach, replays it in a trial or refuses it. Declare the place with `thread create target`, require it on the operation, and call what the node is handed instead'
170
+
171
+ function through(at: string, chain: readonly string[], seen: Set<string>): string | undefined {
172
+ const text = readable(at)
173
+ if (text === undefined) return undefined
174
+ const door = opening(at, text)
175
+ if (door !== undefined) return shown(chain, door)
176
+ for (const one of brought(parsed(at, text))) {
177
+ const next = resolvedOf(one.specifier, at)
178
+ if (next === undefined || seen.has(next)) continue
179
+ seen.add(next)
180
+ const found = through(next, [...chain, one.specifier], seen)
181
+ if (found !== undefined) return found
182
+ }
183
+ return undefined
184
+ }
185
+
186
+ export const slopnator: Slopnator = {
187
+ holds: (_node, at) => {
188
+ const path = here(at.filename)
189
+ if (!READABLE.test(path)) return false
190
+ const from = resolve(ROOT, path)
191
+ const seen = new Set<string>([from])
192
+ for (const one of brought(parsed(from, at.text))) {
193
+ const next = resolvedOf(one.specifier, from)
194
+ if (next === undefined || seen.has(next)) continue
195
+ seen.add(next)
196
+ const found = through(next, [one.specifier], seen)
197
+ if (found !== undefined) return 'line ' + String(one.line) + ': imports ' + found
198
+ }
199
+ return false
200
+ },
201
+ proves: {
202
+ wrong: [
203
+ {
204
+ files: [
205
+ { at: 'thread/integrations/cli.ts', source: "import { spawn } from 'node:child_process'\n\nexport const starts = (program: string): void => {\n spawn(program)\n}\n" },
206
+ { at: 'operations/_probe_node.ts', source: "export { starts } from '../thread/integrations/cli.ts'\n" },
207
+ ],
208
+ at: 'operations/_probe_node.ts',
209
+ },
210
+ {
211
+ files: [
212
+ { at: 'thread/integrations/https.ts', source: "export const sendingHttp = async (address: string): Promise<number> => (await fetch(address)).status\n" },
213
+ { at: 'src/_probe_helper.ts', source: "import { sendingHttp } from '../thread/integrations/https.ts'\n\nexport const status = (address: string): Promise<number> => sendingHttp(address)\n" },
214
+ { at: 'operations/_probe_node.ts', source: "import { status } from '../src/_probe_helper.ts'\n\nexport const node = (address: string): Promise<number> => status(address)\n" },
215
+ ],
216
+ at: 'operations/_probe_node.ts',
217
+ },
218
+ {
219
+ files: [
220
+ { at: 'thread/integrations/disk.ts', source: "import * as disk from 'node:fs'\n\nexport const read = (at: string): string => disk.readFileSync(at, 'utf8')\n" },
221
+ { at: 'operations/_probe_node.ts', source: "export const node = async (at: string): Promise<string> => (await import('../thread/integrations/disk.ts')).read(at)\n" },
222
+ ],
223
+ at: 'operations/_probe_node.ts',
224
+ },
225
+ ],
226
+ right: [
227
+ {
228
+ files: [
229
+ { at: 'thread/operations/nodes.ts', source: "export const nd_action = <T>(run: T): T => run\n" },
230
+ { at: 'operations/_probe_node.ts', source: "import { nd_action } from '../thread/operations/nodes.ts'\n\nexport const node = nd_action(async () => 1)\n" },
231
+ ],
232
+ at: 'operations/_probe_node.ts',
233
+ },
234
+ {
235
+ files: [
236
+ { at: 'thread/integrations/cli.ts', source: "import { spawn } from 'node:child_process'\n\nexport interface Ran {\n readonly out: string\n}\n\nexport const starts = (program: string): void => {\n spawn(program)\n}\n" },
237
+ { at: 'operations/_probe_node.ts', source: "import type { Ran } from '../thread/integrations/cli.ts'\n\nexport const node = (ran: Ran): string => ran.out\n" },
238
+ ],
239
+ at: 'operations/_probe_node.ts',
240
+ },
241
+ {
242
+ files: [
243
+ { at: 'src/_probe_helper.ts', source: "const fetch = (said: string): string => said\n\nexport const shout = (said: string): string => fetch(said).toUpperCase()\n" },
244
+ { at: 'operations/_probe_node.ts', source: "import { shout } from '../src/_probe_helper.ts'\n\nexport const node = (): string => shout('a')\n" },
245
+ ],
246
+ at: 'operations/_probe_node.ts',
247
+ },
248
+ ],
249
+ },
250
+ }
@@ -1,7 +1,7 @@
1
- import { existsSync, readFileSync } from "../../core/files.js";
1
+ import { existsSync, readFileSync } from '../../integrations/disk.js';
2
2
  import { resolve } from 'node:path';
3
- import { declaresIsland, here, rowsIn } from "./sl_unthreaded.js";
4
- import { findTree } from "../../core/tree.js";
3
+ import { declaresIsland, here, rowsIn } from './sl_unthreaded.js';
4
+ import { findTree } from '../../core/tree.js';
5
5
  const ROOT = findTree().at;
6
6
  const TREE = `${findTree().name}/`;
7
7
  function schemaAt(at) {
@@ -60,17 +60,19 @@ export const slopnator = {
60
60
  right: [
61
61
  {
62
62
  files: [
63
- { at: 'threads/_inert_probe.ts', source: "export const M: Threaded = {\n declares: [\n { id: 'events', at: 'src/islands/events.ts', governs: ['app/_declare/events.ts'] },\n ],\n}\n" },
63
+ { at: 'thread/core/thread.ts', source: "export interface Threaded {\n readonly id?: string\n readonly at?: string\n readonly describes?: string\n readonly governs?: readonly string[]\n readonly declares?: readonly Threaded[]\n readonly derives?: readonly Threaded[]\n readonly seeds?: readonly Threaded[]\n}\n" },
64
+ { at: 'threads/thread.ts', source: "import type { Threaded } from '../thread/core/thread.ts'\n\nexport const THREAD: Threaded = {\n declares: [\n {\n at: 'threads/_inert_probe.ts',\n },\n ],\n}\n" },
65
+ { at: 'threads/_inert_probe.ts', source: "import type { Threaded } from '../thread/core/thread.ts'\n\nexport const M: Threaded = {\n declares: [\n { id: 'events', at: 'src/islands/events.ts', governs: ['app/_declare/events.ts'] },\n ],\n}\n" },
64
66
  { at: 'src/islands/events.ts', source: 'export const EventsIslandSchema = {}\n' },
65
67
  ],
66
68
  at: 'threads/_inert_probe.ts',
67
69
  },
68
70
  {
69
71
  files: [
70
- {
71
- at: 'threads/_inert_probe.ts',
72
- source: "export const M: Threaded = {\n declares: [\n { id: 'a-file', at: 'threads/thread.ts' },\n ],\n}\n",
73
- },
72
+ { 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 readonly derives?: readonly Threaded[]\n readonly seeds?: readonly Threaded[]\n}\n" },
73
+ { at: 'threads/thread.ts', source: "import type { Threaded } from '../thread/core/thread.ts'\n\nexport const THREAD: Threaded = {\n declares: [\n {\n at: 'threads/_inert_probe.ts',\n },\n ],\n}\n" },
74
+ { at: 'threads/_inert_probe.ts', source: "import type { Threaded } from '../thread/core/thread.ts'\n\nexport const M: Threaded = {\n declares: [\n { id: 'a-file', at: 'threads/_plain.ts' },\n ],\n}\n" },
75
+ { at: 'threads/_plain.ts', source: 'export const PLAIN = 1\n' },
74
76
  ],
75
77
  at: 'threads/_inert_probe.ts',
76
78
  },
@@ -1,4 +1,4 @@
1
- import { existsSync, readFileSync } from '../../core/files.ts'
1
+ import { existsSync, readFileSync } from '../../integrations/disk.ts'
2
2
  import { resolve } from 'node:path'
3
3
 
4
4
  import type { Slopnator } from '../slopnator.ts'
@@ -63,17 +63,19 @@ export const slopnator: Slopnator = {
63
63
  right: [
64
64
  {
65
65
  files: [
66
- { at: 'threads/_inert_probe.ts', source: "export const M: Threaded = {\n declares: [\n { id: 'events', at: 'src/islands/events.ts', governs: ['app/_declare/events.ts'] },\n ],\n}\n" },
66
+ { at: 'thread/core/thread.ts', source: "export interface Threaded {\n readonly id?: string\n readonly at?: string\n readonly describes?: string\n readonly governs?: readonly string[]\n readonly declares?: readonly Threaded[]\n readonly derives?: readonly Threaded[]\n readonly seeds?: readonly Threaded[]\n}\n" },
67
+ { at: 'threads/thread.ts', source: "import type { Threaded } from '../thread/core/thread.ts'\n\nexport const THREAD: Threaded = {\n declares: [\n {\n at: 'threads/_inert_probe.ts',\n },\n ],\n}\n" },
68
+ { at: 'threads/_inert_probe.ts', source: "import type { Threaded } from '../thread/core/thread.ts'\n\nexport const M: Threaded = {\n declares: [\n { id: 'events', at: 'src/islands/events.ts', governs: ['app/_declare/events.ts'] },\n ],\n}\n" },
67
69
  { at: 'src/islands/events.ts', source: 'export const EventsIslandSchema = {}\n' },
68
70
  ],
69
71
  at: 'threads/_inert_probe.ts',
70
72
  },
71
73
  {
72
74
  files: [
73
- {
74
- at: 'threads/_inert_probe.ts',
75
- source: "export const M: Threaded = {\n declares: [\n { id: 'a-file', at: 'threads/thread.ts' },\n ],\n}\n",
76
- },
75
+ { 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 readonly derives?: readonly Threaded[]\n readonly seeds?: readonly Threaded[]\n}\n" },
76
+ { at: 'threads/thread.ts', source: "import type { Threaded } from '../thread/core/thread.ts'\n\nexport const THREAD: Threaded = {\n declares: [\n {\n at: 'threads/_inert_probe.ts',\n },\n ],\n}\n" },
77
+ { at: 'threads/_inert_probe.ts', source: "import type { Threaded } from '../thread/core/thread.ts'\n\nexport const M: Threaded = {\n declares: [\n { id: 'a-file', at: 'threads/_plain.ts' },\n ],\n}\n" },
78
+ { at: 'threads/_plain.ts', source: 'export const PLAIN = 1\n' },
77
79
  ],
78
80
  at: 'threads/_inert_probe.ts',
79
81
  },
@@ -1,33 +1,11 @@
1
- import { existsSync, readFileSync, readdirSync, statSync } from "../../core/files.js";
1
+ import { existsSync, readFileSync, readdirSync, statSync } from '../../integrations/disk.js';
2
2
  import { join } from 'node:path';
3
- import { ORGANISATION, ours } from "../../core/scope.js";
4
- import { findTree } from "../../core/tree.js";
5
- /**
6
- * A row the package still ships and the tree no longer holds.
7
- *
8
- * Read as TEXT, deliberately. Eight laws were once added whose walks imported a workspace package,
9
- * plain Node could not resolve it, `load()` swallowed the failure, and all eight read from every
10
- * caller exactly like laws finding nothing. Nothing here imports anything but node builtins.
11
- */
12
- /** A node: a const annotated `Threaded`. */
3
+ import { ORGANISATION, ours } from '../../core/scope.js';
4
+ import { findTree } from '../../core/tree.js';
13
5
  const NODE = /export\s+const\s+[A-Za-z_$][\w$]*\s*:\s*Threaded\b/;
14
- /**
15
- * Where a node's rows are written.
16
- *
17
- * A node may open the list inline, or name a `declares` array declared beside it and reference it by
18
- * shorthand. Only the first was read, so `@thread/ui`'s tokens and signals nodes — 107 of its 178
19
- * shipped rows — reported as shipping nothing, and every one of them could be deleted in silence.
20
- */
21
6
  const SHAPES = [/declares:\s*\[/g, /export\s+const\s+declares\b[^\n]*?=\s*\[/g];
22
- /**
23
- * How a row states its id.
24
- *
25
- * A row may be an object literal, or a call to a factory declared beside the list that takes the id
26
- * first. Only the literal was read, which is the other half of why tokens and signals were invisible.
27
- */
28
7
  const ID = /\bid:\s*'([^']+)'/g;
29
8
  const CALLED = /(?:^|[\s,[(])[a-zA-Z_$][\w$]*\(\s*'([^']+)'/g;
30
- /** Strings out first, so a fixture that PRINTS a declaration is not read as being one. */
31
9
  const QUOTED = /"(?:[^"\\\n]|\\.)*"|`(?:[^`\\]|\\.)*`/g;
32
10
  function closes(code, from) {
33
11
  let depth = 0;
@@ -42,13 +20,6 @@ function closes(code, from) {
42
20
  }
43
21
  return -1;
44
22
  }
45
- /**
46
- * What a package SHIPS: only the ids inside a `declares` list.
47
- *
48
- * It was every id anywhere in a file that held one such list, which read an `Appending`'s own `id`
49
- * as a shipped row — `@thread/operations` was reported as shipping `seed-generator`, the name of a
50
- * row it asks an app to APPEND into another list, not one it declares.
51
- */
52
23
  function shippedIn(text) {
53
24
  const code = text.replace(QUOTED, '""');
54
25
  if (!NODE.test(code))
@@ -69,13 +40,6 @@ function shippedIn(text) {
69
40
  }
70
41
  return found;
71
42
  }
72
- /**
73
- * What a tree HOLDS: every id in it, whichever list it sits in.
74
- *
75
- * Gating this on `declares` made every row in `derivation.ts` invisible, because its node states no
76
- * `declares` at all and its rows live in `GENERATORS`. A row plainly on disk read as missing, and
77
- * running the command this law recommends would not have cleared the finding.
78
- */
79
43
  function heldIn(text) {
80
44
  const code = text.replace(QUOTED, '""');
81
45
  const found = new Set();
@@ -93,7 +57,6 @@ function filesIn(dir) {
93
57
  return [];
94
58
  }
95
59
  }
96
- /** Every installed thread package that ships nodes of its own. */
97
60
  function installed(root) {
98
61
  const at = join(root, 'node_modules', ORGANISATION);
99
62
  const found = [];
@@ -126,19 +89,10 @@ function read(path) {
126
89
  export const slopnator = {
127
90
  checks: (root) => {
128
91
  const tree = findTree(root);
129
- /* Every id the tree holds, across every node in it. */
130
92
  const held = new Set();
131
93
  for (const path of filesIn(tree.dir))
132
94
  for (const one of heldIn(read(path)))
133
95
  held.add(one);
134
- /*
135
- * Only packages this tree ATTACHED, not every one installed.
136
- *
137
- * Measured by running it: the first version named `@thread/knowledge`, which the audit app has
138
- * in `node_modules` and never attached — so the law demanded rows for a package nobody had
139
- * asked for. A package is attached when the tree names a file inside it; one nothing points at
140
- * is a dependency, and a dependency ships nothing this tree promised to hold.
141
- */
142
96
  const wired = new Set();
143
97
  for (const path of filesIn(tree.dir)) {
144
98
  for (const one of read(path).matchAll(/node_modules\/(@[\w.-]+\/[\w.-]+)\//g))
@@ -153,7 +107,6 @@ export const slopnator = {
153
107
  if (shipped.size === 0)
154
108
  continue;
155
109
  for (const id of [...shipped].sort()) {
156
- /* The node's own id names the list, not a row in it. */
157
110
  if (held.has(id))
158
111
  continue;
159
112
  found.push(`${pkg.name} ships \`${id}\` and this tree does not hold it — run \`thread attach ${pkg.name}\` to put it back`);
@@ -175,7 +128,9 @@ export const slopnator = {
175
128
  right: [
176
129
  {
177
130
  files: [
178
- { at: '_threads/_probe_missing.ts', source: "import type { Threaded } from '../../core/thread.ts'\n\nexport const M: Threaded = {\n declares: [\n {\n id: 'colour',\n },\n {\n id: 'missing-thing',\n },\n {\n at: 'node_modules/@prestack/thread-probe/dist/foo.ts',\n },\n ],\n}\n" },
131
+ { 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 readonly derives?: readonly Threaded[]\n readonly seeds?: readonly Threaded[]\n}\n" },
132
+ { at: '_threads/thread.ts', source: "import type { Threaded } from '../thread/core/thread.ts'\n\nexport const THREAD: Threaded = {\n declares: [\n {\n at: '_threads/_probe_missing.ts',\n },\n ],\n}\n" },
133
+ { at: '_threads/_probe_missing.ts', source: "import type { Threaded } from '../thread/core/thread.ts'\n\nexport const M: Threaded = {\n declares: [\n {\n id: 'colour',\n },\n {\n id: 'missing-thing',\n },\n {\n at: 'node_modules/@prestack/thread-probe/dist/foo.ts',\n },\n ],\n}\n" },
179
134
  { at: 'node_modules/@prestack/thread-probe/dist/foo.ts', source: "export const PKG: Threaded = {\n declares: [\n {\n id: 'missing-thing',\n },\n ],\n}\n" },
180
135
  ],
181
136
  at: '_threads/_probe_missing.ts',
@@ -1,40 +1,17 @@
1
- import { existsSync, readFileSync, readdirSync, statSync } from '../../core/files.ts'
1
+ import { existsSync, readFileSync, readdirSync, statSync } from '../../integrations/disk.ts'
2
2
  import { join } from 'node:path'
3
3
 
4
4
  import type { Slopnator } from '../slopnator.ts'
5
5
  import { ORGANISATION, ours } from '../../core/scope.ts'
6
6
  import { findTree } from '../../core/tree.ts'
7
7
 
8
- /**
9
- * A row the package still ships and the tree no longer holds.
10
- *
11
- * Read as TEXT, deliberately. Eight laws were once added whose walks imported a workspace package,
12
- * plain Node could not resolve it, `load()` swallowed the failure, and all eight read from every
13
- * caller exactly like laws finding nothing. Nothing here imports anything but node builtins.
14
- */
15
-
16
- /** A node: a const annotated `Threaded`. */
17
8
  const NODE = /export\s+const\s+[A-Za-z_$][\w$]*\s*:\s*Threaded\b/
18
9
 
19
- /**
20
- * Where a node's rows are written.
21
- *
22
- * A node may open the list inline, or name a `declares` array declared beside it and reference it by
23
- * shorthand. Only the first was read, so `@thread/ui`'s tokens and signals nodes — 107 of its 178
24
- * shipped rows — reported as shipping nothing, and every one of them could be deleted in silence.
25
- */
26
10
  const SHAPES = [/declares:\s*\[/g, /export\s+const\s+declares\b[^\n]*?=\s*\[/g]
27
11
 
28
- /**
29
- * How a row states its id.
30
- *
31
- * A row may be an object literal, or a call to a factory declared beside the list that takes the id
32
- * first. Only the literal was read, which is the other half of why tokens and signals were invisible.
33
- */
34
12
  const ID = /\bid:\s*'([^']+)'/g
35
13
  const CALLED = /(?:^|[\s,[(])[a-zA-Z_$][\w$]*\(\s*'([^']+)'/g
36
14
 
37
- /** Strings out first, so a fixture that PRINTS a declaration is not read as being one. */
38
15
  const QUOTED = /"(?:[^"\\\n]|\\.)*"|`(?:[^`\\]|\\.)*`/g
39
16
 
40
17
  function closes(code: string, from: number): number {
@@ -49,13 +26,6 @@ function closes(code: string, from: number): number {
49
26
  return -1
50
27
  }
51
28
 
52
- /**
53
- * What a package SHIPS: only the ids inside a `declares` list.
54
- *
55
- * It was every id anywhere in a file that held one such list, which read an `Appending`'s own `id`
56
- * as a shipped row — `@thread/operations` was reported as shipping `seed-generator`, the name of a
57
- * row it asks an app to APPEND into another list, not one it declares.
58
- */
59
29
  function shippedIn(text: string): ReadonlySet<string> {
60
30
  const code = text.replace(QUOTED, '""')
61
31
  if (!NODE.test(code)) return new Set()
@@ -73,13 +43,6 @@ function shippedIn(text: string): ReadonlySet<string> {
73
43
  return found
74
44
  }
75
45
 
76
- /**
77
- * What a tree HOLDS: every id in it, whichever list it sits in.
78
- *
79
- * Gating this on `declares` made every row in `derivation.ts` invisible, because its node states no
80
- * `declares` at all and its rows live in `GENERATORS`. A row plainly on disk read as missing, and
81
- * running the command this law recommends would not have cleared the finding.
82
- */
83
46
  function heldIn(text: string): ReadonlySet<string> {
84
47
  const code = text.replace(QUOTED, '""')
85
48
  const found = new Set<string>()
@@ -96,7 +59,6 @@ function filesIn(dir: string): readonly string[] {
96
59
  }
97
60
  }
98
61
 
99
- /** Every installed thread package that ships nodes of its own. */
100
62
  function installed(root: string): readonly { readonly name: string; readonly src: string }[] {
101
63
  const at = join(root, 'node_modules', ORGANISATION)
102
64
  const found: { name: string; src: string }[] = []
@@ -129,18 +91,9 @@ export const slopnator: Slopnator = {
129
91
  checks: (root) => {
130
92
  const tree = findTree(root)
131
93
 
132
- /* Every id the tree holds, across every node in it. */
133
94
  const held = new Set<string>()
134
95
  for (const path of filesIn(tree.dir)) for (const one of heldIn(read(path))) held.add(one)
135
96
 
136
- /*
137
- * Only packages this tree ATTACHED, not every one installed.
138
- *
139
- * Measured by running it: the first version named `@thread/knowledge`, which the audit app has
140
- * in `node_modules` and never attached — so the law demanded rows for a package nobody had
141
- * asked for. A package is attached when the tree names a file inside it; one nothing points at
142
- * is a dependency, and a dependency ships nothing this tree promised to hold.
143
- */
144
97
  const wired = new Set<string>()
145
98
  for (const path of filesIn(tree.dir)) {
146
99
  for (const one of read(path).matchAll(/node_modules\/(@[\w.-]+\/[\w.-]+)\//g)) wired.add(one[1] ?? '')
@@ -153,7 +106,6 @@ export const slopnator: Slopnator = {
153
106
  const shipped = shippedIn(read(path))
154
107
  if (shipped.size === 0) continue
155
108
  for (const id of [...shipped].sort()) {
156
- /* The node's own id names the list, not a row in it. */
157
109
  if (held.has(id)) continue
158
110
  found.push(
159
111
  `${pkg.name} ships \`${id}\` and this tree does not hold it — run \`thread attach ${pkg.name}\` to put it back`,
@@ -176,7 +128,9 @@ export const slopnator: Slopnator = {
176
128
  right: [
177
129
  {
178
130
  files: [
179
- { at: '_threads/_probe_missing.ts', source: "import type { Threaded } from '../../core/thread.ts'\n\nexport const M: Threaded = {\n declares: [\n {\n id: 'colour',\n },\n {\n id: 'missing-thing',\n },\n {\n at: 'node_modules/@prestack/thread-probe/dist/foo.ts',\n },\n ],\n}\n" },
131
+ { 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 readonly derives?: readonly Threaded[]\n readonly seeds?: readonly Threaded[]\n}\n" },
132
+ { at: '_threads/thread.ts', source: "import type { Threaded } from '../thread/core/thread.ts'\n\nexport const THREAD: Threaded = {\n declares: [\n {\n at: '_threads/_probe_missing.ts',\n },\n ],\n}\n" },
133
+ { at: '_threads/_probe_missing.ts', source: "import type { Threaded } from '../thread/core/thread.ts'\n\nexport const M: Threaded = {\n declares: [\n {\n id: 'colour',\n },\n {\n id: 'missing-thing',\n },\n {\n at: 'node_modules/@prestack/thread-probe/dist/foo.ts',\n },\n ],\n}\n" },
180
134
  { at: 'node_modules/@prestack/thread-probe/dist/foo.ts', source: "export const PKG: Threaded = {\n declares: [\n {\n id: 'missing-thing',\n },\n ],\n}\n" },
181
135
  ],
182
136
  at: '_threads/_probe_missing.ts',
@@ -1,4 +1,4 @@
1
- import { here } from "./sl_unthreaded.js";
1
+ import { here } from './sl_unthreaded.js';
2
2
  const BARREL_NAME = /(^|\/)index\.(ts|tsx|mts|cts|js|mjs|cjs)$/;
3
3
  const CODE = /\.(ts|tsx|mts|cts|js|mjs|cjs)$/;
4
4
  const SAYS = "can't do barrels lazy ass";
@@ -1,38 +1,56 @@
1
- const STARTS_A_PROCESS = new Set(['node:child_process', 'child_process']);
1
+ import { dirname, join, normalize } from 'node:path';
2
+ import { ROOT, descend, read, reached } from '../../core/descend.js';
3
+ import { rowsIn } from './sl_unthreaded.js';
4
+ const DOOR = 'programs';
5
+ const ROOTED = ROOT + '/';
6
+ const slash = (one) => one.split('\\').join('/');
7
+ const bare = (one) => one.replace(/\.[^./]+$/, '');
8
+ function doorway() {
9
+ descend();
10
+ for (const at of reached()) {
11
+ const text = read(at);
12
+ if (text === undefined)
13
+ continue;
14
+ for (const one of rowsIn(text)) {
15
+ if (one.id === DOOR && one.at !== undefined)
16
+ return one.at;
17
+ }
18
+ }
19
+ return undefined;
20
+ }
2
21
  const named = (held) => {
3
22
  const one = held;
4
23
  return one?.type === 'Literal' && typeof one.value === 'string' ? one.value : undefined;
5
24
  };
6
25
  export const slopnator = {
7
26
  on: ['ImportDeclaration', 'ImportExpression', 'CallExpression'],
8
- holds: (node) => {
27
+ holds: (node, at) => {
9
28
  const held = node;
10
29
  const from = held.type === 'ImportDeclaration' || held.type === 'ImportExpression'
11
30
  ? (named(held.source) ?? named(held.arguments?.[0]))
12
31
  : held.callee?.name === 'require'
13
32
  ? named(held.arguments?.[0])
14
33
  : undefined;
15
- if (from === undefined || !STARTS_A_PROCESS.has(from))
34
+ if (from === undefined)
16
35
  return false;
17
- return `it imports \`${from}\`, so this slopnator can start a process — and whatever it starts can reach back into the enforcement that called it`;
36
+ const door = doorway();
37
+ if (door === undefined)
38
+ return false;
39
+ const here = at.filename.startsWith(ROOTED) ? at.filename.slice(ROOTED.length) : at.filename;
40
+ const aimed = from.startsWith('.') ? slash(normalize(join(dirname(here), from))) : from;
41
+ if (bare(aimed) !== bare(door))
42
+ return false;
43
+ return ('it imports ' + door + ', which is where every process this framework starts comes from. A check that can reach it can ' +
44
+ 'start something, and whatever it starts can reach back into the enforcement that called it');
18
45
  },
19
46
  proves: {
20
47
  wrong: [
21
48
  {
22
49
  files: [
23
- {
24
- at: 'thread/integrity/slopnators/sl_probe_spawn.ts',
25
- source: "import { spawnSync } from 'node:child_process'\nexport const slopnator = { holds: () => false, proves: { wrong: ['a'], right: ['b'] } }\n",
26
- },
27
- ],
28
- at: 'thread/integrity/slopnators/sl_probe_spawn.ts',
29
- },
30
- {
31
- files: [
32
- {
33
- at: 'thread/integrity/slopnators/sl_probe_spawn.ts',
34
- source: "const held = await import('child_process')\nexport const slopnator = { holds: () => false, proves: { wrong: ['a'], right: ['b'] } }\n",
35
- },
50
+ { 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 readonly derives?: readonly Threaded[]\n readonly seeds?: readonly Threaded[]\n}\n" },
51
+ { at: 'threads/thread.ts', source: "import type { Threaded } from '../thread/core/thread.ts'\n\nexport const THREAD: Threaded = {\n declares: [\n {\n at: 'threads/_probe_programs.ts',\n },\n ],\n}\n" },
52
+ { at: 'threads/_probe_programs.ts', source: "import type { Threaded } from '../thread/core/thread.ts'\n\nexport const INTEGRATIONS: Threaded = {\n id: 'integrations',\n declares: [\n {\n id: 'programs',\n at: 'thread/integrations/cli.ts',\n },\n ],\n}\n" },
53
+ { at: 'thread/integrity/slopnators/sl_probe_spawn.ts', source: "import { starts } from '../../integrations/cli.ts'\nexport const slopnator = { holds: () => false, proves: { wrong: ['a'], right: ['b'] } }\n" },
36
54
  ],
37
55
  at: 'thread/integrity/slopnators/sl_probe_spawn.ts',
38
56
  },
@@ -42,7 +60,7 @@ export const slopnator = {
42
60
  files: [
43
61
  {
44
62
  at: 'thread/integrity/slopnators/sl_probe_spawn.ts',
45
- source: "import { readFileSync } from 'node:fs'\nexport const slopnator = { holds: () => false, proves: { wrong: ['a'], right: ['b'] } }\n",
63
+ source: "import { dirname } from 'node:path'\nexport const slopnator = { holds: () => false, proves: { wrong: ['a'], right: ['b'] } }\n",
46
64
  },
47
65
  ],
48
66
  at: 'thread/integrity/slopnators/sl_probe_spawn.ts',