@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,352 @@
1
+ import { parser } from 'typescript-eslint';
2
+ import { descend, forget as forgetDescent, read, reached as fromThread, threading } from "../../core/descend.js";
3
+ import { findTree } from "../../core/tree.js";
4
+ const TREE_AT = findTree();
5
+ const ROOT = `${TREE_AT.at.split('\\').join('/')}/`;
6
+ const TREE = TREE_AT.name;
7
+ const THREAD_AT = `${TREE_AT.name}/thread.ts`;
8
+ const FOLLOWABLE = /\.(ts|tsx|mts|cts|mjs|cjs|js)$/;
9
+ const LISTS = new Set(['declares', 'derives', 'seeds']);
10
+ let threaders;
11
+ let tree;
12
+ const parsed = new Map();
13
+ const broke = new Map();
14
+ const SAID = new Set(['at', 'id', 'describes']);
15
+ const keyOf = (property) => {
16
+ if (property.computed === true)
17
+ return undefined;
18
+ const key = property.key;
19
+ if (key?.type === 'Identifier')
20
+ return key.name;
21
+ if (key?.type === 'Literal' && typeof key.value === 'string')
22
+ return key.value;
23
+ return undefined;
24
+ };
25
+ function annotated(node) {
26
+ let held = node.id?.typeAnnotation?.typeAnnotation;
27
+ while (held !== undefined && (held.type === 'TSArrayType' || held.type === 'TSTypeOperator')) {
28
+ held = held.elementType ?? held.typeAnnotation;
29
+ }
30
+ return held?.typeName?.name;
31
+ }
32
+ export const here = (filename) => {
33
+ const path = filename.split('\\').join('/');
34
+ return path.startsWith(ROOT) ? path.slice(ROOT.length) : path;
35
+ };
36
+ function rows(node, list, found, hoisted) {
37
+ for (const one of node.elements ?? []) {
38
+ const element = one?.type === 'Identifier' && one.name !== undefined ? hoisted.get(one.name) : one;
39
+ if (element === null || element === undefined || element.type !== 'ObjectExpression')
40
+ continue;
41
+ const said = {};
42
+ for (const property of element.properties ?? []) {
43
+ const key = keyOf(property);
44
+ const held = property.value;
45
+ if (key !== undefined && SAID.has(key) && held?.type === 'Literal' && typeof held.value === 'string') {
46
+ said[key] = held.value;
47
+ }
48
+ if ((key === 'governs' || key === 'wears') && held?.type === 'ArrayExpression') {
49
+ said[key] = (held.elements ?? [])
50
+ .map((one) => (one?.type === 'Literal' && typeof one.value === 'string' ? one.value : undefined))
51
+ .filter((one) => one !== undefined);
52
+ }
53
+ if (key !== undefined && LISTS.has(key) && held?.type === 'ArrayExpression')
54
+ rows(held, key, found, hoisted);
55
+ }
56
+ if (said.at !== undefined || said.id !== undefined)
57
+ found.push({ list, ...said });
58
+ }
59
+ }
60
+ function reaching(text) {
61
+ const already = parsed.get(text);
62
+ if (already !== undefined)
63
+ return already;
64
+ const types = threading();
65
+ const found = [];
66
+ const parse = (jsx) => parser.parseForESLint(text, {
67
+ range: true,
68
+ ecmaVersion: 'latest',
69
+ sourceType: 'module',
70
+ ecmaFeatures: { jsx },
71
+ });
72
+ const read = (() => {
73
+ try {
74
+ return parse(true);
75
+ }
76
+ catch {
77
+ try {
78
+ return parse(false);
79
+ }
80
+ catch (error) {
81
+ broke.set(text, error instanceof Error ? (error.message.split('\n')[0] ?? error.message) : String(error));
82
+ return undefined;
83
+ }
84
+ }
85
+ })();
86
+ if (read === undefined) {
87
+ parsed.set(text, found);
88
+ return found;
89
+ }
90
+ const body = read.ast.body ?? [];
91
+ const hoisted = new Map();
92
+ for (const statement of body) {
93
+ const held = statement.declaration ?? statement;
94
+ if (held.type !== 'VariableDeclaration')
95
+ continue;
96
+ for (const one of held.declarations ?? []) {
97
+ const name = one.id?.name;
98
+ if (name !== undefined && one.init?.type === 'ObjectExpression')
99
+ hoisted.set(name, one.init);
100
+ }
101
+ }
102
+ for (const statement of body) {
103
+ const held = statement.declaration ?? statement;
104
+ if (held.type !== 'VariableDeclaration' || held.kind !== 'const')
105
+ continue;
106
+ for (const one of held.declarations ?? []) {
107
+ if (!types.has(annotated(one) ?? ''))
108
+ continue;
109
+ const init = one.init;
110
+ for (const node of init?.type === 'ArrayExpression' ? (init.elements ?? []) : [init]) {
111
+ if (node === null || node === undefined || node.type !== 'ObjectExpression')
112
+ continue;
113
+ const said = {};
114
+ for (const property of node.properties ?? []) {
115
+ const key = keyOf(property);
116
+ const value = property.value;
117
+ if ((key === 'id' || key === 'describes') && value?.type === 'Literal' && typeof value.value === 'string')
118
+ said[key] = value.value;
119
+ if (key === 'version' && value?.type === 'Literal' && typeof value.value === 'number')
120
+ said.version = value.value;
121
+ if (key !== undefined && LISTS.has(key) && value?.type === 'ArrayExpression')
122
+ rows(value, key, found, hoisted);
123
+ }
124
+ if (said.id !== undefined)
125
+ found.push({ ...said });
126
+ }
127
+ }
128
+ }
129
+ parsed.set(text, found);
130
+ return found;
131
+ }
132
+ export function atsIn(text, only) {
133
+ const found = [];
134
+ for (const one of reaching(text)) {
135
+ if (only !== undefined && (one.list === undefined || !only.has(one.list)))
136
+ continue;
137
+ if (one.at !== undefined)
138
+ found.push(one.at);
139
+ }
140
+ return found;
141
+ }
142
+ export const rowsIn = (text) => reaching(text);
143
+ const SCHEMA = /^export const ([A-Za-z]+)IslandSchema\b/m;
144
+ const DEFINE = /^export function (define[A-Za-z]+)\s*\(/m;
145
+ export function governedBy(one) {
146
+ if (one.includes('*')) {
147
+ const cut = one.lastIndexOf('/', one.indexOf('*'));
148
+ return { kind: 'pattern', matching: one, under: cut === -1 ? '' : one.slice(0, cut + 1) };
149
+ }
150
+ if (one.endsWith('/'))
151
+ return { kind: 'folder', under: one };
152
+ return { kind: 'file', at: one };
153
+ }
154
+ export const filesGoverned = (all) => all.map(governedBy).flatMap((one) => (one.kind === 'file' ? [one.at] : []));
155
+ export const foldersGoverned = (all) => all.map(governedBy).flatMap((one) => (one.kind === 'file' ? [] : [one.under]));
156
+ export function governs(all, path) {
157
+ return all.map(governedBy).some((one) => {
158
+ if (one.kind === 'file')
159
+ return path === one.at;
160
+ if (one.kind === 'folder')
161
+ return path.startsWith(one.under);
162
+ const tail = one.matching.slice(one.under.length).split('*').filter(Boolean);
163
+ const rest = path.startsWith(one.under) ? path.slice(one.under.length) : undefined;
164
+ return rest !== undefined && tail.every((part) => rest.includes(part));
165
+ });
166
+ }
167
+ export function islandsDeclared() {
168
+ const found = [];
169
+ const seen = new Set();
170
+ const all = [];
171
+ for (const at of fromThread()) {
172
+ const held = read(at);
173
+ if (held === undefined)
174
+ continue;
175
+ if (unreadable(at, held) !== undefined)
176
+ continue;
177
+ all.push(...reaching(held));
178
+ }
179
+ for (const row of all) {
180
+ if (row.id === undefined || row.at === undefined || seen.has(row.at))
181
+ continue;
182
+ const source = read(row.at);
183
+ if (source === undefined || !SCHEMA.test(source))
184
+ continue;
185
+ seen.add(row.at);
186
+ const tree = `${TREE}/${row.id}.ts`;
187
+ const named = all.find((one) => one.at === tree)?.id;
188
+ found.push({
189
+ id: row.id,
190
+ at: row.at,
191
+ describes: row.describes ?? '',
192
+ governs: row.governs ?? [],
193
+ define: DEFINE.exec(source)?.[1],
194
+ ...(named === undefined ? {} : { tree, member: named }),
195
+ });
196
+ }
197
+ return found;
198
+ }
199
+ export const declaresIsland = (text) => SCHEMA.exec(text)?.[1];
200
+ export function meaningsIn(text) {
201
+ const found = [];
202
+ for (const one of reaching(text)) {
203
+ if (one.id === undefined)
204
+ continue;
205
+ found.push({ id: one.id, describes: one.describes ?? '' });
206
+ }
207
+ return found;
208
+ }
209
+ function descended() {
210
+ const pass = descend();
211
+ const reached = new Set([THREAD_AT, ...pass.threaded.keys()]);
212
+ const declared = new Map();
213
+ for (const [at, held] of pass.threaded)
214
+ declared.set(at, held.filter((one) => one.list === 'declares').length);
215
+ const meanings = new Map();
216
+ for (const [word, held] of pass.coined)
217
+ meanings.set(word, new Set(held.said));
218
+ return { reached, declared, meanings };
219
+ }
220
+ export function index() {
221
+ tree ??= descended();
222
+ return tree;
223
+ }
224
+ export function forget() {
225
+ parsed.clear();
226
+ broke.clear();
227
+ threaders = undefined;
228
+ tree = undefined;
229
+ forgetDescent();
230
+ }
231
+ export const threaded = () => index().reached;
232
+ export const parses = (path) => FOLLOWABLE.test(path);
233
+ export function unreadable(path, text) {
234
+ if (!parses(path))
235
+ return undefined;
236
+ atsIn(text);
237
+ const said = broke.get(text);
238
+ if (said === undefined)
239
+ return undefined;
240
+ return `this file cannot be parsed, so no law that reads its structure measured anything on it — ${said}`;
241
+ }
242
+ export const names = (path, text) => (parses(path) ? atsIn(text) : []);
243
+ export const slopnator = {
244
+ holds: (_node, at) => {
245
+ const path = here(at.filename);
246
+ if (path === THREAD_AT)
247
+ return false;
248
+ const why = unreadable(path, at.text);
249
+ if (why !== undefined)
250
+ return why;
251
+ const pass = descend();
252
+ if (pass.threaded.has(path))
253
+ return false;
254
+ if (names(path, at.text).length > 0)
255
+ return false;
256
+ const by = pass.mentioned.get(path) ?? [];
257
+ const cut = by.filter((one) => one !== THREAD_AT && !pass.threaded.has(one));
258
+ if (cut.length > 0) {
259
+ return `broken thread — ${cut.join(' and ')} names ${path}, and nothing declares ${cut.length === 1 ? 'it' : 'them'}, so the descent from ${THREAD_AT} never arrives`;
260
+ }
261
+ if (by.length > 0) {
262
+ return `broken thread — ${by.join(' and ')} names ${path} and is itself declared, and the descent still never arrived, so the row was read and never walked`;
263
+ }
264
+ return `nothing that traces back to ${TREE}/ names ${path}, so the descent from ${THREAD_AT} never reaches it`;
265
+ },
266
+ proves: {
267
+ wrong: [
268
+ 'export const ORPHAN = 2',
269
+ 'export const LOOSE = { id: 2 }',
270
+ {
271
+ files: [
272
+ { at: 'threads/_names_it.ts', source: "export const OWNER = {\n declares: [\n {\n at: 'scripts/_probe_bare.ts',\n },\n ],\n}\n" },
273
+ { at: 'scripts/_probe_bare.ts', source: 'export const CHILD = 1\n' },
274
+ ],
275
+ at: 'scripts/_probe_bare.ts',
276
+ },
277
+ {
278
+ files: [
279
+ {
280
+ at: 'threads/_names_it.ts',
281
+ source: "export const REAL: Threaded = {\n declares: [\n {\n at: 'threads/_names_it.ts',\n },\n ],\n}\n\nexport const BARE = {\n declares: [\n {\n at: 'scripts/_probe_after.ts',\n },\n ],\n}\n",
282
+ },
283
+ { at: 'scripts/_probe_after.ts', source: 'export const CHILD = 1\n' },
284
+ ],
285
+ at: 'scripts/_probe_after.ts',
286
+ },
287
+ {
288
+ files: [
289
+ { at: 'threads/_names_it.ts', source: "export let LOOSE = {\n declares: [\n {\n at: 'scripts/_probe_let.ts',\n },\n ],\n}\n" },
290
+ { at: 'scripts/_probe_let.ts', source: 'export const CHILD = 1\n' },
291
+ ],
292
+ at: 'scripts/_probe_let.ts',
293
+ },
294
+ {
295
+ files: [
296
+ { at: 'threads/_names_it.ts', source: "export var LOOSE = {\n declares: [\n {\n at: 'scripts/_probe_var.ts',\n },\n ],\n}\n" },
297
+ { at: 'scripts/_probe_var.ts', source: 'export const CHILD = 1\n' },
298
+ ],
299
+ at: 'scripts/_probe_var.ts',
300
+ },
301
+ {
302
+ files: [
303
+ { at: 'threads/_names_it.ts', source: "export default {\n declares: [\n {\n at: 'scripts/_probe_default.ts',\n },\n ],\n}\n" },
304
+ { at: 'scripts/_probe_default.ts', source: 'export const CHILD = 1\n' },
305
+ ],
306
+ at: 'scripts/_probe_default.ts',
307
+ },
308
+ {
309
+ files: [
310
+ { at: 'threads/_names_it.ts', source: "export function held() {\n return {\n declares: [\n {\n at: 'scripts/_probe_fn.ts',\n },\n ],\n }\n}\n" },
311
+ { at: 'scripts/_probe_fn.ts', source: 'export const CHILD = 1\n' },
312
+ ],
313
+ at: 'scripts/_probe_fn.ts',
314
+ },
315
+ {
316
+ files: [
317
+ { at: 'threads/_names_it.ts', source: "export class Held {\n held = {\n declares: [\n {\n at: 'scripts/_probe_class.ts',\n },\n ],\n }\n}\n" },
318
+ { at: 'scripts/_probe_class.ts', source: 'export const CHILD = 1\n' },
319
+ ],
320
+ at: 'scripts/_probe_class.ts',
321
+ },
322
+ {
323
+ files: [
324
+ {
325
+ at: 'threads/_names_it.ts',
326
+ source: "interface Held { declares: unknown[] }\nexport const FAKE: Held = {\n declares: [\n {\n at: 'scripts/_probe_fake.ts',\n },\n ],\n}\n",
327
+ },
328
+ { at: 'scripts/_probe_fake.ts', source: 'export const CHILD = 1\n' },
329
+ ],
330
+ at: 'scripts/_probe_fake.ts',
331
+ },
332
+ ],
333
+ right: [
334
+ "export const HELD: Threaded = {\n declares: [\n {\n at: 'threads/probe.tsx',\n },\n ],\n}",
335
+ {
336
+ files: [
337
+ { at: 'threads/_names_it.ts', source: "export const OWNER: Threaded = {\n declares: [\n {\n at: 'scripts/_probe_child.ts',\n },\n ],\n}\n" },
338
+ { at: 'scripts/_probe_child.ts', source: 'export const CHILD = 1\n' },
339
+ ],
340
+ at: 'scripts/_probe_child.ts',
341
+ },
342
+ {
343
+ files: [
344
+ { at: 'threads/_names_it.ts', source: "export const OWNER: Threaded = {\n declares: [\n {\n at: 'scripts/_probe_parent.ts',\n },\n ],\n}\n" },
345
+ { at: 'scripts/_probe_parent.ts', source: "export const MID: Threaded = {\n declares: [\n {\n at: 'scripts/_probe_leaf.ts',\n },\n ],\n}\n" },
346
+ { at: 'scripts/_probe_leaf.ts', source: 'export const LEAF = 1\n' },
347
+ ],
348
+ at: 'scripts/_probe_leaf.ts',
349
+ },
350
+ ],
351
+ },
352
+ };
@@ -0,0 +1,2 @@
1
+ import type { Slopnator } from '../slopnator.ts';
2
+ export declare const slopnator: Slopnator;
@@ -0,0 +1,157 @@
1
+ import { existsSync, readFileSync, readdirSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import { named as packageOf } from "../../core/scope.js";
4
+ /*
5
+ * A KEY THE INTERFACE NAMES AND A LANGUAGE DOES NOT ANSWER.
6
+ *
7
+ * "i18n keys must be translated OR ITS AN ERROR ... a new constraint.ts row and slopnator that
8
+ * checks i18n entries against the keys in the UI and if there's anything missing in whatever
9
+ * file, it's a build error, compile error, gate error, everything. Cannot be allowed."
10
+ * — founder, 2026-08-29
11
+ *
12
+ * The generator already writes a line into every language file for every key it finds, so a key is
13
+ * never structurally absent — what it cannot do is invent the sentence. This is the half that
14
+ * refuses: a line holding nothing is a key nobody has answered, and an app that ships one shows a
15
+ * person an empty button.
16
+ *
17
+ * Deliberately independent of the generator rather than calling it. A check that runs the thing it
18
+ * is checking passes whenever that thing passes, which is no check at all — this reads the files
19
+ * that are actually on disk and compares them to the markup that is actually there.
20
+ */
21
+ const SKIP = new Set(['node_modules', '.next', '.git', 'dist', '_generated']);
22
+ const CODE = /\.(tsx?|jsx?)$/;
23
+ /*
24
+ * Which props carry a key, read off the installed package's own declarations.
25
+ *
26
+ * A list written here would go stale the first time an element gained a prop — silently, by finding
27
+ * fewer keys than there are, which is the failure this whole law exists to prevent.
28
+ */
29
+ const TYPED = /readonly\s+([A-Za-z][A-Za-z0-9]*)\??\s*:\s*MessageKey\b/g;
30
+ function walk(at, found = []) {
31
+ let entries;
32
+ try {
33
+ entries = readdirSync(at, { withFileTypes: true });
34
+ }
35
+ catch {
36
+ return found;
37
+ }
38
+ for (const entry of entries) {
39
+ if (SKIP.has(entry.name))
40
+ continue;
41
+ const path = join(at, entry.name);
42
+ if (entry.isDirectory())
43
+ walk(path, found);
44
+ else if (CODE.test(entry.name))
45
+ found.push(path);
46
+ }
47
+ return found;
48
+ }
49
+ function keyProps(root) {
50
+ const found = new Set();
51
+ const home = join(root, 'node_modules', packageOf('ui'));
52
+ if (!existsSync(home))
53
+ return [];
54
+ for (const file of walk(home)) {
55
+ for (const one of readFileSync(file, 'utf8').matchAll(TYPED))
56
+ found.add(one[1] ?? '');
57
+ }
58
+ return [...found].sort();
59
+ }
60
+ function languagesIn(root) {
61
+ const at = join(root, '_threads', 'localizations.ts');
62
+ if (!existsSync(at))
63
+ return { held: [], source: undefined };
64
+ const text = readFileSync(at, 'utf8');
65
+ const held = [];
66
+ let source;
67
+ for (const one of text.matchAll(/id:\s*(?:SOURCE_ROW|'([^']+)')\s*,[\s\S]{0,200}?is:\s*'([^']*)'/g)) {
68
+ const id = one[1];
69
+ if (id === undefined || id === 'source') {
70
+ source = one[2];
71
+ continue;
72
+ }
73
+ held.push(id);
74
+ }
75
+ return { held, source };
76
+ }
77
+ export const slopnator = {
78
+ checks: (root) => {
79
+ const { held } = languagesIn(root);
80
+ if (held.length === 0)
81
+ return [];
82
+ const props = keyProps(root);
83
+ if (props.length === 0)
84
+ return [];
85
+ const named = new RegExp(`\\b(?:${props.join('|')})\\s*=\\s*"([^"]+)"`, 'g');
86
+ const used = new Map();
87
+ for (const file of walk(root)) {
88
+ const text = readFileSync(file, 'utf8');
89
+ for (const one of text.matchAll(named)) {
90
+ const key = one[1] ?? '';
91
+ if (!used.has(key))
92
+ used.set(key, file.slice(root.length).replace(/^\/+/, ''));
93
+ }
94
+ }
95
+ if (used.size === 0)
96
+ return [];
97
+ const findings = [];
98
+ for (const language of held) {
99
+ const at = join(root, 'i18n', `${language}.json`);
100
+ if (!existsSync(at)) {
101
+ findings.push(`i18n/${language}.json is not there and \`${language}\` is a declared language, so every key this ` +
102
+ 'interface names goes unanswered in it — run `thread i18n`');
103
+ continue;
104
+ }
105
+ let wording;
106
+ try {
107
+ wording = JSON.parse(readFileSync(at, 'utf8'));
108
+ }
109
+ catch {
110
+ findings.push(`i18n/${language}.json will not parse, so what this app says in \`${language}\` cannot be known — ` +
111
+ 'a reader that cannot read is not a reader that found nothing');
112
+ continue;
113
+ }
114
+ for (const [key, where] of [...used].sort()) {
115
+ const said = wording[key];
116
+ if (typeof said === 'string' && said !== '')
117
+ continue;
118
+ findings.push(`\`${key}\` is named by ${where} and says nothing in \`${language}\` — an untranslated key is a ` +
119
+ 'blank on the screen, and it is an error rather than a word that comes out in the wrong language');
120
+ }
121
+ }
122
+ return findings;
123
+ },
124
+ proves: {
125
+ wrong: [
126
+ {
127
+ files: [
128
+ { at: '_threads/localizations.ts', source: "export const LOCALIZATION = {\n holds: [\n { id: 'source', is: 'en' },\n { id: 'en', is: 'English' },\n ],\n}\n" },
129
+ { at: `node_modules/${packageOf('ui')}/probe.ts`, source: 'export interface P {\n readonly label: MessageKey\n}\n' },
130
+ { at: 'compositions/cmp_probe.tsx', source: 'export const P = () => <Button label="probe.blank" />\n' },
131
+ { at: 'i18n/en.json', source: '{\n "probe.blank": ""\n}\n' },
132
+ ],
133
+ at: 'compositions/cmp_probe.tsx',
134
+ },
135
+ {
136
+ files: [
137
+ { at: '_threads/localizations.ts', source: "export const LOCALIZATION = {\n holds: [\n { id: 'source', is: 'en' },\n { id: 'en', is: 'English' },\n ],\n}\n" },
138
+ { at: `node_modules/${packageOf('ui')}/probe.ts`, source: 'export interface P {\n readonly label: MessageKey\n}\n' },
139
+ { at: 'compositions/cmp_probe.tsx', source: 'export const P = () => <Button label="probe.missing" />\n' },
140
+ { at: 'i18n/en.json', source: '{\n "probe.other": "Something"\n}\n' },
141
+ ],
142
+ at: 'compositions/cmp_probe.tsx',
143
+ },
144
+ ],
145
+ right: [
146
+ {
147
+ files: [
148
+ { at: '_threads/localizations.ts', source: "export const LOCALIZATION = {\n holds: [\n { id: 'source', is: 'en' },\n { id: 'en', is: 'English' },\n ],\n}\n" },
149
+ { at: `node_modules/${packageOf('ui')}/probe.ts`, source: 'export interface P {\n readonly label: MessageKey\n}\n' },
150
+ { at: 'compositions/cmp_probe.tsx', source: 'export const P = () => <Button label="probe.answered" />\n' },
151
+ { at: 'i18n/en.json', source: '{\n "probe.answered": "Answered"\n}\n' },
152
+ ],
153
+ at: 'compositions/cmp_probe.tsx',
154
+ },
155
+ ],
156
+ },
157
+ };
@@ -0,0 +1,2 @@
1
+ import type { Slopnator } from '../slopnator.ts';
2
+ export declare const slopnator: Slopnator;
@@ -0,0 +1,31 @@
1
+ import { here, parses, rowsIn } from "./sl_unthreaded.js";
2
+ export const slopnator = {
3
+ holds: (_node, at) => {
4
+ const path = here(at.filename);
5
+ if (!parses(path))
6
+ return false;
7
+ let governing;
8
+ try {
9
+ governing = rowsIn(at.text);
10
+ }
11
+ catch {
12
+ return 'this file cannot be parsed, so the declarations it governs with were never read and nothing about their versions was measured';
13
+ }
14
+ for (const node of governing) {
15
+ if (node.list !== undefined || node.version !== undefined)
16
+ continue;
17
+ return (`"${node.id ?? ''}" is annotated with the interface that makes other things conform to it and states no \`version\`, ` +
18
+ 'so a change to what it governs is a diff nobody downstream can detect and nothing can be carried forward from the shape it used to have');
19
+ }
20
+ return false;
21
+ },
22
+ proves: {
23
+ wrong: [
24
+ "export const L: Constraint = {\n id: 'a-law',\n at: 'packages/core/integrity/src/constraint.ts',\n describes: 'A rule that must be enforced.',\n}",
25
+ ],
26
+ right: [
27
+ "export const L: Constraint = {\n id: 'a-law',\n at: 'packages/core/integrity/src/constraint.ts',\n describes: 'A rule that must be enforced.',\n version: 3,\n}",
28
+ "export const HELPER = {\n id: 'a-helper',\n at: 'threads/meta.ts',\n describes: 'Nothing conforms to this.',\n}",
29
+ ],
30
+ },
31
+ };
@@ -0,0 +1,24 @@
1
+ import { type Constraint } from './constraint.ts';
2
+ import { type Slopnator, type Spoken } from './slopnator.ts';
3
+ interface Node {
4
+ readonly type?: string;
5
+ readonly comments?: readonly Spoken[];
6
+ readonly [key: string]: unknown;
7
+ }
8
+ export declare function visit(root: Node, on: ReadonlySet<string>, each: (node: Node, ancestors: readonly Node[]) => void): void;
9
+ export interface Caught {
10
+ readonly rule: string;
11
+ readonly file: string;
12
+ readonly found: string;
13
+ }
14
+ export declare function tracked(root: string): readonly string[];
15
+ export declare function sweep(root: string, all: readonly Constraint[], slopnators: ReadonlyMap<string, Slopnator>, files: readonly string[]): {
16
+ readonly findings: readonly Caught[];
17
+ readonly swept: number;
18
+ readonly tracked: number;
19
+ readonly rules: number;
20
+ readonly unrun: readonly string[];
21
+ readonly unmet: readonly string[];
22
+ readonly linted: readonly string[];
23
+ };
24
+ export {};