@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.
- package/CHANGELOG.md +192 -0
- package/dist/attach.d.ts +1 -0
- package/dist/attach.js +1 -0
- package/dist/core/actor.d.ts +12 -0
- package/dist/core/actor.js +8 -0
- package/dist/core/asking.d.ts +180 -0
- package/dist/core/asking.js +1109 -0
- package/dist/core/attach.d.ts +54 -0
- package/dist/core/attach.js +510 -0
- package/dist/core/chain.d.ts +16 -0
- package/dist/core/chain.js +114 -0
- package/dist/core/cli.d.ts +2 -0
- package/dist/core/cli.js +1682 -0
- package/dist/core/condition.d.ts +52 -0
- package/dist/core/condition.js +198 -0
- package/dist/core/descend.d.ts +81 -0
- package/dist/core/descend.js +879 -0
- package/dist/core/finding.d.ts +12 -0
- package/dist/core/finding.js +23 -0
- package/dist/core/generators/meta.d.ts +2 -0
- package/dist/core/generators/meta.js +823 -0
- package/dist/core/init.d.ts +15 -0
- package/dist/core/init.js +134 -0
- package/dist/core/invoked.d.ts +9 -0
- package/dist/core/invoked.js +41 -0
- package/dist/core/round.d.ts +1 -0
- package/dist/core/round.js +57 -0
- package/dist/core/rows.d.ts +26 -0
- package/dist/core/rows.js +354 -0
- package/dist/core/scope.d.ts +6 -0
- package/dist/core/scope.js +12 -0
- package/dist/core/thread.d.ts +26 -0
- package/dist/core/thread.js +5 -0
- package/dist/core/tree.d.ts +12 -0
- package/dist/core/tree.js +56 -0
- package/dist/core/types.d.ts +49 -0
- package/dist/core/types.js +311 -0
- package/dist/derivation/attach.d.ts +2 -0
- package/dist/derivation/attach.js +61 -0
- package/dist/derivation/generators/build.d.ts +16 -0
- package/dist/derivation/generators/build.js +217 -0
- package/dist/derivation/generators/check.d.ts +18 -0
- package/dist/derivation/generators/check.js +78 -0
- package/dist/derivation/generators/derivation.d.ts +3 -0
- package/dist/derivation/generators/derivation.js +273 -0
- package/dist/derivation/generators/determinism.d.ts +20 -0
- package/dist/derivation/generators/determinism.js +110 -0
- package/dist/derivation/generators/release.d.ts +3 -0
- package/dist/derivation/generators/release.js +183 -0
- package/dist/derivation/makers/release.d.ts +1 -0
- package/dist/derivation/makers/release.js +107 -0
- package/dist/derivation/release.d.ts +10 -0
- package/dist/derivation/release.js +12 -0
- package/dist/integrations/attach.d.ts +6 -0
- package/dist/integrations/attach.js +177 -0
- package/dist/integrations/generators/read.d.ts +1 -0
- package/dist/integrations/generators/read.js +92 -0
- package/dist/integrations/generators/sdk.d.ts +1 -0
- package/dist/integrations/generators/sdk.js +148 -0
- package/dist/integrations/generators/secret.d.ts +1 -0
- package/dist/integrations/generators/secret.js +233 -0
- package/dist/integrations/generators/target.d.ts +1 -0
- package/dist/integrations/generators/target.js +765 -0
- package/dist/integrations/generators/vault.d.ts +1 -0
- package/dist/integrations/generators/vault.js +164 -0
- package/dist/integrations/sdk.d.ts +6 -0
- package/dist/integrations/sdk.js +151 -0
- package/dist/integrations/secret.d.ts +16 -0
- package/dist/integrations/secret.js +30 -0
- package/dist/integrations/target.d.ts +49 -0
- package/dist/integrations/target.js +225 -0
- package/dist/integrations/vault.d.ts +12 -0
- package/dist/integrations/vault.js +104 -0
- package/dist/integrity/attach.d.ts +2 -0
- package/dist/integrity/attach.js +213 -0
- package/dist/integrity/check-threads.d.ts +10 -0
- package/dist/integrity/check-threads.js +153 -0
- package/dist/integrity/constraint.d.ts +16 -0
- package/dist/integrity/constraint.js +1105 -0
- package/dist/integrity/generators/compiler.d.ts +1 -0
- package/dist/integrity/generators/compiler.js +130 -0
- package/dist/integrity/generators/gate.d.ts +1 -0
- package/dist/integrity/generators/gate.js +172 -0
- package/dist/integrity/generators/issues.d.ts +1 -0
- package/dist/integrity/generators/issues.js +100 -0
- package/dist/integrity/generators/publish.d.ts +1 -0
- package/dist/integrity/generators/publish.js +186 -0
- package/dist/integrity/generators/rule.d.ts +2 -0
- package/dist/integrity/generators/rule.js +213 -0
- package/dist/integrity/generators/seal.d.ts +4 -0
- package/dist/integrity/generators/seal.js +101 -0
- package/dist/integrity/generators/trial.d.ts +39 -0
- package/dist/integrity/generators/trial.js +254 -0
- package/dist/integrity/lint.d.ts +50 -0
- package/dist/integrity/lint.js +161 -0
- package/dist/integrity/lsp/bin.d.ts +2 -0
- package/dist/integrity/lsp/bin.js +35 -0
- package/dist/integrity/lsp/diagnostics.d.ts +25 -0
- package/dist/integrity/lsp/diagnostics.js +57 -0
- package/dist/integrity/lsp/findings.d.ts +20 -0
- package/dist/integrity/lsp/findings.js +39 -0
- package/dist/integrity/lsp/lint.d.ts +7 -0
- package/dist/integrity/lsp/lint.js +95 -0
- package/dist/integrity/lsp/protocol.d.ts +20 -0
- package/dist/integrity/lsp/protocol.js +57 -0
- package/dist/integrity/lsp/server.d.ts +15 -0
- package/dist/integrity/lsp/server.js +171 -0
- package/dist/integrity/sealed.d.ts +1 -0
- package/dist/integrity/sealed.js +56 -0
- package/dist/integrity/slopnator.d.ts +90 -0
- package/dist/integrity/slopnator.js +267 -0
- package/dist/integrity/slopnators/sl_broken_lineage.d.ts +2 -0
- package/dist/integrity/slopnators/sl_broken_lineage.js +35 -0
- package/dist/integrity/slopnators/sl_declared_elsewhere.d.ts +2 -0
- package/dist/integrity/slopnators/sl_declared_elsewhere.js +33 -0
- package/dist/integrity/slopnators/sl_derived_drift.d.ts +8 -0
- package/dist/integrity/slopnators/sl_derived_drift.js +91 -0
- package/dist/integrity/slopnators/sl_hardcoded_style.d.ts +2 -0
- package/dist/integrity/slopnators/sl_hardcoded_style.js +46 -0
- package/dist/integrity/slopnators/sl_inert_island.d.ts +2 -0
- package/dist/integrity/slopnators/sl_inert_island.js +85 -0
- package/dist/integrity/slopnators/sl_inert_slopnator.d.ts +2 -0
- package/dist/integrity/slopnators/sl_inert_slopnator.js +76 -0
- package/dist/integrity/slopnators/sl_missing_row.d.ts +2 -0
- package/dist/integrity/slopnators/sl_missing_row.js +180 -0
- package/dist/integrity/slopnators/sl_no_barrel.d.ts +2 -0
- package/dist/integrity/slopnators/sl_no_barrel.js +41 -0
- package/dist/integrity/slopnators/sl_no_child_process.d.ts +2 -0
- package/dist/integrity/slopnators/sl_no_child_process.js +61 -0
- package/dist/integrity/slopnators/sl_no_comment.d.ts +2 -0
- package/dist/integrity/slopnators/sl_no_comment.js +41 -0
- package/dist/integrity/slopnators/sl_no_dispatch_bypass.d.ts +2 -0
- package/dist/integrity/slopnators/sl_no_dispatch_bypass.js +301 -0
- package/dist/integrity/slopnators/sl_no_import_cycle.d.ts +2 -0
- package/dist/integrity/slopnators/sl_no_import_cycle.js +42 -0
- package/dist/integrity/slopnators/sl_no_such_command.d.ts +2 -0
- package/dist/integrity/slopnators/sl_no_such_command.js +55 -0
- package/dist/integrity/slopnators/sl_only_takes_a_literal.d.ts +2 -0
- package/dist/integrity/slopnators/sl_only_takes_a_literal.js +59 -0
- package/dist/integrity/slopnators/sl_secret_literal.d.ts +2 -0
- package/dist/integrity/slopnators/sl_secret_literal.js +89 -0
- package/dist/integrity/slopnators/sl_stale_path.d.ts +2 -0
- package/dist/integrity/slopnators/sl_stale_path.js +92 -0
- package/dist/integrity/slopnators/sl_twice_meant.d.ts +2 -0
- package/dist/integrity/slopnators/sl_twice_meant.js +30 -0
- package/dist/integrity/slopnators/sl_twice_parented.d.ts +2 -0
- package/dist/integrity/slopnators/sl_twice_parented.js +34 -0
- package/dist/integrity/slopnators/sl_undeclared_crossing.d.ts +2 -0
- package/dist/integrity/slopnators/sl_undeclared_crossing.js +78 -0
- package/dist/integrity/slopnators/sl_undeclared_destination.d.ts +2 -0
- package/dist/integrity/slopnators/sl_undeclared_destination.js +56 -0
- package/dist/integrity/slopnators/sl_undeclared_element.d.ts +2 -0
- package/dist/integrity/slopnators/sl_undeclared_element.js +34 -0
- package/dist/integrity/slopnators/sl_undeclared_node.d.ts +2 -0
- package/dist/integrity/slopnators/sl_undeclared_node.js +59 -0
- package/dist/integrity/slopnators/sl_undeclared_operation.d.ts +2 -0
- package/dist/integrity/slopnators/sl_undeclared_operation.js +120 -0
- package/dist/integrity/slopnators/sl_undeclared_variant.d.ts +2 -0
- package/dist/integrity/slopnators/sl_undeclared_variant.js +117 -0
- package/dist/integrity/slopnators/sl_undescribed.d.ts +2 -0
- package/dist/integrity/slopnators/sl_undescribed.js +54 -0
- package/dist/integrity/slopnators/sl_ungoverned_island.d.ts +2 -0
- package/dist/integrity/slopnators/sl_ungoverned_island.js +58 -0
- package/dist/integrity/slopnators/sl_unkept_rows.d.ts +2 -0
- package/dist/integrity/slopnators/sl_unkept_rows.js +104 -0
- package/dist/integrity/slopnators/sl_unnamed_island.d.ts +2 -0
- package/dist/integrity/slopnators/sl_unnamed_island.js +37 -0
- package/dist/integrity/slopnators/sl_unpaired_member.d.ts +2 -0
- package/dist/integrity/slopnators/sl_unpaired_member.js +114 -0
- package/dist/integrity/slopnators/sl_unpaired_slopnator.d.ts +2 -0
- package/dist/integrity/slopnators/sl_unpaired_slopnator.js +60 -0
- package/dist/integrity/slopnators/sl_unprefixed_member.d.ts +2 -0
- package/dist/integrity/slopnators/sl_unprefixed_member.js +126 -0
- package/dist/integrity/slopnators/sl_unseamed_reach.d.ts +2 -0
- package/dist/integrity/slopnators/sl_unseamed_reach.js +73 -0
- package/dist/integrity/slopnators/sl_unthreaded.d.ts +56 -0
- package/dist/integrity/slopnators/sl_unthreaded.js +352 -0
- package/dist/integrity/slopnators/sl_untranslated_key.d.ts +2 -0
- package/dist/integrity/slopnators/sl_untranslated_key.js +157 -0
- package/dist/integrity/slopnators/sl_unversioned.d.ts +2 -0
- package/dist/integrity/slopnators/sl_unversioned.js +31 -0
- package/dist/integrity/sweep.d.ts +24 -0
- package/dist/integrity/sweep.js +264 -0
- package/dist/knowledge/attach.d.ts +2 -0
- package/dist/knowledge/attach.js +339 -0
- package/dist/knowledge/generators/docsgen.d.ts +1 -0
- package/dist/knowledge/generators/docsgen.js +145 -0
- package/dist/knowledge/generators/glossary.d.ts +1 -0
- package/dist/knowledge/generators/glossary.js +56 -0
- package/dist/knowledge/generators/skills.d.ts +1 -0
- package/dist/knowledge/generators/skills.js +222 -0
- package/dist/knowledge/glossary.d.ts +6 -0
- package/dist/knowledge/glossary.js +1 -0
- package/dist/operations/access.d.ts +27 -0
- package/dist/operations/access.js +61 -0
- package/dist/operations/attach.d.ts +6 -0
- package/dist/operations/attach.js +414 -0
- package/dist/operations/capabilities.d.ts +37 -0
- package/dist/operations/capabilities.js +35 -0
- package/dist/operations/cost.d.ts +14 -0
- package/dist/operations/cost.js +37 -0
- package/dist/operations/dispatch.d.ts +14 -0
- package/dist/operations/dispatch.js +327 -0
- package/dist/operations/error-results.d.ts +4 -0
- package/dist/operations/error-results.js +25 -0
- package/dist/operations/errors/catalog.d.ts +58 -0
- package/dist/operations/errors/catalog.js +91 -0
- package/dist/operations/errors/classes.d.ts +73 -0
- package/dist/operations/errors/classes.js +22 -0
- package/dist/operations/errors/errors.d.ts +52 -0
- package/dist/operations/errors/errors.js +257 -0
- package/dist/operations/generators/capability.d.ts +5 -0
- package/dist/operations/generators/capability.js +603 -0
- package/dist/operations/generators/compile.d.ts +2 -0
- package/dist/operations/generators/compile.js +541 -0
- package/dist/operations/generators/grant.d.ts +1 -0
- package/dist/operations/generators/grant.js +267 -0
- package/dist/operations/generators/operation.d.ts +1 -0
- package/dist/operations/generators/operation.js +811 -0
- package/dist/operations/generators/permits.d.ts +1 -0
- package/dist/operations/generators/permits.js +196 -0
- package/dist/operations/generators/seed.d.ts +20 -0
- package/dist/operations/generators/seed.js +285 -0
- package/dist/operations/generators/trial.d.ts +53 -0
- package/dist/operations/generators/trial.js +376 -0
- package/dist/operations/http.d.ts +30 -0
- package/dist/operations/http.js +121 -0
- package/dist/operations/makers/capability.d.ts +1 -0
- package/dist/operations/makers/capability.js +117 -0
- package/dist/operations/makers/trial.d.ts +1 -0
- package/dist/operations/makers/trial.js +413 -0
- package/dist/operations/memory.d.ts +104 -0
- package/dist/operations/memory.js +365 -0
- package/dist/operations/metering.d.ts +55 -0
- package/dist/operations/metering.js +247 -0
- package/dist/operations/nodes.d.ts +35 -0
- package/dist/operations/nodes.js +15 -0
- package/dist/operations/operations.d.ts +18 -0
- package/dist/operations/operations.js +5 -0
- package/dist/operations/queue-policy.d.ts +3 -0
- package/dist/operations/queue-policy.js +8 -0
- package/dist/operations/result.d.ts +19 -0
- package/dist/operations/result.js +10 -0
- package/dist/operations/seams.d.ts +239 -0
- package/dist/operations/seams.js +45 -0
- package/dist/operations/store.d.ts +14 -0
- package/dist/operations/store.js +1 -0
- package/package.json +16 -0
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import { relative as relativeTo, resolve } from 'node:path';
|
|
2
|
+
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
3
|
+
import { named } from "../../core/scope.js";
|
|
4
|
+
import { report, SOURCE_NAME } from "./diagnostics.js";
|
|
5
|
+
import { METHOD_NOT_FOUND } from "./protocol.js";
|
|
6
|
+
const SYNC_FULL = 1;
|
|
7
|
+
export class LspServer {
|
|
8
|
+
#root;
|
|
9
|
+
#options;
|
|
10
|
+
#open = new Map();
|
|
11
|
+
#published = new Set();
|
|
12
|
+
#running = false;
|
|
13
|
+
#again = false;
|
|
14
|
+
#shutdownRequested = false;
|
|
15
|
+
#exited = false;
|
|
16
|
+
constructor(options) {
|
|
17
|
+
this.#options = options;
|
|
18
|
+
this.#root = options.root;
|
|
19
|
+
}
|
|
20
|
+
get exited() {
|
|
21
|
+
return this.#exited;
|
|
22
|
+
}
|
|
23
|
+
get exitCode() {
|
|
24
|
+
return this.#shutdownRequested ? 0 : 1;
|
|
25
|
+
}
|
|
26
|
+
async handle(message) {
|
|
27
|
+
if (message.method === undefined)
|
|
28
|
+
return;
|
|
29
|
+
switch (message.method) {
|
|
30
|
+
case 'initialize':
|
|
31
|
+
this.#root = rootIn(message.params) ?? this.#root;
|
|
32
|
+
this.#reply(message.id, {
|
|
33
|
+
capabilities: {
|
|
34
|
+
textDocumentSync: { openClose: true, change: SYNC_FULL, save: true },
|
|
35
|
+
diagnosticProvider: {
|
|
36
|
+
identifier: SOURCE_NAME,
|
|
37
|
+
interFileDependencies: true,
|
|
38
|
+
workspaceDiagnostics: false,
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
serverInfo: { name: named('integrity') },
|
|
42
|
+
});
|
|
43
|
+
return;
|
|
44
|
+
case 'initialized':
|
|
45
|
+
return;
|
|
46
|
+
case 'shutdown':
|
|
47
|
+
this.#shutdownRequested = true;
|
|
48
|
+
this.#reply(message.id, null);
|
|
49
|
+
return;
|
|
50
|
+
case 'exit':
|
|
51
|
+
this.#exited = true;
|
|
52
|
+
return;
|
|
53
|
+
case 'textDocument/didOpen': {
|
|
54
|
+
const params = message.params;
|
|
55
|
+
const path = this.#pathOf(params?.textDocument?.uri);
|
|
56
|
+
if (path === undefined)
|
|
57
|
+
return;
|
|
58
|
+
this.#open.set(path, typeof params?.textDocument?.text === 'string' ? params.textDocument.text : '');
|
|
59
|
+
await this.#recompute();
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
case 'textDocument/didChange': {
|
|
63
|
+
const params = message.params;
|
|
64
|
+
const path = this.#pathOf(params?.textDocument?.uri);
|
|
65
|
+
if (path === undefined)
|
|
66
|
+
return;
|
|
67
|
+
const latest = params?.contentChanges?.at(-1)?.text;
|
|
68
|
+
if (typeof latest === 'string')
|
|
69
|
+
this.#open.set(path, latest);
|
|
70
|
+
await this.#recompute();
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
case 'textDocument/didSave':
|
|
74
|
+
await this.#recompute();
|
|
75
|
+
return;
|
|
76
|
+
case 'textDocument/didClose': {
|
|
77
|
+
const path = this.#pathOf(message.params?.textDocument?.uri);
|
|
78
|
+
if (path === undefined)
|
|
79
|
+
return;
|
|
80
|
+
this.#open.delete(path);
|
|
81
|
+
await this.#recompute();
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
case 'textDocument/diagnostic': {
|
|
85
|
+
const path = this.#pathOf(message.params?.textDocument?.uri);
|
|
86
|
+
const computed = await report(this.#sourceForRoot(), this.#open, this.#published);
|
|
87
|
+
this.#published = new Set([...computed].filter(([, list]) => list.length > 0).map(([file]) => file));
|
|
88
|
+
this.#reply(message.id, {
|
|
89
|
+
kind: 'full',
|
|
90
|
+
items: path === undefined ? [] : (computed.get(path) ?? []),
|
|
91
|
+
});
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
default:
|
|
95
|
+
if (message.id !== undefined && message.id !== null) {
|
|
96
|
+
this.#options.send({
|
|
97
|
+
jsonrpc: '2.0',
|
|
98
|
+
id: message.id,
|
|
99
|
+
error: { code: METHOD_NOT_FOUND, message: `${message.method} is not implemented by ${SOURCE_NAME}` },
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
#sourceForRoot() {
|
|
105
|
+
return this.#options.sourceFor(this.#root);
|
|
106
|
+
}
|
|
107
|
+
#reply(id, result) {
|
|
108
|
+
if (id === undefined || id === null)
|
|
109
|
+
return;
|
|
110
|
+
this.#options.send({ jsonrpc: '2.0', id, result });
|
|
111
|
+
}
|
|
112
|
+
async #recompute() {
|
|
113
|
+
if (this.#running) {
|
|
114
|
+
this.#again = true;
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
this.#running = true;
|
|
118
|
+
try {
|
|
119
|
+
do {
|
|
120
|
+
this.#again = false;
|
|
121
|
+
await (this.#options.settle?.() ?? sleep(200));
|
|
122
|
+
const computed = await report(this.#sourceForRoot(), this.#open, this.#published);
|
|
123
|
+
const nonEmpty = new Set();
|
|
124
|
+
for (const [path, diagnostics] of computed) {
|
|
125
|
+
this.#publish(path, diagnostics);
|
|
126
|
+
if (diagnostics.length > 0)
|
|
127
|
+
nonEmpty.add(path);
|
|
128
|
+
}
|
|
129
|
+
this.#published = nonEmpty;
|
|
130
|
+
} while (this.#again);
|
|
131
|
+
}
|
|
132
|
+
finally {
|
|
133
|
+
this.#running = false;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
#publish(path, diagnostics) {
|
|
137
|
+
this.#options.send({
|
|
138
|
+
jsonrpc: '2.0',
|
|
139
|
+
method: 'textDocument/publishDiagnostics',
|
|
140
|
+
params: { uri: pathToFileURL(resolve(this.#root, path)).href, diagnostics },
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
#pathOf(uri) {
|
|
144
|
+
if (typeof uri !== 'string' || !uri.startsWith('file://'))
|
|
145
|
+
return undefined;
|
|
146
|
+
let absolute;
|
|
147
|
+
try {
|
|
148
|
+
absolute = fileURLToPath(uri);
|
|
149
|
+
}
|
|
150
|
+
catch {
|
|
151
|
+
return undefined;
|
|
152
|
+
}
|
|
153
|
+
const path = relativeTo(this.#root, absolute).split('\\').join('/');
|
|
154
|
+
return path.startsWith('..') || path.startsWith('/') ? undefined : path;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
const sleep = (ms) => new Promise((done) => setTimeout(done, ms));
|
|
158
|
+
function rootIn(params) {
|
|
159
|
+
const folder = params?.workspaceFolders?.[0]?.uri;
|
|
160
|
+
for (const uri of [folder, params?.rootUri]) {
|
|
161
|
+
if (typeof uri !== 'string' || !uri.startsWith('file://'))
|
|
162
|
+
continue;
|
|
163
|
+
try {
|
|
164
|
+
return fileURLToPath(uri);
|
|
165
|
+
}
|
|
166
|
+
catch {
|
|
167
|
+
continue;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
return typeof params?.rootPath === 'string' ? params.rootPath : undefined;
|
|
171
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const SEALED: Readonly<Record<string, string>>;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
export const SEALED = {
|
|
2
|
+
'attach.js': 'e879e49449a3b90118335ab4d2ea6666956da92203438fac7b88ff3a65a79103',
|
|
3
|
+
'check-threads.js': '1754d3acf4e5ea45ce11bf62b1404d59fa027eb22450e253e303acd13f75f6d8',
|
|
4
|
+
'constraint.js': 'c6eebad19fc11c42c095d82f5658c204f322d136bcbaab98aa9fc06dfa625afb',
|
|
5
|
+
'generators/compiler.js': 'c6db73bfd31513d8334ef5cd063fb5f12b89c56739ff0f2b73249252d1817cdc',
|
|
6
|
+
'generators/gate.js': 'f865f469f13b9e3b2eb120bd233b3e286c0df30a4cdf15cd20be4cb995854f88',
|
|
7
|
+
'generators/issues.js': '21cd7349d3375f15cb8b1a0c31714ab645b021b952275046b56357fba68478b8',
|
|
8
|
+
'generators/publish.js': 'dcbeaf744dd786e8a606fa9d79fb4d3e3cfa6ae1a1432261547f1e4ef186b81e',
|
|
9
|
+
'generators/rule.js': '82bdfefbf394528fa4e7994a60eb423b3188277571937e161648f72666728aa4',
|
|
10
|
+
'generators/seal.js': '39fb8ab5c95b9c0fd1d3d38090785b2e1890653bb1d0454868426b67b28abe3a',
|
|
11
|
+
'generators/trial.js': 'a2ef8fda83bb2894ae088a0d7887a4e49b9693fa1a7e1345879aaa63041914b0',
|
|
12
|
+
'lint.js': '63f9957725a94498d7e5a49544da343c5f2e1b3b9c2778000dae4a4f39036ef9',
|
|
13
|
+
'lsp/bin.js': 'b71d5da40c137dbe87083ad29bf1a54e30a190113b8811602263f4c5a4d5a11c',
|
|
14
|
+
'lsp/diagnostics.js': '0fd15477b4df60e5ec065bb1a85c5daab6b9b433e730cb836b8ddc747554e0b9',
|
|
15
|
+
'lsp/findings.js': '72f7d80645c0941926a1cc029417fed93eca87e7d53bf2d032f4ef647fb1f168',
|
|
16
|
+
'lsp/lint.js': '159b07ffa100a62b2cd445d621606ae0d6b68aacc342cb5fdb3a2f4cadd7f508',
|
|
17
|
+
'lsp/protocol.js': 'd0ad8650307bf563bd058dc2bbbbc0ec9d4fd6dc2c9120bdc261d57c076ddc5d',
|
|
18
|
+
'lsp/server.js': 'a9f76280e395a47521973a14f3769c4df9e8c1153d29dedc535f56276e5a2e2e',
|
|
19
|
+
'slopnator.js': '804adba20c269c1134fd09e075d354db73b79cfd00c4b4ce66c1e40279670905',
|
|
20
|
+
'slopnators/sl_broken_lineage.js': '6d0ee15127166d9792b365e8a4623b5770a57e2ff92c2accf196afbb85052112',
|
|
21
|
+
'slopnators/sl_declared_elsewhere.js': '394da91ac84369217b4a02bc078ab98f62bf6d522042d84bec82b9705d621751',
|
|
22
|
+
'slopnators/sl_derived_drift.js': 'f7fb0e26cd6bff9dfe7392e1b1593a51741a6f6fac408bdafbb783e04141cf2f',
|
|
23
|
+
'slopnators/sl_hardcoded_style.js': 'a2b006990bc33150733645f3c17942ff497e7b3a6565af1331263df36d3b5e0a',
|
|
24
|
+
'slopnators/sl_inert_island.js': '0900de73ab7439b02a3321a6459e53882cfba6267dc8ef45d630ebe40f4bfa84',
|
|
25
|
+
'slopnators/sl_inert_slopnator.js': 'e3c35cfa2b094e1e2fd6824b75138cf44e8edb5ec396375d67e47897d84c45d7',
|
|
26
|
+
'slopnators/sl_missing_row.js': 'd942940388a873bebeff72d93cdb5fef20ba88e4fe29c1aa68e2f42389714a4a',
|
|
27
|
+
'slopnators/sl_no_barrel.js': '7df2a763d4b4ab16d945341b844271380b25774491bfa9c204dc252910951bc6',
|
|
28
|
+
'slopnators/sl_no_child_process.js': '24d4f89a1493c95db12e92a34dc4281a422226f638a47c795f26447090e056a1',
|
|
29
|
+
'slopnators/sl_no_comment.js': '4c4b9b502d6b33ed00d653d0b190d8f95b02bc5ef10da87f97913ca425f59f40',
|
|
30
|
+
'slopnators/sl_no_dispatch_bypass.js': '1def29fca350861b7185129180edd4727199e0f2bba92a0e0a8c0af437cbb382',
|
|
31
|
+
'slopnators/sl_no_import_cycle.js': '7ab70e4c182e88f73b4af75cddef9655a49996b8c70fcc2dcebd5f4aeb5fef92',
|
|
32
|
+
'slopnators/sl_no_such_command.js': 'fe037c2ad6891fd3c0338dd84c7007110c0bcd272a12bd8f1cb85ec779df7a8b',
|
|
33
|
+
'slopnators/sl_only_takes_a_literal.js': '3d5074f9c6a9bb581c80af8aa6d4a6793e29ac93c793367a67326acc1612c8fe',
|
|
34
|
+
'slopnators/sl_secret_literal.js': 'bc3987627f3b2892eec169d775771b6309d47b2c93e98c1bccad3f3ed5c5d2b6',
|
|
35
|
+
'slopnators/sl_stale_path.js': '3cc4050cb9ca2875afbb69c818d20d06e82f36a762ec48bd033c342a7023ff8a',
|
|
36
|
+
'slopnators/sl_twice_meant.js': '6fc5aaf29b635335bd4963931475e7334110439f8305737786c01be39f81230c',
|
|
37
|
+
'slopnators/sl_twice_parented.js': 'caa590bc4c11dbebe971f5a611a1712a529cc5a2900f37ba43cfa6b67a0a722b',
|
|
38
|
+
'slopnators/sl_undeclared_crossing.js': '47e41a38334a4b5a3eb0616d675dc46c5e4d6ce1138710b4470acc276e6ddec5',
|
|
39
|
+
'slopnators/sl_undeclared_destination.js': '3bbf07029a31eb31fa6556b686d067aae0cd5bf9487fe6ca1896984a55970a5c',
|
|
40
|
+
'slopnators/sl_undeclared_element.js': '2aa469ffdd58f104936904cbee2c95cb38182a2ac1057e0caa4db3d85e81331c',
|
|
41
|
+
'slopnators/sl_undeclared_node.js': '84c069a7d1bdec3d3c950ad40e016c129f280796b63a196a7866232e14995443',
|
|
42
|
+
'slopnators/sl_undeclared_operation.js': 'bc3fa4efc83bf5641020373d0f1506856c6272c3d59e4c4d261e3076f69a155e',
|
|
43
|
+
'slopnators/sl_undeclared_variant.js': 'e41ec11c9f4baf52dbea1aa7828238e5738de609ea47cdcac793cbe6198df770',
|
|
44
|
+
'slopnators/sl_undescribed.js': '15c6139a4afc3a4dc1a6196ec1acc2af5066a3330343074f6e2b321291cdbbd0',
|
|
45
|
+
'slopnators/sl_ungoverned_island.js': '555ae51adf994c5fce4ce51701af7d022be8824c001eeb132403fd6342a12a49',
|
|
46
|
+
'slopnators/sl_unkept_rows.js': '8abd67e8b8347a6dd9abd20fb7719d4f10553d6f329001a34ccc3e063d448e1d',
|
|
47
|
+
'slopnators/sl_unnamed_island.js': '0c92c5b6fdf59fb97448f24d72b883cd64317c23280316e043f993e7b75ac1da',
|
|
48
|
+
'slopnators/sl_unpaired_member.js': 'aafba4f829ef038289176b6e5ea6622494eba1982268d8d52b44f8c9889943e9',
|
|
49
|
+
'slopnators/sl_unpaired_slopnator.js': '2fa5b8c3c8b6a8a71466086f1d3ae32ea16fd22c49b113225e3c9a7e76eda45e',
|
|
50
|
+
'slopnators/sl_unprefixed_member.js': '67640331089c55ac59e7950e3c919144740309fb4a91cf0af87c41edb761f75d',
|
|
51
|
+
'slopnators/sl_unseamed_reach.js': 'd27a64c85b221e7c5bac99ab9d36955ad4945520d2b9e5ec75927574ca454cda',
|
|
52
|
+
'slopnators/sl_unthreaded.js': 'b686212f3515eb2769030979f7141d236677c9f3c1802e436f254ea5410a2361',
|
|
53
|
+
'slopnators/sl_untranslated_key.js': '224e80a4f34ce760502ee256347c9191e8d103e8b0ff58ef995c10111ddf2854',
|
|
54
|
+
'slopnators/sl_unversioned.js': 'df73b3186e42e6b58e7fb3fd48c3146a2d3902a337861a6ccb52f433a569e392',
|
|
55
|
+
'sweep.js': '2402ba042f87e693bf785d8e81b40870dd2fec539a70a9593d7ecba767dcda2c',
|
|
56
|
+
};
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { type Constraint } from './constraint.ts';
|
|
2
|
+
export declare const NOT_WRITTEN: unique symbol;
|
|
3
|
+
export declare const SHIPPED: string;
|
|
4
|
+
export declare const SEEDED: string;
|
|
5
|
+
export declare const ROOT: string;
|
|
6
|
+
export declare const DEPTH = "THREAD_DEPTH";
|
|
7
|
+
export declare const CEILING = 1;
|
|
8
|
+
export declare const SPAWN_TIMEOUT = 120000;
|
|
9
|
+
export declare const SPAWN_BUFFER: number;
|
|
10
|
+
export declare function nested(): boolean;
|
|
11
|
+
export declare function ran(command: string, options: {
|
|
12
|
+
readonly cwd: string;
|
|
13
|
+
readonly args?: readonly string[];
|
|
14
|
+
}): {
|
|
15
|
+
readonly status: number | null;
|
|
16
|
+
readonly said: string;
|
|
17
|
+
} | undefined;
|
|
18
|
+
export interface SlopnatorContext {
|
|
19
|
+
readonly filename: string;
|
|
20
|
+
readonly text: string;
|
|
21
|
+
readonly ancestors: () => readonly unknown[];
|
|
22
|
+
readonly comments: () => readonly Spoken[];
|
|
23
|
+
}
|
|
24
|
+
export interface Spoken {
|
|
25
|
+
readonly value: string;
|
|
26
|
+
readonly range: readonly [number, number];
|
|
27
|
+
}
|
|
28
|
+
export interface Proves {
|
|
29
|
+
readonly wrong: readonly Proof[];
|
|
30
|
+
readonly right: readonly Proof[];
|
|
31
|
+
}
|
|
32
|
+
export interface Reads {
|
|
33
|
+
readonly holds: (node: unknown, at: SlopnatorContext) => boolean | string | typeof NOT_WRITTEN;
|
|
34
|
+
readonly fixes?: (node: unknown, at: SlopnatorContext) => string | undefined;
|
|
35
|
+
readonly proves: Proves;
|
|
36
|
+
}
|
|
37
|
+
export interface Reading extends Reads {
|
|
38
|
+
readonly on?: readonly string[];
|
|
39
|
+
}
|
|
40
|
+
export interface Running {
|
|
41
|
+
readonly checks: (root: string) => readonly string[];
|
|
42
|
+
readonly proves: Proves;
|
|
43
|
+
}
|
|
44
|
+
export type Slopnator = Reading | Running;
|
|
45
|
+
export type Visiting = Reading & {
|
|
46
|
+
readonly on: readonly string[];
|
|
47
|
+
};
|
|
48
|
+
export declare const reads: (one: Slopnator) => one is Reading;
|
|
49
|
+
export declare const visits: (one: Slopnator) => one is Visiting;
|
|
50
|
+
export declare const sweeps: (one: Slopnator) => one is Reading;
|
|
51
|
+
export interface Sourced {
|
|
52
|
+
readonly at: string;
|
|
53
|
+
readonly source: string;
|
|
54
|
+
}
|
|
55
|
+
export type Proof = string | {
|
|
56
|
+
readonly files: readonly Sourced[];
|
|
57
|
+
readonly at: string;
|
|
58
|
+
};
|
|
59
|
+
export interface Byproduct {
|
|
60
|
+
readonly path: string;
|
|
61
|
+
readonly content: string;
|
|
62
|
+
}
|
|
63
|
+
export declare function slopnatorName(rule: string): string;
|
|
64
|
+
export declare function names(fragment: string, path: readonly string[]): boolean;
|
|
65
|
+
export declare function governs(rule: Constraint, filename: string): boolean;
|
|
66
|
+
export declare class UnloadableSlopnatorError extends Error {
|
|
67
|
+
constructor(rule: string, at: string, because: string);
|
|
68
|
+
}
|
|
69
|
+
export declare class UnreadableRulesError extends Error {
|
|
70
|
+
constructor(at: string, because: string);
|
|
71
|
+
}
|
|
72
|
+
export declare class BrokenSealError extends Error {
|
|
73
|
+
constructor(at: string, why: string);
|
|
74
|
+
}
|
|
75
|
+
export declare function unsealed(home?: string): readonly string[];
|
|
76
|
+
export declare function verify(): void;
|
|
77
|
+
export declare function load(root: string, all: readonly Constraint[]): Promise<ReadonlyMap<string, Slopnator>>;
|
|
78
|
+
export declare function proofsOf(rule: Constraint, slopnators: ReadonlyMap<string, Slopnator>): {
|
|
79
|
+
readonly wrong: readonly Proof[];
|
|
80
|
+
readonly right: readonly Proof[];
|
|
81
|
+
};
|
|
82
|
+
export declare function scaffold(): string;
|
|
83
|
+
export declare function write(root: string, byproduct: Byproduct): void;
|
|
84
|
+
export declare function onDisk(root: string): readonly string[];
|
|
85
|
+
export declare function named(all: readonly Constraint[]): readonly string[];
|
|
86
|
+
export declare function rules(): Promise<readonly Constraint[] | undefined>;
|
|
87
|
+
export declare function seeds(root: string, all: readonly Constraint[]): readonly Byproduct[];
|
|
88
|
+
export declare const GENERALLY = "The check the rule beside it refuses with. The rule says what is banned and this finds it, reading every file that rule governs and answering whether each one breaks it.";
|
|
89
|
+
export declare function declaring(at: string, says: string): string;
|
|
90
|
+
export declare function declareSeeded(root: string, ids: readonly string[], says?: string): Promise<readonly string[]>;
|
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExtension) || function (path, preserveJsx) {
|
|
2
|
+
if (typeof path === "string" && /^\.\.?\//.test(path)) {
|
|
3
|
+
return path.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function (m, tsx, d, ext, cm) {
|
|
4
|
+
return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : (d + ext + "." + cm.toLowerCase() + "js");
|
|
5
|
+
});
|
|
6
|
+
}
|
|
7
|
+
return path;
|
|
8
|
+
};
|
|
9
|
+
import { spawnSync } from 'node:child_process';
|
|
10
|
+
import { createHash } from 'node:crypto';
|
|
11
|
+
import { existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync } from 'node:fs';
|
|
12
|
+
import { dirname, join, sep } from 'node:path';
|
|
13
|
+
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
14
|
+
import { invoked } from "../core/invoked.js";
|
|
15
|
+
import { listBounds, quoted, withSeed } from "../core/rows.js";
|
|
16
|
+
import { CONSTRAINTS } from "./constraint.js";
|
|
17
|
+
import { SEALED } from "./sealed.js";
|
|
18
|
+
import { findTree, inTree } from "../core/tree.js";
|
|
19
|
+
export const NOT_WRITTEN = Symbol.for('thread.slopnator.not_written');
|
|
20
|
+
const HOME = dirname(fileURLToPath(import.meta.url));
|
|
21
|
+
const BUILT = dirname(dirname(HOME));
|
|
22
|
+
export const SHIPPED = `${HOME}/slopnators`;
|
|
23
|
+
export const SEEDED = `${findTree().name}/slopnators`;
|
|
24
|
+
function homes(root) {
|
|
25
|
+
return [SHIPPED, join(root, SEEDED)];
|
|
26
|
+
}
|
|
27
|
+
export const ROOT = `${findTree().at}/`;
|
|
28
|
+
export const DEPTH = 'THREAD_DEPTH';
|
|
29
|
+
export const CEILING = 1;
|
|
30
|
+
export const SPAWN_TIMEOUT = 120_000;
|
|
31
|
+
export const SPAWN_BUFFER = 8 * 1024 * 1024;
|
|
32
|
+
export function nested() {
|
|
33
|
+
return Number(process.env[DEPTH] ?? '0') >= CEILING;
|
|
34
|
+
}
|
|
35
|
+
export function ran(command, options) {
|
|
36
|
+
if (nested())
|
|
37
|
+
return undefined;
|
|
38
|
+
const depth = Number(process.env[DEPTH] ?? '0');
|
|
39
|
+
const out = spawnSync(command, [...(options.args ?? [])], {
|
|
40
|
+
shell: options.args === undefined,
|
|
41
|
+
cwd: options.cwd,
|
|
42
|
+
encoding: 'utf8',
|
|
43
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
44
|
+
timeout: SPAWN_TIMEOUT,
|
|
45
|
+
killSignal: 'SIGKILL',
|
|
46
|
+
maxBuffer: SPAWN_BUFFER,
|
|
47
|
+
env: { ...process.env, [DEPTH]: String(depth + 1) },
|
|
48
|
+
});
|
|
49
|
+
return { status: out.status, said: `${String(out.stdout ?? '')}${String(out.stderr ?? '')}` };
|
|
50
|
+
}
|
|
51
|
+
export const reads = (one) => 'holds' in one;
|
|
52
|
+
export const visits = (one) => 'holds' in one && one.on !== undefined;
|
|
53
|
+
export const sweeps = (one) => 'holds' in one && one.on === undefined;
|
|
54
|
+
export function slopnatorName(rule) {
|
|
55
|
+
return `sl_${rule
|
|
56
|
+
.replace(/[^a-zA-Z0-9]+/g, '_')
|
|
57
|
+
.replace(/^_+|_+$/g, '')
|
|
58
|
+
.toLowerCase()}`;
|
|
59
|
+
}
|
|
60
|
+
const CANONICAL_TREE = 'threads';
|
|
61
|
+
const TREE = findTree().name;
|
|
62
|
+
function parts(one) {
|
|
63
|
+
return one.split('\\').join('/').split('/').filter((part) => part !== '');
|
|
64
|
+
}
|
|
65
|
+
function wanted(fragment) {
|
|
66
|
+
const all = parts(fragment);
|
|
67
|
+
return all[0] === CANONICAL_TREE ? [TREE, ...all.slice(1)] : all;
|
|
68
|
+
}
|
|
69
|
+
export function names(fragment, path) {
|
|
70
|
+
if (fragment === '.')
|
|
71
|
+
return true;
|
|
72
|
+
const want = wanted(fragment);
|
|
73
|
+
if (want.length === 0)
|
|
74
|
+
return false;
|
|
75
|
+
if (!fragment.endsWith('/'))
|
|
76
|
+
return want.join('/') === path.join('/');
|
|
77
|
+
for (let at = 0; at + want.length < path.length; at += 1) {
|
|
78
|
+
if (want.every((part, step) => part === path[at + step]))
|
|
79
|
+
return true;
|
|
80
|
+
}
|
|
81
|
+
return false;
|
|
82
|
+
}
|
|
83
|
+
export function governs(rule, filename) {
|
|
84
|
+
const path = parts(filename.startsWith(ROOT) ? filename.slice(ROOT.length) : filename);
|
|
85
|
+
if ((rule.where.except ?? []).some((fragment) => names(fragment, path)))
|
|
86
|
+
return false;
|
|
87
|
+
return rule.where.paths.some((fragment) => names(fragment, path));
|
|
88
|
+
}
|
|
89
|
+
export class UnloadableSlopnatorError extends Error {
|
|
90
|
+
constructor(rule, at, because) {
|
|
91
|
+
super(`rule "${rule}" names a slopnator at ${at}, the file is on disk, and loading it threw: ${because}. ` +
|
|
92
|
+
'A slopnated that cannot be imported reads from every caller exactly like a slopnator that found nothing — ' +
|
|
93
|
+
'the sweep skips it, the trial falls through, and the rule reports green while enforcing nothing. ' +
|
|
94
|
+
'Fix the import, or delete the rule.');
|
|
95
|
+
this.name = 'UnloadableSlopnatorError';
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
export class UnreadableRulesError extends Error {
|
|
99
|
+
constructor(at, because) {
|
|
100
|
+
super(`${at} is where this tree states the rules it authors, and reading it threw: ${because}. ` +
|
|
101
|
+
'A tree whose rules cannot be read reports exactly like a tree that declared none — every surface runs, finds nothing of its own, and exits 0. ' +
|
|
102
|
+
'Fix the file, or delete it if this tree authors no rules of its own.');
|
|
103
|
+
this.name = 'UnreadableRulesError';
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
export class BrokenSealError extends Error {
|
|
107
|
+
constructor(at, why) {
|
|
108
|
+
super(`${at} ${why} — this package is not the one that was published, so nothing it refuses or allows can be trusted. ` +
|
|
109
|
+
'Reinstall it (`pnpm install --frozen-lockfile` re-verifies the published bytes), or if you meant to change it, run `thread seal` and publish that.');
|
|
110
|
+
this.name = 'BrokenSealError';
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
const INSTALLED = HOME.includes(`${sep}node_modules${sep}`);
|
|
114
|
+
export function unsealed(home = HOME) {
|
|
115
|
+
const broken = [];
|
|
116
|
+
for (const [at, hash] of Object.entries(SEALED)) {
|
|
117
|
+
let held;
|
|
118
|
+
try {
|
|
119
|
+
held = readFileSync(join(home, at));
|
|
120
|
+
}
|
|
121
|
+
catch {
|
|
122
|
+
broken.push(`${at} is missing`);
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
125
|
+
if (createHash('sha256').update(held).digest('hex') !== hash)
|
|
126
|
+
broken.push(`${at} differs from the seal`);
|
|
127
|
+
}
|
|
128
|
+
return broken;
|
|
129
|
+
}
|
|
130
|
+
export function verify() {
|
|
131
|
+
if (!INSTALLED)
|
|
132
|
+
return;
|
|
133
|
+
const broken = unsealed(BUILT);
|
|
134
|
+
const first = broken[0];
|
|
135
|
+
if (first !== undefined)
|
|
136
|
+
throw new BrokenSealError(first.split(' ')[0] ?? '', first.split(' ').slice(1).join(' '));
|
|
137
|
+
}
|
|
138
|
+
export async function load(root, all) {
|
|
139
|
+
verify();
|
|
140
|
+
const found = new Map();
|
|
141
|
+
for (const rule of all) {
|
|
142
|
+
const name = slopnatorName(rule.id);
|
|
143
|
+
const at = homes(root)
|
|
144
|
+
.flatMap((dir) => [join(dir, `${name}.ts`), join(dir, `${name}.js`)])
|
|
145
|
+
.find((one) => existsSync(one));
|
|
146
|
+
if (at === undefined)
|
|
147
|
+
continue;
|
|
148
|
+
let module;
|
|
149
|
+
try {
|
|
150
|
+
module = await import(__rewriteRelativeImportExtension(pathToFileURL(at).href));
|
|
151
|
+
}
|
|
152
|
+
catch (thrown) {
|
|
153
|
+
throw new UnloadableSlopnatorError(rule.id, at, thrown instanceof Error ? thrown.message : String(thrown));
|
|
154
|
+
}
|
|
155
|
+
const one = module.slopnator;
|
|
156
|
+
if (one === undefined) {
|
|
157
|
+
throw new UnloadableSlopnatorError(rule.id, at, 'it exports no `slopnator`');
|
|
158
|
+
}
|
|
159
|
+
found.set(name, one);
|
|
160
|
+
}
|
|
161
|
+
return found;
|
|
162
|
+
}
|
|
163
|
+
export function proofsOf(rule, slopnators) {
|
|
164
|
+
const slopnated = slopnators.get(slopnatorName(rule.id))?.proves;
|
|
165
|
+
return { wrong: [rule.wrong, ...(slopnated?.wrong ?? [])], right: [rule.right, ...(slopnated?.right ?? [])] };
|
|
166
|
+
}
|
|
167
|
+
export function scaffold() {
|
|
168
|
+
return `import { NOT_WRITTEN, type Slopnator } from './slopnator.ts'
|
|
169
|
+
|
|
170
|
+
export const slopnator: Slopnator = {
|
|
171
|
+
on: [],
|
|
172
|
+
holds: () => NOT_WRITTEN,
|
|
173
|
+
proves: { wrong: [], right: [] },
|
|
174
|
+
}
|
|
175
|
+
`;
|
|
176
|
+
}
|
|
177
|
+
export function write(root, byproduct) {
|
|
178
|
+
const path = join(root, byproduct.path);
|
|
179
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
180
|
+
writeFileSync(path, byproduct.content, 'utf8');
|
|
181
|
+
}
|
|
182
|
+
export function onDisk(root) {
|
|
183
|
+
const found = new Set();
|
|
184
|
+
for (const dir of homes(root)) {
|
|
185
|
+
if (!existsSync(dir))
|
|
186
|
+
continue;
|
|
187
|
+
for (const file of readdirSync(dir)) {
|
|
188
|
+
if (file.startsWith('sl_') && file.endsWith('.ts'))
|
|
189
|
+
found.add(file.replace(/\.ts$/, ''));
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
return [...found].sort();
|
|
193
|
+
}
|
|
194
|
+
export function named(all) {
|
|
195
|
+
return all.map((rule) => slopnatorName(rule.id)).sort();
|
|
196
|
+
}
|
|
197
|
+
export async function rules() {
|
|
198
|
+
const at = inTree(findTree(), 'rules.ts');
|
|
199
|
+
if (!existsSync(at))
|
|
200
|
+
return undefined;
|
|
201
|
+
let module;
|
|
202
|
+
try {
|
|
203
|
+
module = (await import(__rewriteRelativeImportExtension(pathToFileURL(at).href)));
|
|
204
|
+
}
|
|
205
|
+
catch (thrown) {
|
|
206
|
+
throw new UnreadableRulesError(at, thrown instanceof Error ? thrown.message : String(thrown));
|
|
207
|
+
}
|
|
208
|
+
if (module.RULES === undefined)
|
|
209
|
+
throw new UnreadableRulesError(at, 'it exports no `RULES`');
|
|
210
|
+
return module.RULES;
|
|
211
|
+
}
|
|
212
|
+
export function seeds(root, all) {
|
|
213
|
+
const found = onDisk(root);
|
|
214
|
+
return named(all)
|
|
215
|
+
.filter((wanted) => !found.includes(wanted))
|
|
216
|
+
.map((wanted) => ({ path: `${SEEDED}/${wanted}.ts`, content: scaffold() }));
|
|
217
|
+
}
|
|
218
|
+
export const GENERALLY = 'The check the rule beside it refuses with. The rule says what is banned and this finds it, reading every file that rule governs and answering whether each one breaks it.';
|
|
219
|
+
export function declaring(at, says) {
|
|
220
|
+
return [
|
|
221
|
+
' {',
|
|
222
|
+
" id: 'slopnator',",
|
|
223
|
+
` at: '${at}',`,
|
|
224
|
+
` describes: ${quoted(says)},`,
|
|
225
|
+
' },',
|
|
226
|
+
].join('\n');
|
|
227
|
+
}
|
|
228
|
+
export async function declareSeeded(root, ids, says) {
|
|
229
|
+
if (ids.length === 0)
|
|
230
|
+
return [];
|
|
231
|
+
const at = inTree(findTree(), 'rules.ts');
|
|
232
|
+
let text;
|
|
233
|
+
try {
|
|
234
|
+
text = readFileSync(at, 'utf8');
|
|
235
|
+
}
|
|
236
|
+
catch {
|
|
237
|
+
return [];
|
|
238
|
+
}
|
|
239
|
+
const where = listBounds(text, 'RULES');
|
|
240
|
+
if (where === undefined)
|
|
241
|
+
return [];
|
|
242
|
+
const done = [];
|
|
243
|
+
for (const id of ids) {
|
|
244
|
+
const next = withSeed(text, listBounds(text, 'RULES') ?? where, id, declaring(`${SEEDED}/${slopnatorName(id)}.ts`, says ?? GENERALLY));
|
|
245
|
+
if (next === text)
|
|
246
|
+
continue;
|
|
247
|
+
text = next;
|
|
248
|
+
done.push(id);
|
|
249
|
+
}
|
|
250
|
+
if (done.length > 0)
|
|
251
|
+
writeFileSync(at, text, 'utf8');
|
|
252
|
+
return done;
|
|
253
|
+
}
|
|
254
|
+
if (invoked(import.meta.url)) {
|
|
255
|
+
const own = (await rules()) ?? [];
|
|
256
|
+
const seeded = seeds(ROOT, own);
|
|
257
|
+
for (const byproduct of seeded) {
|
|
258
|
+
write(ROOT, byproduct);
|
|
259
|
+
console.log(`slopnator: seeded ${byproduct.path}`);
|
|
260
|
+
}
|
|
261
|
+
const held = new Set(onDisk(ROOT));
|
|
262
|
+
const declared = await declareSeeded(ROOT, own.filter((rule) => held.has(slopnatorName(rule.id))).map((rule) => rule.id));
|
|
263
|
+
for (const id of declared)
|
|
264
|
+
console.log(`slopnator: declared the slopnator for "${id}" in its own row`);
|
|
265
|
+
console.log(`slopnator: ${String(own.length)} rule(s), ${String(seeded.length)} seeded, ${String(declared.length)} declared. ` +
|
|
266
|
+
`${String(CONSTRAINTS.length)} shipped constraint(s) are authored, not seeded — unpaired-slopnator refuses one whose slopnated is missing.`);
|
|
267
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { here, names, threaded, unreadable } from "./sl_unthreaded.js";
|
|
2
|
+
import { findTree } from "../../core/tree.js";
|
|
3
|
+
const THREAD_AT = `${findTree().name}/thread.ts`;
|
|
4
|
+
export const slopnator = {
|
|
5
|
+
holds: (_node, at) => {
|
|
6
|
+
const path = here(at.filename);
|
|
7
|
+
if (path === THREAD_AT)
|
|
8
|
+
return false;
|
|
9
|
+
if (unreadable(path, at.text) !== undefined)
|
|
10
|
+
return false;
|
|
11
|
+
const children = names(path, at.text);
|
|
12
|
+
if (children.length === 0)
|
|
13
|
+
return false;
|
|
14
|
+
if (threaded().has(path))
|
|
15
|
+
return false;
|
|
16
|
+
return `this file names ${String(children.length)} child(ren) — ${children.slice(0, 3).join(', ')} — and its own origin does not trace back to threads/, so everything it parents is stranded with it`;
|
|
17
|
+
},
|
|
18
|
+
proves: {
|
|
19
|
+
wrong: [
|
|
20
|
+
"export const M: Threaded = {\n declares: [\n {\n at: 'scripts/finding.ts',\n },\n ],\n}",
|
|
21
|
+
"export const M: Threaded = {\n derives: [\n {\n at: 'scripts/finding.ts',\n },\n ],\n}",
|
|
22
|
+
],
|
|
23
|
+
right: [
|
|
24
|
+
{
|
|
25
|
+
files: [
|
|
26
|
+
{ at: 'threads/_names_it.ts', source: "export const OWNER: Threaded = {\n declares: [\n {\n at: 'scripts/_probe_mid.ts',\n },\n ],\n}\n" },
|
|
27
|
+
{ at: 'scripts/_probe_mid.ts', source: "export const MID: Threaded = {\n declares: [\n {\n at: 'scripts/_probe_end.ts',\n },\n ],\n}\n" },
|
|
28
|
+
{ at: 'scripts/_probe_end.ts', source: 'export const END = 1\n' },
|
|
29
|
+
],
|
|
30
|
+
at: 'scripts/_probe_mid.ts',
|
|
31
|
+
},
|
|
32
|
+
'export const PLAIN = 1',
|
|
33
|
+
],
|
|
34
|
+
},
|
|
35
|
+
};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { filesGoverned, here, islandsDeclared } from "./sl_unthreaded.js";
|
|
2
|
+
const sentence = (describes) => `${describes.split(/ — |\. /)[0] ?? describes}.`;
|
|
3
|
+
export const slopnator = {
|
|
4
|
+
holds: (_node, at) => {
|
|
5
|
+
const path = here(at.filename);
|
|
6
|
+
for (const island of islandsDeclared()) {
|
|
7
|
+
if (island.define === undefined)
|
|
8
|
+
continue;
|
|
9
|
+
const call = new RegExp(`(?<!function\\s)\\b${island.define}\\s*\\(`);
|
|
10
|
+
if (!call.test(at.text))
|
|
11
|
+
continue;
|
|
12
|
+
const places = filesGoverned(island.governs);
|
|
13
|
+
if (places.includes(path))
|
|
14
|
+
continue;
|
|
15
|
+
const where = places.length === 0 ? 'nowhere — its kind is derived, never authored' : places.join(' or ');
|
|
16
|
+
return (`\`${island.define}\` is called here and the "${island.id}" island governs ${where}, so the loader never opens this ` +
|
|
17
|
+
`file for it and the declaration produces nothing. ${sentence(island.describes)}`);
|
|
18
|
+
}
|
|
19
|
+
return false;
|
|
20
|
+
},
|
|
21
|
+
proves: {
|
|
22
|
+
wrong: [
|
|
23
|
+
"export default defineEvents({ specVersion: 1, events: [] })",
|
|
24
|
+
"export default definePermissions({ specVersion: 1, entities: [] })",
|
|
25
|
+
"export default defineMap({ specVersion: 1, screens: [] })",
|
|
26
|
+
],
|
|
27
|
+
right: [
|
|
28
|
+
"import events from 'app/_declare/events'",
|
|
29
|
+
"import permissions from 'app/_declare/permissions'",
|
|
30
|
+
'const screens = useRead(recentScreens)',
|
|
31
|
+
],
|
|
32
|
+
},
|
|
33
|
+
};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { Slopnator } from '../slopnator.ts';
|
|
2
|
+
export interface Derivation {
|
|
3
|
+
readonly at: string;
|
|
4
|
+
readonly by?: string;
|
|
5
|
+
readonly runs?: string;
|
|
6
|
+
}
|
|
7
|
+
export declare function derivations(root: string): readonly Derivation[];
|
|
8
|
+
export declare const slopnator: Slopnator;
|