@prestack/thread 0.0.1

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 (248) hide show
  1. package/CHANGELOG.md +192 -0
  2. package/dist/attach.d.ts +1 -0
  3. package/dist/attach.js +1 -0
  4. package/dist/core/actor.d.ts +12 -0
  5. package/dist/core/actor.js +8 -0
  6. package/dist/core/asking.d.ts +180 -0
  7. package/dist/core/asking.js +1109 -0
  8. package/dist/core/attach.d.ts +54 -0
  9. package/dist/core/attach.js +510 -0
  10. package/dist/core/chain.d.ts +16 -0
  11. package/dist/core/chain.js +114 -0
  12. package/dist/core/cli.d.ts +2 -0
  13. package/dist/core/cli.js +1682 -0
  14. package/dist/core/condition.d.ts +52 -0
  15. package/dist/core/condition.js +198 -0
  16. package/dist/core/descend.d.ts +81 -0
  17. package/dist/core/descend.js +879 -0
  18. package/dist/core/finding.d.ts +12 -0
  19. package/dist/core/finding.js +23 -0
  20. package/dist/core/generators/meta.d.ts +2 -0
  21. package/dist/core/generators/meta.js +823 -0
  22. package/dist/core/init.d.ts +15 -0
  23. package/dist/core/init.js +134 -0
  24. package/dist/core/invoked.d.ts +9 -0
  25. package/dist/core/invoked.js +41 -0
  26. package/dist/core/round.d.ts +1 -0
  27. package/dist/core/round.js +57 -0
  28. package/dist/core/rows.d.ts +26 -0
  29. package/dist/core/rows.js +354 -0
  30. package/dist/core/scope.d.ts +6 -0
  31. package/dist/core/scope.js +12 -0
  32. package/dist/core/thread.d.ts +26 -0
  33. package/dist/core/thread.js +5 -0
  34. package/dist/core/tree.d.ts +12 -0
  35. package/dist/core/tree.js +56 -0
  36. package/dist/core/types.d.ts +49 -0
  37. package/dist/core/types.js +311 -0
  38. package/dist/derivation/attach.d.ts +2 -0
  39. package/dist/derivation/attach.js +61 -0
  40. package/dist/derivation/generators/build.d.ts +16 -0
  41. package/dist/derivation/generators/build.js +217 -0
  42. package/dist/derivation/generators/check.d.ts +18 -0
  43. package/dist/derivation/generators/check.js +78 -0
  44. package/dist/derivation/generators/derivation.d.ts +3 -0
  45. package/dist/derivation/generators/derivation.js +273 -0
  46. package/dist/derivation/generators/determinism.d.ts +20 -0
  47. package/dist/derivation/generators/determinism.js +110 -0
  48. package/dist/derivation/generators/release.d.ts +3 -0
  49. package/dist/derivation/generators/release.js +183 -0
  50. package/dist/derivation/makers/release.d.ts +1 -0
  51. package/dist/derivation/makers/release.js +107 -0
  52. package/dist/derivation/release.d.ts +10 -0
  53. package/dist/derivation/release.js +12 -0
  54. package/dist/integrations/attach.d.ts +6 -0
  55. package/dist/integrations/attach.js +177 -0
  56. package/dist/integrations/generators/read.d.ts +1 -0
  57. package/dist/integrations/generators/read.js +92 -0
  58. package/dist/integrations/generators/sdk.d.ts +1 -0
  59. package/dist/integrations/generators/sdk.js +148 -0
  60. package/dist/integrations/generators/secret.d.ts +1 -0
  61. package/dist/integrations/generators/secret.js +233 -0
  62. package/dist/integrations/generators/target.d.ts +1 -0
  63. package/dist/integrations/generators/target.js +765 -0
  64. package/dist/integrations/generators/vault.d.ts +1 -0
  65. package/dist/integrations/generators/vault.js +164 -0
  66. package/dist/integrations/sdk.d.ts +6 -0
  67. package/dist/integrations/sdk.js +151 -0
  68. package/dist/integrations/secret.d.ts +16 -0
  69. package/dist/integrations/secret.js +30 -0
  70. package/dist/integrations/target.d.ts +49 -0
  71. package/dist/integrations/target.js +225 -0
  72. package/dist/integrations/vault.d.ts +12 -0
  73. package/dist/integrations/vault.js +104 -0
  74. package/dist/integrity/attach.d.ts +2 -0
  75. package/dist/integrity/attach.js +213 -0
  76. package/dist/integrity/check-threads.d.ts +10 -0
  77. package/dist/integrity/check-threads.js +153 -0
  78. package/dist/integrity/constraint.d.ts +16 -0
  79. package/dist/integrity/constraint.js +1105 -0
  80. package/dist/integrity/generators/compiler.d.ts +1 -0
  81. package/dist/integrity/generators/compiler.js +130 -0
  82. package/dist/integrity/generators/gate.d.ts +1 -0
  83. package/dist/integrity/generators/gate.js +172 -0
  84. package/dist/integrity/generators/issues.d.ts +1 -0
  85. package/dist/integrity/generators/issues.js +100 -0
  86. package/dist/integrity/generators/publish.d.ts +1 -0
  87. package/dist/integrity/generators/publish.js +186 -0
  88. package/dist/integrity/generators/rule.d.ts +2 -0
  89. package/dist/integrity/generators/rule.js +213 -0
  90. package/dist/integrity/generators/seal.d.ts +4 -0
  91. package/dist/integrity/generators/seal.js +101 -0
  92. package/dist/integrity/generators/trial.d.ts +39 -0
  93. package/dist/integrity/generators/trial.js +254 -0
  94. package/dist/integrity/lint.d.ts +50 -0
  95. package/dist/integrity/lint.js +161 -0
  96. package/dist/integrity/lsp/bin.d.ts +2 -0
  97. package/dist/integrity/lsp/bin.js +35 -0
  98. package/dist/integrity/lsp/diagnostics.d.ts +25 -0
  99. package/dist/integrity/lsp/diagnostics.js +57 -0
  100. package/dist/integrity/lsp/findings.d.ts +20 -0
  101. package/dist/integrity/lsp/findings.js +39 -0
  102. package/dist/integrity/lsp/lint.d.ts +7 -0
  103. package/dist/integrity/lsp/lint.js +95 -0
  104. package/dist/integrity/lsp/protocol.d.ts +20 -0
  105. package/dist/integrity/lsp/protocol.js +57 -0
  106. package/dist/integrity/lsp/server.d.ts +15 -0
  107. package/dist/integrity/lsp/server.js +171 -0
  108. package/dist/integrity/sealed.d.ts +1 -0
  109. package/dist/integrity/sealed.js +56 -0
  110. package/dist/integrity/slopnator.d.ts +90 -0
  111. package/dist/integrity/slopnator.js +267 -0
  112. package/dist/integrity/slopnators/sl_broken_lineage.d.ts +2 -0
  113. package/dist/integrity/slopnators/sl_broken_lineage.js +35 -0
  114. package/dist/integrity/slopnators/sl_declared_elsewhere.d.ts +2 -0
  115. package/dist/integrity/slopnators/sl_declared_elsewhere.js +33 -0
  116. package/dist/integrity/slopnators/sl_derived_drift.d.ts +8 -0
  117. package/dist/integrity/slopnators/sl_derived_drift.js +91 -0
  118. package/dist/integrity/slopnators/sl_hardcoded_style.d.ts +2 -0
  119. package/dist/integrity/slopnators/sl_hardcoded_style.js +46 -0
  120. package/dist/integrity/slopnators/sl_inert_island.d.ts +2 -0
  121. package/dist/integrity/slopnators/sl_inert_island.js +85 -0
  122. package/dist/integrity/slopnators/sl_inert_slopnator.d.ts +2 -0
  123. package/dist/integrity/slopnators/sl_inert_slopnator.js +76 -0
  124. package/dist/integrity/slopnators/sl_missing_row.d.ts +2 -0
  125. package/dist/integrity/slopnators/sl_missing_row.js +180 -0
  126. package/dist/integrity/slopnators/sl_no_barrel.d.ts +2 -0
  127. package/dist/integrity/slopnators/sl_no_barrel.js +41 -0
  128. package/dist/integrity/slopnators/sl_no_child_process.d.ts +2 -0
  129. package/dist/integrity/slopnators/sl_no_child_process.js +61 -0
  130. package/dist/integrity/slopnators/sl_no_comment.d.ts +2 -0
  131. package/dist/integrity/slopnators/sl_no_comment.js +41 -0
  132. package/dist/integrity/slopnators/sl_no_dispatch_bypass.d.ts +2 -0
  133. package/dist/integrity/slopnators/sl_no_dispatch_bypass.js +301 -0
  134. package/dist/integrity/slopnators/sl_no_import_cycle.d.ts +2 -0
  135. package/dist/integrity/slopnators/sl_no_import_cycle.js +42 -0
  136. package/dist/integrity/slopnators/sl_no_such_command.d.ts +2 -0
  137. package/dist/integrity/slopnators/sl_no_such_command.js +55 -0
  138. package/dist/integrity/slopnators/sl_only_takes_a_literal.d.ts +2 -0
  139. package/dist/integrity/slopnators/sl_only_takes_a_literal.js +59 -0
  140. package/dist/integrity/slopnators/sl_secret_literal.d.ts +2 -0
  141. package/dist/integrity/slopnators/sl_secret_literal.js +89 -0
  142. package/dist/integrity/slopnators/sl_stale_path.d.ts +2 -0
  143. package/dist/integrity/slopnators/sl_stale_path.js +92 -0
  144. package/dist/integrity/slopnators/sl_twice_meant.d.ts +2 -0
  145. package/dist/integrity/slopnators/sl_twice_meant.js +30 -0
  146. package/dist/integrity/slopnators/sl_twice_parented.d.ts +2 -0
  147. package/dist/integrity/slopnators/sl_twice_parented.js +34 -0
  148. package/dist/integrity/slopnators/sl_undeclared_crossing.d.ts +2 -0
  149. package/dist/integrity/slopnators/sl_undeclared_crossing.js +78 -0
  150. package/dist/integrity/slopnators/sl_undeclared_destination.d.ts +2 -0
  151. package/dist/integrity/slopnators/sl_undeclared_destination.js +56 -0
  152. package/dist/integrity/slopnators/sl_undeclared_element.d.ts +2 -0
  153. package/dist/integrity/slopnators/sl_undeclared_element.js +34 -0
  154. package/dist/integrity/slopnators/sl_undeclared_node.d.ts +2 -0
  155. package/dist/integrity/slopnators/sl_undeclared_node.js +59 -0
  156. package/dist/integrity/slopnators/sl_undeclared_operation.d.ts +2 -0
  157. package/dist/integrity/slopnators/sl_undeclared_operation.js +120 -0
  158. package/dist/integrity/slopnators/sl_undeclared_variant.d.ts +2 -0
  159. package/dist/integrity/slopnators/sl_undeclared_variant.js +117 -0
  160. package/dist/integrity/slopnators/sl_undescribed.d.ts +2 -0
  161. package/dist/integrity/slopnators/sl_undescribed.js +54 -0
  162. package/dist/integrity/slopnators/sl_ungoverned_island.d.ts +2 -0
  163. package/dist/integrity/slopnators/sl_ungoverned_island.js +58 -0
  164. package/dist/integrity/slopnators/sl_unkept_rows.d.ts +2 -0
  165. package/dist/integrity/slopnators/sl_unkept_rows.js +104 -0
  166. package/dist/integrity/slopnators/sl_unnamed_island.d.ts +2 -0
  167. package/dist/integrity/slopnators/sl_unnamed_island.js +37 -0
  168. package/dist/integrity/slopnators/sl_unpaired_member.d.ts +2 -0
  169. package/dist/integrity/slopnators/sl_unpaired_member.js +114 -0
  170. package/dist/integrity/slopnators/sl_unpaired_slopnator.d.ts +2 -0
  171. package/dist/integrity/slopnators/sl_unpaired_slopnator.js +60 -0
  172. package/dist/integrity/slopnators/sl_unprefixed_member.d.ts +2 -0
  173. package/dist/integrity/slopnators/sl_unprefixed_member.js +126 -0
  174. package/dist/integrity/slopnators/sl_unseamed_reach.d.ts +2 -0
  175. package/dist/integrity/slopnators/sl_unseamed_reach.js +73 -0
  176. package/dist/integrity/slopnators/sl_unthreaded.d.ts +56 -0
  177. package/dist/integrity/slopnators/sl_unthreaded.js +352 -0
  178. package/dist/integrity/slopnators/sl_untranslated_key.d.ts +2 -0
  179. package/dist/integrity/slopnators/sl_untranslated_key.js +157 -0
  180. package/dist/integrity/slopnators/sl_unversioned.d.ts +2 -0
  181. package/dist/integrity/slopnators/sl_unversioned.js +31 -0
  182. package/dist/integrity/sweep.d.ts +24 -0
  183. package/dist/integrity/sweep.js +264 -0
  184. package/dist/knowledge/attach.d.ts +2 -0
  185. package/dist/knowledge/attach.js +339 -0
  186. package/dist/knowledge/generators/docsgen.d.ts +1 -0
  187. package/dist/knowledge/generators/docsgen.js +145 -0
  188. package/dist/knowledge/generators/glossary.d.ts +1 -0
  189. package/dist/knowledge/generators/glossary.js +56 -0
  190. package/dist/knowledge/generators/skills.d.ts +1 -0
  191. package/dist/knowledge/generators/skills.js +222 -0
  192. package/dist/knowledge/glossary.d.ts +6 -0
  193. package/dist/knowledge/glossary.js +1 -0
  194. package/dist/operations/access.d.ts +27 -0
  195. package/dist/operations/access.js +61 -0
  196. package/dist/operations/attach.d.ts +6 -0
  197. package/dist/operations/attach.js +414 -0
  198. package/dist/operations/capabilities.d.ts +37 -0
  199. package/dist/operations/capabilities.js +35 -0
  200. package/dist/operations/cost.d.ts +14 -0
  201. package/dist/operations/cost.js +37 -0
  202. package/dist/operations/dispatch.d.ts +14 -0
  203. package/dist/operations/dispatch.js +327 -0
  204. package/dist/operations/error-results.d.ts +4 -0
  205. package/dist/operations/error-results.js +25 -0
  206. package/dist/operations/errors/catalog.d.ts +58 -0
  207. package/dist/operations/errors/catalog.js +91 -0
  208. package/dist/operations/errors/classes.d.ts +73 -0
  209. package/dist/operations/errors/classes.js +22 -0
  210. package/dist/operations/errors/errors.d.ts +52 -0
  211. package/dist/operations/errors/errors.js +257 -0
  212. package/dist/operations/generators/capability.d.ts +5 -0
  213. package/dist/operations/generators/capability.js +603 -0
  214. package/dist/operations/generators/compile.d.ts +2 -0
  215. package/dist/operations/generators/compile.js +541 -0
  216. package/dist/operations/generators/grant.d.ts +1 -0
  217. package/dist/operations/generators/grant.js +267 -0
  218. package/dist/operations/generators/operation.d.ts +1 -0
  219. package/dist/operations/generators/operation.js +811 -0
  220. package/dist/operations/generators/permits.d.ts +1 -0
  221. package/dist/operations/generators/permits.js +196 -0
  222. package/dist/operations/generators/seed.d.ts +20 -0
  223. package/dist/operations/generators/seed.js +285 -0
  224. package/dist/operations/generators/trial.d.ts +53 -0
  225. package/dist/operations/generators/trial.js +376 -0
  226. package/dist/operations/http.d.ts +30 -0
  227. package/dist/operations/http.js +121 -0
  228. package/dist/operations/makers/capability.d.ts +1 -0
  229. package/dist/operations/makers/capability.js +117 -0
  230. package/dist/operations/makers/trial.d.ts +1 -0
  231. package/dist/operations/makers/trial.js +413 -0
  232. package/dist/operations/memory.d.ts +104 -0
  233. package/dist/operations/memory.js +365 -0
  234. package/dist/operations/metering.d.ts +55 -0
  235. package/dist/operations/metering.js +247 -0
  236. package/dist/operations/nodes.d.ts +35 -0
  237. package/dist/operations/nodes.js +15 -0
  238. package/dist/operations/operations.d.ts +18 -0
  239. package/dist/operations/operations.js +5 -0
  240. package/dist/operations/queue-policy.d.ts +3 -0
  241. package/dist/operations/queue-policy.js +8 -0
  242. package/dist/operations/result.d.ts +19 -0
  243. package/dist/operations/result.js +10 -0
  244. package/dist/operations/seams.d.ts +239 -0
  245. package/dist/operations/seams.js +45 -0
  246. package/dist/operations/store.d.ts +14 -0
  247. package/dist/operations/store.js +1 -0
  248. package/package.json +16 -0
@@ -0,0 +1,91 @@
1
+ import { readFileSync } from 'node:fs';
2
+ import { resolve } from 'node:path';
3
+ import { atsIn } from "./sl_unthreaded.js";
4
+ import { reached } from "../../core/descend.js";
5
+ import { findTree } from "../../core/tree.js";
6
+ const ROOT = `${findTree().at}/`;
7
+ export function derivations(root) {
8
+ const found = [];
9
+ for (const at of reached()) {
10
+ let text;
11
+ try {
12
+ text = readFileSync(resolve(root, at), 'utf8');
13
+ }
14
+ catch {
15
+ continue;
16
+ }
17
+ const threadedDerives = new Set(atsIn(text, new Set(['derives'])));
18
+ for (const opens of text.matchAll(/^\s*derives:\s*\[/gm)) {
19
+ const from = opens.index ?? 0;
20
+ let depth = 1;
21
+ let cursor = from + opens[0].length;
22
+ while (cursor < text.length && depth > 0) {
23
+ const one = text[cursor];
24
+ if (one === '[')
25
+ depth += 1;
26
+ else if (one === ']')
27
+ depth -= 1;
28
+ cursor += 1;
29
+ }
30
+ const own = [];
31
+ let nesting = 0;
32
+ for (const line of text.slice(from, cursor).split('\n')) {
33
+ if (nesting === 1)
34
+ own.push(line);
35
+ for (const one of line) {
36
+ if (one === '[')
37
+ nesting += 1;
38
+ else if (one === ']')
39
+ nesting -= 1;
40
+ }
41
+ }
42
+ const block = own.join('\n');
43
+ for (const row of block.split(/^\s*\{$/m)) {
44
+ const at = /^\s*at:\s*'([^']+)'/m.exec(row)?.[1];
45
+ const by = /^\s*by:\s*'([^']+)'/m.exec(row)?.[1];
46
+ const runs = /^\s*runs:\s*'([^']+)'/m.exec(row)?.[1];
47
+ if (at !== undefined && threadedDerives.has(at))
48
+ found.push({ at, by, runs });
49
+ }
50
+ }
51
+ }
52
+ return found;
53
+ }
54
+ export const slopnator = {
55
+ checks: (root) => {
56
+ const findings = [];
57
+ const all = derivations(root);
58
+ const complete = all.filter((one) => one.by !== undefined && one.runs !== undefined);
59
+ const covered = new Set(complete.map((one) => one.at));
60
+ for (const at of new Set(all.map((one) => one.at))) {
61
+ if (covered.has(at))
62
+ continue;
63
+ findings.push(`${at} sits in a \`derives\` list and no row naming it states both \`by\` and \`runs\` — a byproduct nothing can re-run is a claim, and this check used to drop such a row in silence`);
64
+ }
65
+ return findings;
66
+ },
67
+ proves: {
68
+ wrong: [
69
+ {
70
+ files: [
71
+ { at: 'threads/_drift_probe.ts', source: "export const M: Threaded = {\n derives: [\n {\n at: 'scripts/_generated/gate.mjs',\n by: 'pnpm gate:generate',\n runs: 'pnpm no-such-check',\n },\n ],\n}\n" },
72
+ ],
73
+ at: 'threads/_drift_probe.ts',
74
+ },
75
+ {
76
+ files: [
77
+ { at: 'threads/_drift_probe.ts', source: "export const M: Threaded = {\n derives: [\n {\n at: 'scripts/finding.ts',\n },\n ],\n}\n" },
78
+ ],
79
+ at: 'threads/_drift_probe.ts',
80
+ },
81
+ ],
82
+ right: [
83
+ {
84
+ files: [
85
+ { at: 'threads/_drift_probe.ts', source: "export const M: Threaded = {\n derives: [\n {\n at: 'scripts/_generated/gate.mjs',\n by: 'pnpm gate:generate',\n runs: 'pnpm gate:check',\n },\n ],\n}\n" },
86
+ ],
87
+ at: 'threads/_drift_probe.ts',
88
+ },
89
+ ],
90
+ },
91
+ };
@@ -0,0 +1,2 @@
1
+ import type { Slopnator } from '../slopnator.ts';
2
+ export declare const slopnator: Slopnator;
@@ -0,0 +1,46 @@
1
+ import ts from 'typescript';
2
+ const ALONE = /^\s*([a-z][a-z0-9]*(?:-[a-z0-9]+)*)\s*:\s*([^;{}\n]+);\s*$/;
3
+ const NAMED = /var\(\s*--/;
4
+ const PIECES = new Set([
5
+ ts.SyntaxKind.StringLiteral,
6
+ ts.SyntaxKind.NoSubstitutionTemplateLiteral,
7
+ ts.SyntaxKind.TemplateHead,
8
+ ts.SyntaxKind.TemplateMiddle,
9
+ ts.SyntaxKind.TemplateTail,
10
+ ]);
11
+ export const slopnator = {
12
+ holds: (_node, at) => {
13
+ const scanner = ts.createScanner(ts.ScriptTarget.Latest, false, ts.LanguageVariant.JSX, at.text);
14
+ let token = scanner.scan();
15
+ while (token !== ts.SyntaxKind.EndOfFileToken) {
16
+ if (PIECES.has(token)) {
17
+ const said = scanner.getTokenValue();
18
+ const found = ALONE.exec(said);
19
+ if (found !== null) {
20
+ const property = found[1] ?? '';
21
+ const value = (found[2] ?? '').trim();
22
+ if (value !== '' && !NAMED.test(value)) {
23
+ return (`\`${property}\` is written into the compiler with \`${value}\` typed into it, so a ` +
24
+ 'stylesheet carries a decision nothing declared — no rebrand moves it, no mode or theme reaches it, and no sheet knows it is there');
25
+ }
26
+ }
27
+ }
28
+ token = scanner.scan();
29
+ }
30
+ return false;
31
+ },
32
+ proves: {
33
+ wrong: [
34
+ "lines.push(' body {', ' background: #f8fbfa;', ' }')",
35
+ "classes.push(' padding: 8px 16px;')",
36
+ "return [' :where(*) {', ' text-box-trim: trim-both;', ' }']",
37
+ "held.push(' color-scheme: light dark;')",
38
+ ],
39
+ right: [
40
+ "lines.push(' body {', ' background: var(--colour-page);', ' }')",
41
+ 'lines.push(...block(selector, made, indent))',
42
+ 'held.push(` ${nameOf(id)}: ${made[id] ?? String()};`)',
43
+ 'const held: readonly string[] = [];',
44
+ ],
45
+ },
46
+ };
@@ -0,0 +1,2 @@
1
+ import type { Slopnator } from '../slopnator.ts';
2
+ export declare const slopnator: Slopnator;
@@ -0,0 +1,85 @@
1
+ import { existsSync, readFileSync } from 'node:fs';
2
+ import { resolve } from 'node:path';
3
+ import { declaresIsland, here, rowsIn } from "./sl_unthreaded.js";
4
+ import { findTree } from "../../core/tree.js";
5
+ const ROOT = findTree().at;
6
+ const TREE = `${findTree().name}/`;
7
+ function schemaAt(at) {
8
+ const path = resolve(ROOT, at);
9
+ if (!existsSync(path))
10
+ return undefined;
11
+ try {
12
+ return declaresIsland(readFileSync(path, 'utf8'));
13
+ }
14
+ catch {
15
+ return undefined;
16
+ }
17
+ }
18
+ export const slopnator = {
19
+ holds: (_node, at) => {
20
+ const path = here(at.filename);
21
+ if (!path.startsWith(TREE))
22
+ return false;
23
+ let rows;
24
+ try {
25
+ rows = rowsIn(at.text);
26
+ }
27
+ catch {
28
+ return 'this file cannot be parsed, so the islands it declares were never read and nothing about what they govern was measured';
29
+ }
30
+ for (const row of rows) {
31
+ if (row.at === undefined || row.id === undefined)
32
+ continue;
33
+ const schema = schemaAt(row.at);
34
+ if (schema === undefined)
35
+ continue;
36
+ if ((row.governs ?? []).length > 0)
37
+ continue;
38
+ return (`island "${row.id}" states nothing it governs, so ${schema}IslandSchema is a shape nothing is held to — ` +
39
+ 'no file is the place its kind is declared, which makes every file the place, and the island is inert');
40
+ }
41
+ return false;
42
+ },
43
+ proves: {
44
+ wrong: [
45
+ {
46
+ files: [
47
+ {
48
+ at: 'threads/_inert_probe.ts',
49
+ source: "export const M: Islanding = {\n declares: [\n { id: 'entities', at: 'packages/core/spec/src/islands/entities.ts' },\n ],\n}\n",
50
+ },
51
+ ],
52
+ at: 'threads/_inert_probe.ts',
53
+ },
54
+ {
55
+ files: [
56
+ {
57
+ at: 'threads/_inert_probe.ts',
58
+ source: "export const M: Islanding = {\n declares: [\n { id: 'events', at: 'packages/core/spec/src/islands/events.ts', governs: [] },\n ],\n}\n",
59
+ },
60
+ ],
61
+ at: 'threads/_inert_probe.ts',
62
+ },
63
+ ],
64
+ right: [
65
+ {
66
+ files: [
67
+ {
68
+ at: 'threads/_inert_probe.ts',
69
+ source: "export const M: Islanding = {\n declares: [\n { id: 'events', at: 'packages/core/spec/src/islands/events.ts', governs: ['app/_declare/events.ts'] },\n ],\n}\n",
70
+ },
71
+ ],
72
+ at: 'threads/_inert_probe.ts',
73
+ },
74
+ {
75
+ files: [
76
+ {
77
+ at: 'threads/_inert_probe.ts',
78
+ source: "export const M: Threaded = {\n declares: [\n { id: 'a-file', at: 'threads/thread.ts' },\n ],\n}\n",
79
+ },
80
+ ],
81
+ at: 'threads/_inert_probe.ts',
82
+ },
83
+ ],
84
+ },
85
+ };
@@ -0,0 +1,2 @@
1
+ import type { Slopnator } from '../slopnator.ts';
2
+ export declare const slopnator: Slopnator;
@@ -0,0 +1,76 @@
1
+ const keyOf = (node) => node.key?.name ?? (typeof node.key?.value === 'string' ? node.key.value : undefined);
2
+ const answersUnwritten = (held) => {
3
+ const fn = held;
4
+ if (fn?.type !== 'ArrowFunctionExpression')
5
+ return false;
6
+ return fn.body?.type === 'Identifier' && fn.body.name === 'NOT_WRITTEN';
7
+ };
8
+ const statesNothing = (held) => {
9
+ const proves = held;
10
+ if (proves?.type !== 'ObjectExpression')
11
+ return false;
12
+ return (proves.properties ?? []).some((one) => {
13
+ const key = keyOf(one);
14
+ if (key !== 'wrong' && key !== 'right')
15
+ return false;
16
+ const list = one.value;
17
+ return list?.type === 'ArrayExpression' && (list.elements ?? []).length === 0;
18
+ });
19
+ };
20
+ export const slopnator = {
21
+ on: ['Property'],
22
+ holds: (node) => {
23
+ const held = node;
24
+ const key = keyOf(held);
25
+ if (key === 'holds' && answersUnwritten(held.value)) {
26
+ return 'this is still the scaffold — it answers `NOT_WRITTEN` to everything, so the rule that names it registers, loads, reports nothing, and reads from outside exactly like a rule being obeyed';
27
+ }
28
+ if (key === 'on') {
29
+ const list = held.value;
30
+ if (list?.type === 'ArrayExpression' && (list.elements ?? []).length === 0) {
31
+ return 'it says it visits syntax and then names no node type, so it is handed nothing and governs nothing inside the files its rule governs';
32
+ }
33
+ }
34
+ if (key === 'proves' && statesNothing(held.value)) {
35
+ return 'its `proves` states nothing — with no form it refuses and none it accepts, no trial is derived from it, so nothing anywhere shows the slopnator does what its row claims';
36
+ }
37
+ return false;
38
+ },
39
+ proves: {
40
+ wrong: [
41
+ {
42
+ files: [
43
+ {
44
+ at: 'packages/core/src/integrity/slopnators/sl_probe_inert.ts',
45
+ source: 'export const slopnator = {\n on: [],\n holds: () => NOT_WRITTEN,\n proves: { wrong: [], right: [] },\n}\n',
46
+ },
47
+ ],
48
+ at: 'packages/core/src/integrity/slopnators/sl_probe_inert.ts',
49
+ },
50
+ {
51
+ files: [
52
+ {
53
+ at: 'packages/core/src/integrity/slopnators/sl_probe_inert.ts',
54
+ source: "export const slopnator = {\n holds: (_node, at) => at.text.includes('nope') && 'found',\n proves: { wrong: [], right: ['const held = 1'] },\n}\n",
55
+ },
56
+ ],
57
+ at: 'packages/core/src/integrity/slopnators/sl_probe_inert.ts',
58
+ },
59
+ ],
60
+ right: [
61
+ {
62
+ files: [
63
+ {
64
+ at: 'packages/core/src/integrity/slopnators/sl_probe_inert.ts',
65
+ source: "export const slopnator = {\n holds: (_node, at) => at.text.includes('nope') && 'found',\n proves: { wrong: ['nope'], right: ['const held = 1'] },\n}\n",
66
+ },
67
+ ],
68
+ at: 'packages/core/src/integrity/slopnators/sl_probe_inert.ts',
69
+ },
70
+ {
71
+ files: [{ at: 'packages/core/src/integrity/slopnators/sl_probe_inert.ts', source: 'export const held = 1\n' }],
72
+ at: 'packages/core/src/integrity/slopnators/sl_probe_inert.ts',
73
+ },
74
+ ],
75
+ },
76
+ };
@@ -0,0 +1,2 @@
1
+ import type { Slopnator } from '../slopnator.ts';
2
+ export declare const slopnator: Slopnator;
@@ -0,0 +1,180 @@
1
+ import { existsSync, readFileSync, readdirSync, statSync } from 'node:fs';
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
+ const ROOT = findTree().at;
13
+ /** A node: a const annotated `Threaded`. */
14
+ const NODE = /export\s+const\s+[A-Za-z_$][\w$]*\s*:\s*Threaded\b/;
15
+ /**
16
+ * Where a node's rows are written.
17
+ *
18
+ * A node may open the list inline, or name a `declares` array declared beside it and reference it by
19
+ * shorthand. Only the first was read, so `@thread/ui`'s tokens and signals nodes — 107 of its 178
20
+ * shipped rows — reported as shipping nothing, and every one of them could be deleted in silence.
21
+ */
22
+ const SHAPES = [/declares:\s*\[/g, /export\s+const\s+declares\b[^\n]*?=\s*\[/g];
23
+ /**
24
+ * How a row states its id.
25
+ *
26
+ * A row may be an object literal, or a call to a factory declared beside the list that takes the id
27
+ * first. Only the literal was read, which is the other half of why tokens and signals were invisible.
28
+ */
29
+ const ID = /\bid:\s*'([^']+)'/g;
30
+ const CALLED = /(?:^|[\s,[(])[a-zA-Z_$][\w$]*\(\s*'([^']+)'/g;
31
+ /** Strings out first, so a fixture that PRINTS a declaration is not read as being one. */
32
+ const QUOTED = /"(?:[^"\\\n]|\\.)*"|`(?:[^`\\]|\\.)*`/g;
33
+ function closes(code, from) {
34
+ let depth = 0;
35
+ for (let at = from; at < code.length; at += 1) {
36
+ if (code[at] === '[')
37
+ depth += 1;
38
+ else if (code[at] === ']') {
39
+ depth -= 1;
40
+ if (depth === 0)
41
+ return at;
42
+ }
43
+ }
44
+ return -1;
45
+ }
46
+ /**
47
+ * What a package SHIPS: only the ids inside a `declares` list.
48
+ *
49
+ * It was every id anywhere in a file that held one such list, which read an `Appending`'s own `id`
50
+ * as a shipped row — `@thread/operations` was reported as shipping `seed-generator`, the name of a
51
+ * row it asks an app to APPEND into another list, not one it declares.
52
+ */
53
+ function shippedIn(text) {
54
+ const code = text.replace(QUOTED, '""');
55
+ if (!NODE.test(code))
56
+ return new Set();
57
+ const found = new Set();
58
+ for (const shape of SHAPES) {
59
+ for (const one of code.matchAll(shape)) {
60
+ const opens = (one.index ?? 0) + one[0].length - 1;
61
+ const shut = closes(code, opens);
62
+ if (shut === -1)
63
+ continue;
64
+ const rows = code.slice(opens, shut);
65
+ for (const held of rows.matchAll(ID))
66
+ found.add(held[1] ?? '');
67
+ for (const held of rows.matchAll(CALLED))
68
+ found.add(held[1] ?? '');
69
+ }
70
+ }
71
+ return found;
72
+ }
73
+ /**
74
+ * What a tree HOLDS: every id in it, whichever list it sits in.
75
+ *
76
+ * Gating this on `declares` made every row in `derivation.ts` invisible, because its node states no
77
+ * `declares` at all and its rows live in `GENERATORS`. A row plainly on disk read as missing, and
78
+ * running the command this law recommends would not have cleared the finding.
79
+ */
80
+ function heldIn(text) {
81
+ const code = text.replace(QUOTED, '""');
82
+ const found = new Set();
83
+ for (const one of code.matchAll(ID))
84
+ found.add(one[1] ?? '');
85
+ for (const one of code.matchAll(CALLED))
86
+ found.add(one[1] ?? '');
87
+ return found;
88
+ }
89
+ function filesIn(dir) {
90
+ try {
91
+ return readdirSync(dir).filter((one) => one.endsWith('.ts')).map((one) => join(dir, one));
92
+ }
93
+ catch {
94
+ return [];
95
+ }
96
+ }
97
+ /** Every installed thread package that ships nodes of its own. */
98
+ function installed() {
99
+ const at = join(ROOT, 'node_modules', ORGANISATION);
100
+ const found = [];
101
+ for (const one of safely(at)) {
102
+ const name = `${ORGANISATION}/${one}`;
103
+ if (!ours(name))
104
+ continue;
105
+ const src = join(at, one, 'dist');
106
+ if (existsSync(src) && statSync(src).isDirectory())
107
+ found.push({ name, src });
108
+ }
109
+ return found;
110
+ }
111
+ function safely(dir) {
112
+ try {
113
+ return readdirSync(dir);
114
+ }
115
+ catch {
116
+ return [];
117
+ }
118
+ }
119
+ function read(path) {
120
+ try {
121
+ return readFileSync(path, 'utf8');
122
+ }
123
+ catch {
124
+ return '';
125
+ }
126
+ }
127
+ export const slopnator = {
128
+ checks: () => {
129
+ const tree = findTree();
130
+ /* Every id the tree holds, across every node in it. */
131
+ const held = new Set();
132
+ for (const path of filesIn(tree.dir))
133
+ for (const one of heldIn(read(path)))
134
+ held.add(one);
135
+ /*
136
+ * Only packages this tree ATTACHED, not every one installed.
137
+ *
138
+ * Measured by running it: the first version named `@thread/knowledge`, which the audit app has
139
+ * in `node_modules` and never attached — so the law demanded rows for a package nobody had
140
+ * asked for. A package is attached when the tree names a file inside it; one nothing points at
141
+ * is a dependency, and a dependency ships nothing this tree promised to hold.
142
+ */
143
+ const wired = new Set();
144
+ for (const path of filesIn(tree.dir)) {
145
+ for (const one of read(path).matchAll(/node_modules\/(@[\w.-]+\/[\w.-]+)\//g))
146
+ wired.add(one[1] ?? '');
147
+ }
148
+ const found = [];
149
+ for (const pkg of installed()) {
150
+ if (!wired.has(pkg.name))
151
+ continue;
152
+ for (const path of filesIn(pkg.src)) {
153
+ const shipped = shippedIn(read(path));
154
+ if (shipped.size === 0)
155
+ continue;
156
+ for (const id of [...shipped].sort()) {
157
+ /* The node's own id names the list, not a row in it. */
158
+ if (held.has(id))
159
+ continue;
160
+ found.push(`${pkg.name} ships \`${id}\` and this tree does not hold it — run \`thread attach ${pkg.name}\` to put it back`);
161
+ }
162
+ }
163
+ }
164
+ return found;
165
+ },
166
+ proves: {
167
+ wrong: [
168
+ {
169
+ files: [{ at: '_threads/_probe_missing.ts', source: "import type { Threaded } from '../../core/thread.ts'\n\nexport const M: Threaded = {\n declares: [],\n}\n" }],
170
+ at: '_threads/_probe_missing.ts',
171
+ },
172
+ ],
173
+ right: [
174
+ {
175
+ files: [{ 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}\n" }],
176
+ at: '_threads/_probe_missing.ts',
177
+ },
178
+ ],
179
+ },
180
+ };
@@ -0,0 +1,2 @@
1
+ import type { Slopnator } from '../slopnator.ts';
2
+ export declare const slopnator: Slopnator;
@@ -0,0 +1,41 @@
1
+ import { here } from "./sl_unthreaded.js";
2
+ const BARREL_NAME = /(^|\/)index\.(ts|tsx|mts|cts|js|mjs|cjs)$/;
3
+ const CODE = /\.(ts|tsx|mts|cts|js|mjs|cjs)$/;
4
+ const SAYS = "can't do barrels lazy ass";
5
+ function reExportsOnly(text) {
6
+ const lines = text
7
+ .split('\n')
8
+ .map((one) => one.trim())
9
+ .filter((one) => one !== '');
10
+ if (lines.length === 0)
11
+ return false;
12
+ return lines.every((one) => one.startsWith('export *') || one.startsWith('export {') || one.startsWith('}') || one.startsWith('} from') || /^[A-Za-z_$][\w$]*,?$/.test(one) || /^type [A-Za-z_$][\w$]*,?$/.test(one));
13
+ }
14
+ export const slopnator = {
15
+ holds: (_node, at) => {
16
+ const path = here(at.filename);
17
+ if (!CODE.test(path))
18
+ return false;
19
+ if (BARREL_NAME.test(path)) {
20
+ return `${SAYS} — this file is named for the folder it sits in, so it exists to list what the folder holds, and the tree already says that`;
21
+ }
22
+ if (reExportsOnly(at.text)) {
23
+ return `${SAYS} — every line of this file is a re-export, so it holds no thing of its own and names nothing the tree has not already named`;
24
+ }
25
+ return false;
26
+ },
27
+ proves: {
28
+ wrong: [
29
+ {
30
+ files: [{ at: 'packages/_probe_barrel/index.ts', source: "export const held = 1\n" }],
31
+ at: 'packages/_probe_barrel/index.ts',
32
+ },
33
+ ],
34
+ right: [
35
+ {
36
+ files: [{ at: 'packages/_probe_barrel/held.ts', source: "export const held = 1\n" }],
37
+ at: 'packages/_probe_barrel/held.ts',
38
+ },
39
+ ],
40
+ },
41
+ };
@@ -0,0 +1,2 @@
1
+ import type { Slopnator } from '../slopnator.ts';
2
+ export declare const slopnator: Slopnator;
@@ -0,0 +1,61 @@
1
+ const STARTS_A_PROCESS = new Set(['node:child_process', 'child_process']);
2
+ const named = (held) => {
3
+ const one = held;
4
+ return one?.type === 'Literal' && typeof one.value === 'string' ? one.value : undefined;
5
+ };
6
+ export const slopnator = {
7
+ on: ['ImportDeclaration', 'ImportExpression', 'CallExpression'],
8
+ holds: (node) => {
9
+ const held = node;
10
+ const from = held.type === 'ImportDeclaration' || held.type === 'ImportExpression'
11
+ ? (named(held.source) ?? named(held.arguments?.[0]))
12
+ : held.callee?.name === 'require'
13
+ ? named(held.arguments?.[0])
14
+ : undefined;
15
+ if (from === undefined || !STARTS_A_PROCESS.has(from))
16
+ 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`;
18
+ },
19
+ proves: {
20
+ wrong: [
21
+ {
22
+ files: [
23
+ {
24
+ at: 'packages/core/src/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: 'packages/core/src/integrity/slopnators/sl_probe_spawn.ts',
29
+ },
30
+ {
31
+ files: [
32
+ {
33
+ at: 'packages/core/src/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
+ },
36
+ ],
37
+ at: 'packages/core/src/integrity/slopnators/sl_probe_spawn.ts',
38
+ },
39
+ ],
40
+ right: [
41
+ {
42
+ files: [
43
+ {
44
+ at: 'packages/core/src/integrity/slopnators/sl_probe_spawn.ts',
45
+ source: "import { readFileSync } from 'node:fs'\nexport const slopnator = { holds: () => false, proves: { wrong: ['a'], right: ['b'] } }\n",
46
+ },
47
+ ],
48
+ at: 'packages/core/src/integrity/slopnators/sl_probe_spawn.ts',
49
+ },
50
+ {
51
+ files: [
52
+ {
53
+ at: 'packages/core/src/integrity/slopnators/sl_probe_spawn.ts',
54
+ source: "export const slopnator = { holds: () => false, proves: { wrong: ['a'], right: ['b'] } }\n",
55
+ },
56
+ ],
57
+ at: 'packages/core/src/integrity/slopnators/sl_probe_spawn.ts',
58
+ },
59
+ ],
60
+ },
61
+ };
@@ -0,0 +1,2 @@
1
+ import type { Slopnator } from '../slopnator.ts';
2
+ export declare const slopnator: Slopnator;
@@ -0,0 +1,41 @@
1
+ const shebang = (text) => (text.startsWith('#!') ? text.slice(2, text.indexOf('\n')) : undefined);
2
+ const spoken = (text, all) => {
3
+ const line = shebang(text);
4
+ return all.filter((one, index) => !(index === 0 && one.value === line));
5
+ };
6
+ export const slopnator = {
7
+ on: ['Program'],
8
+ holds: (_node, at) => {
9
+ const all = spoken(at.text, at.comments());
10
+ if (all.length === 0)
11
+ return false;
12
+ const first = (all[0]?.value ?? '')
13
+ .split('\n')
14
+ .map((line) => line.trim().replace(/^\*+\s?/, ''))
15
+ .find((line) => line !== '') ?? '';
16
+ return `${String(all.length)} comment(s), the first saying "${first.slice(0, 60)}"`;
17
+ },
18
+ fixes: (_node, at) => {
19
+ const all = spoken(at.text, at.comments());
20
+ if (all.length === 0)
21
+ return undefined;
22
+ let out = at.text;
23
+ for (const one of [...all].sort((a, b) => b.range[0] - a.range[0])) {
24
+ out = out.slice(0, one.range[0]) + out.slice(one.range[1]);
25
+ }
26
+ return out.replace(/[ \t]+$/gm, '').replace(/\n{3,}/g, '\n\n');
27
+ },
28
+ proves: {
29
+ wrong: [
30
+ 'const a = 1 // a line comment',
31
+ '/* a block comment */\nconst a = 1',
32
+ '/** JSDoc on the export */\nexport const a = 1',
33
+ ],
34
+ right: [
35
+ 'const a = "// not a comment"',
36
+ 'const url = "https://example.com/docs"',
37
+ 'const a = 1',
38
+ '#!/usr/bin/env node\nconst a = 1',
39
+ ],
40
+ },
41
+ };
@@ -0,0 +1,2 @@
1
+ import type { Slopnator } from '../slopnator.ts';
2
+ export declare const slopnator: Slopnator;