@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,58 @@
|
|
|
1
|
+
import { here, islandsDeclared } from "./sl_unthreaded.js";
|
|
2
|
+
import { findTree } from "../../core/tree.js";
|
|
3
|
+
const TREE = `${findTree().name}/`;
|
|
4
|
+
const GOVERNS = /^\s*governs:\s*'([^']+)'/gm;
|
|
5
|
+
export const slopnator = {
|
|
6
|
+
holds: (_node, at) => {
|
|
7
|
+
const path = here(at.filename);
|
|
8
|
+
if (!path.startsWith(TREE))
|
|
9
|
+
return false;
|
|
10
|
+
let islands;
|
|
11
|
+
try {
|
|
12
|
+
islands = islandsDeclared().map((one) => one.id);
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
return 'this file cannot be parsed, so the islands the tree declares were never read and nothing a law threaded to govern was measured';
|
|
16
|
+
}
|
|
17
|
+
for (const match of at.text.matchAll(GOVERNS)) {
|
|
18
|
+
const governed = match[1] ?? '';
|
|
19
|
+
if (islands.includes(governed))
|
|
20
|
+
continue;
|
|
21
|
+
return `a law governs "${governed}" and the tree declares no island by that name, so the rule reads as coverage of a kind nobody can declare`;
|
|
22
|
+
}
|
|
23
|
+
return false;
|
|
24
|
+
},
|
|
25
|
+
proves: {
|
|
26
|
+
wrong: [
|
|
27
|
+
{
|
|
28
|
+
files: [
|
|
29
|
+
{
|
|
30
|
+
at: 'threads/_governed_probe.ts',
|
|
31
|
+
source: "export const L: Constraint = {\n id: 'a-law',\n governs: 'no-island-by-this-name',\n}\n",
|
|
32
|
+
},
|
|
33
|
+
],
|
|
34
|
+
at: 'threads/_governed_probe.ts',
|
|
35
|
+
},
|
|
36
|
+
],
|
|
37
|
+
right: [
|
|
38
|
+
{
|
|
39
|
+
files: [
|
|
40
|
+
{
|
|
41
|
+
at: 'threads/_governed_probe.ts',
|
|
42
|
+
source: "export const L: Constraint = {\n id: 'a-law',\n governs: 'entities',\n}\n",
|
|
43
|
+
},
|
|
44
|
+
],
|
|
45
|
+
at: 'threads/_governed_probe.ts',
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
files: [
|
|
49
|
+
{
|
|
50
|
+
at: 'threads/_governed_probe.ts',
|
|
51
|
+
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",
|
|
52
|
+
},
|
|
53
|
+
],
|
|
54
|
+
at: 'threads/_governed_probe.ts',
|
|
55
|
+
},
|
|
56
|
+
],
|
|
57
|
+
},
|
|
58
|
+
};
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { existsSync, readdirSync, readFileSync } from 'node:fs';
|
|
2
|
+
import { resolve } from 'node:path';
|
|
3
|
+
const STORES = 'src/stores';
|
|
4
|
+
/* What hands an operation its seams, seeded once and then the app's own. */
|
|
5
|
+
const SEAMS = 'src/seams.ts';
|
|
6
|
+
/* What the declarations settle, which the seams file is expected to hand on. */
|
|
7
|
+
const DECLARED = 'DECLARED';
|
|
8
|
+
/*
|
|
9
|
+
* WHAT THIS READS, AND WHY IT READS THAT RATHER THAN THE TARGETS SHEET.
|
|
10
|
+
*
|
|
11
|
+
* A target that keeps entities derives a store of its own, written by `thread schema` into
|
|
12
|
+
* `src/stores/<target>.ts`. So one of those files existing IS the fact that this app declares
|
|
13
|
+
* somewhere to keep rows — already resolved, by the command that resolved it, with no second reading
|
|
14
|
+
* of what a reach can hold and no chance of the two disagreeing about it.
|
|
15
|
+
*
|
|
16
|
+
* Where rows go is settled in `src/_generated/seams.ts`, which `thread capabilities` writes from those
|
|
17
|
+
* stores and refuses when it drifts. The one place it can still go wrong is `src/seams.ts`, which is
|
|
18
|
+
* the app's own: an app that stops handing on what was settled there hands its operations a store
|
|
19
|
+
* that keeps nothing, while the tables are written, the migrations are written, and the gate is green.
|
|
20
|
+
*/
|
|
21
|
+
function held(root, at) {
|
|
22
|
+
const path = resolve(root, at);
|
|
23
|
+
try {
|
|
24
|
+
return existsSync(path) ? readFileSync(path, 'utf8') : '';
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
return '';
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
function derived(root) {
|
|
31
|
+
const at = resolve(root, STORES);
|
|
32
|
+
try {
|
|
33
|
+
return existsSync(at) ? readdirSync(at).filter((one) => one.endsWith('.ts')) : [];
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
return [];
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
export const slopnator = {
|
|
40
|
+
checks: (root) => {
|
|
41
|
+
const stores = derived(root);
|
|
42
|
+
if (stores.length === 0)
|
|
43
|
+
return [];
|
|
44
|
+
/*
|
|
45
|
+
* AN APP WITH NOTHING THAT HANDS A STORE TO AN OPERATION IS NOT KEEPING ROWS THROUGH ONE, SO
|
|
46
|
+
* THERE IS NOTHING TO REFUSE.
|
|
47
|
+
*
|
|
48
|
+
* The seams file is seeded by `thread capabilities`, which ships in the operations package — so
|
|
49
|
+
* an app that attached data and integrity and nothing else has no seams and no command that
|
|
50
|
+
* seeds one. Its absence means rows go nowhere yet, which is a state to pass rather than refuse:
|
|
51
|
+
* nothing is being lost, because nothing is being kept.
|
|
52
|
+
*/
|
|
53
|
+
const where = held(root, SEAMS);
|
|
54
|
+
if (where === '')
|
|
55
|
+
return [];
|
|
56
|
+
if (where.includes(DECLARED))
|
|
57
|
+
return [];
|
|
58
|
+
return [
|
|
59
|
+
`${STORES} holds ${stores.join(', ')} and ${SEAMS} no longer hands on ${DECLARED}, so every row this app keeps goes ` +
|
|
60
|
+
'somewhere that is gone the moment it stops. The tables are written, the migrations are written, the gate is ' +
|
|
61
|
+
`green, and the database is never opened. Spread ${DECLARED} back into SEAMS, or name its store there`,
|
|
62
|
+
];
|
|
63
|
+
},
|
|
64
|
+
proves: {
|
|
65
|
+
wrong: [
|
|
66
|
+
{
|
|
67
|
+
files: [
|
|
68
|
+
{ at: 'src/stores/pl_probe.ts', source: 'export const store = {}\n' },
|
|
69
|
+
{
|
|
70
|
+
at: 'src/seams.ts',
|
|
71
|
+
source: "import { inMemorySeams } from '../../operations/memory.ts'\n\nexport const SEAMS = inMemorySeams().seams\n",
|
|
72
|
+
},
|
|
73
|
+
],
|
|
74
|
+
at: 'src/seams.ts',
|
|
75
|
+
},
|
|
76
|
+
],
|
|
77
|
+
right: [
|
|
78
|
+
{
|
|
79
|
+
files: [
|
|
80
|
+
{ at: 'src/stores/pl_probe.ts', source: 'export const store = {}\n' },
|
|
81
|
+
{
|
|
82
|
+
at: 'src/seams.ts',
|
|
83
|
+
source: "import { DECLARED } from './_generated/seams.ts'\n\nexport const SEAMS = { ...DECLARED }\n",
|
|
84
|
+
},
|
|
85
|
+
],
|
|
86
|
+
at: 'src/seams.ts',
|
|
87
|
+
},
|
|
88
|
+
/* An app that derived a store and has nothing handing one to an operation — data attached, operations not. */
|
|
89
|
+
{
|
|
90
|
+
files: [{ at: 'src/stores/pl_probe.ts', source: 'export const store = {}\n' }],
|
|
91
|
+
at: 'src/seams.ts',
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
files: [
|
|
95
|
+
{
|
|
96
|
+
at: 'src/seams.ts',
|
|
97
|
+
source: "import { inMemorySeams } from '../../operations/memory.ts'\n\nexport const SEAMS = inMemorySeams().seams\n",
|
|
98
|
+
},
|
|
99
|
+
],
|
|
100
|
+
at: 'src/seams.ts',
|
|
101
|
+
},
|
|
102
|
+
],
|
|
103
|
+
},
|
|
104
|
+
};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { declaresIsland, here, islandsDeclared } from "./sl_unthreaded.js";
|
|
2
|
+
export const slopnator = {
|
|
3
|
+
holds: (_node, at) => {
|
|
4
|
+
const schema = declaresIsland(at.text);
|
|
5
|
+
if (schema === undefined)
|
|
6
|
+
return false;
|
|
7
|
+
const path = here(at.filename);
|
|
8
|
+
if (islandsDeclared().some((island) => island.at === path))
|
|
9
|
+
return false;
|
|
10
|
+
return (`${schema}IslandSchema is an island and no row in the tree names it, so nothing states what it governs and ` +
|
|
11
|
+
'no law can reach it — the kind is declarable and every rule about where it may be declared is absent');
|
|
12
|
+
},
|
|
13
|
+
proves: {
|
|
14
|
+
wrong: [
|
|
15
|
+
{
|
|
16
|
+
files: [
|
|
17
|
+
{
|
|
18
|
+
at: 'packages/core/spec/src/islands/_unnamed_probe.ts',
|
|
19
|
+
source: 'export const ThingIslandSchema = z.strictObject({\n specVersion: z.literal(1),\n})\n',
|
|
20
|
+
},
|
|
21
|
+
],
|
|
22
|
+
at: 'packages/core/spec/src/islands/_unnamed_probe.ts',
|
|
23
|
+
},
|
|
24
|
+
],
|
|
25
|
+
right: [
|
|
26
|
+
{
|
|
27
|
+
files: [
|
|
28
|
+
{
|
|
29
|
+
at: 'packages/core/spec/src/islands/_unnamed_probe.ts',
|
|
30
|
+
source: 'export const ThingSchema = z.strictObject({\n name: z.string(),\n})\n',
|
|
31
|
+
},
|
|
32
|
+
],
|
|
33
|
+
at: 'packages/core/spec/src/islands/_unnamed_probe.ts',
|
|
34
|
+
},
|
|
35
|
+
],
|
|
36
|
+
},
|
|
37
|
+
};
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { existsSync, readFileSync, readdirSync } from 'node:fs';
|
|
2
|
+
import { resolve } from 'node:path';
|
|
3
|
+
import { foldersGoverned, here, islandsDeclared, rowsIn } from "./sl_unthreaded.js";
|
|
4
|
+
import { findTree } from "../../core/tree.js";
|
|
5
|
+
const ROOT = findTree().at;
|
|
6
|
+
const TREE = `${findTree().name}/`;
|
|
7
|
+
const seeding = (one) => {
|
|
8
|
+
const folder = foldersGoverned(one.governs)[0];
|
|
9
|
+
if (folder === undefined || one.tree === undefined || one.member === undefined)
|
|
10
|
+
return undefined;
|
|
11
|
+
return { island: one.id, folder, tree: one.tree, member: one.member };
|
|
12
|
+
};
|
|
13
|
+
const islandsSeeding = () => islandsDeclared().flatMap((one) => {
|
|
14
|
+
const held = seeding(one);
|
|
15
|
+
return held === undefined ? [] : [held];
|
|
16
|
+
});
|
|
17
|
+
const declares = (island) => new RegExp(`^export const ${island.toUpperCase()}\\b`, 'm');
|
|
18
|
+
const exports = (member) => new RegExp(`^export const ${member}\\b`, 'm');
|
|
19
|
+
function seededNames(text) {
|
|
20
|
+
const found = [];
|
|
21
|
+
for (const row of rowsIn(text)) {
|
|
22
|
+
if (row.list !== undefined || row.id === undefined)
|
|
23
|
+
continue;
|
|
24
|
+
found.push(`${row.id}.ts`);
|
|
25
|
+
}
|
|
26
|
+
return found;
|
|
27
|
+
}
|
|
28
|
+
function onDisk(folder) {
|
|
29
|
+
const dir = resolve(ROOT, folder);
|
|
30
|
+
if (!existsSync(dir))
|
|
31
|
+
return [];
|
|
32
|
+
try {
|
|
33
|
+
return readdirSync(dir).filter((name) => name.endsWith('.ts'));
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
return [];
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
function declaredNames(one) {
|
|
40
|
+
const path = resolve(ROOT, one.tree);
|
|
41
|
+
if (!existsSync(path))
|
|
42
|
+
return [];
|
|
43
|
+
try {
|
|
44
|
+
return seededNames(readFileSync(path, 'utf8'));
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
return [];
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
export const slopnator = {
|
|
51
|
+
holds: (_node, at) => {
|
|
52
|
+
const path = here(at.filename);
|
|
53
|
+
for (const one of islandsSeeding()) {
|
|
54
|
+
if (path.startsWith(TREE)) {
|
|
55
|
+
if (!declares(one.island).test(at.text))
|
|
56
|
+
continue;
|
|
57
|
+
let wanted;
|
|
58
|
+
try {
|
|
59
|
+
wanted = seededNames(at.text);
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
return 'this file cannot be parsed, so the members it declares were never read and no file was measured against them';
|
|
63
|
+
}
|
|
64
|
+
const found = onDisk(one.folder);
|
|
65
|
+
for (const name of wanted) {
|
|
66
|
+
if (found.includes(name))
|
|
67
|
+
continue;
|
|
68
|
+
return `a row here declares ${one.member} \`${name.replace(/\.ts$/, '')}\` and ${one.folder}${name} is not on disk, so the row brings nothing into existence — run \`pnpm member:seed\``;
|
|
69
|
+
}
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
if (!path.startsWith(one.folder))
|
|
73
|
+
continue;
|
|
74
|
+
const name = path.slice(one.folder.length);
|
|
75
|
+
if (!name.endsWith('.ts') || name.includes('/'))
|
|
76
|
+
continue;
|
|
77
|
+
if (!declaredNames(one).includes(name)) {
|
|
78
|
+
return `${one.folder} holds ${one.member} \`${name.replace(/\.ts$/, '')}\` and no row in ${one.tree} declares it, so it can act while nothing brought it into existence — declare the row first and let \`pnpm member:seed\` write the file, or delete it`;
|
|
79
|
+
}
|
|
80
|
+
if (!exports(one.member).test(at.text)) {
|
|
81
|
+
return `${name} is declared and exports no \`${one.member}\`, so the row and the file both exist and the ${one.member} still loads as nothing — write it into the template the seeder left`;
|
|
82
|
+
}
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
85
|
+
return false;
|
|
86
|
+
},
|
|
87
|
+
proves: {
|
|
88
|
+
wrong: [
|
|
89
|
+
{
|
|
90
|
+
files: [
|
|
91
|
+
{
|
|
92
|
+
at: 'threads/_member_probe.ts',
|
|
93
|
+
source: "export const OPERATIONS: readonly Member[] = [\n {\n id: 'wf_unseeded_probe',\n at: 'threads/operations.ts',\n },\n]\n",
|
|
94
|
+
},
|
|
95
|
+
],
|
|
96
|
+
at: 'threads/_member_probe.ts',
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
files: [{ at: 'operations/wf_undeclared_probe.ts', source: 'export const operation = { id: 1 }\n' }],
|
|
100
|
+
at: 'operations/wf_undeclared_probe.ts',
|
|
101
|
+
},
|
|
102
|
+
],
|
|
103
|
+
right: [
|
|
104
|
+
{
|
|
105
|
+
files: [{ at: 'threads/_member_probe.ts', source: 'export const OPERATIONS: readonly Member[] = []\n' }],
|
|
106
|
+
at: 'threads/_member_probe.ts',
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
files: [{ at: 'threads/_member_quiet.ts', source: 'export const NOTHING = []\n' }],
|
|
110
|
+
at: 'threads/_member_quiet.ts',
|
|
111
|
+
},
|
|
112
|
+
],
|
|
113
|
+
},
|
|
114
|
+
};
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
2
|
+
import { join, resolve } from 'node:path';
|
|
3
|
+
import { SEEDED, SHIPPED, slopnatorName } from "../slopnator.js";
|
|
4
|
+
import { here } from "./sl_unthreaded.js";
|
|
5
|
+
import { findTree } from "../../core/tree.js";
|
|
6
|
+
const ROOT = findTree().at;
|
|
7
|
+
const TREE = `${findTree().name}/`;
|
|
8
|
+
const ID = /^ {4}id:\s*'([^']+)'/gm;
|
|
9
|
+
const DECLARES = /^(?:export )?const (CONSTRAINTS|RULES)\b/m;
|
|
10
|
+
const idsIn = (text) => [...text.matchAll(ID)].map((one) => one[1] ?? '');
|
|
11
|
+
export const slopnator = {
|
|
12
|
+
holds: (_node, at) => {
|
|
13
|
+
const path = here(at.filename);
|
|
14
|
+
if (path.startsWith(TREE) && DECLARES.test(at.text)) {
|
|
15
|
+
for (const id of idsIn(at.text)) {
|
|
16
|
+
const name = `${slopnatorName(id)}.ts`;
|
|
17
|
+
if ([SHIPPED, resolve(ROOT, SEEDED)].some((dir) => existsSync(join(dir, name))))
|
|
18
|
+
continue;
|
|
19
|
+
return `"${id}" names "${slopnatorName(id)}", and no such file is in ${SEEDED}/ or shipped with the package — one that cannot run registers, reports nothing, and is indistinguishable from one being obeyed. Seed it`;
|
|
20
|
+
}
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
const home = [SEEDED].find((dir) => path.startsWith(`${dir}/`));
|
|
24
|
+
if (home === undefined)
|
|
25
|
+
return false;
|
|
26
|
+
const file = path.slice(home.length + 1);
|
|
27
|
+
if (!file.startsWith('sl_') || !file.endsWith('.ts'))
|
|
28
|
+
return false;
|
|
29
|
+
const declared = [join(SHIPPED, '..', 'constraint.ts'), resolve(ROOT, `${TREE}rules.ts`)]
|
|
30
|
+
.map((one) => { try {
|
|
31
|
+
return readFileSync(one, 'utf8');
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
return '';
|
|
35
|
+
} })
|
|
36
|
+
.join('\n');
|
|
37
|
+
const named = new Set(idsIn(declared).map((id) => slopnatorName(id)));
|
|
38
|
+
if (named.has(file.replace(/\.ts$/, '')))
|
|
39
|
+
return false;
|
|
40
|
+
return `${path} is named by no law, so it governs nothing while looking like enforcement to anybody reading the directory — delete it, or declare the law that needs it. A law renamed makes this a \`git mv\`, not a second file`;
|
|
41
|
+
},
|
|
42
|
+
proves: {
|
|
43
|
+
wrong: [
|
|
44
|
+
"const CONSTRAINTS = [\n {\n id: 'nothing-names-this-slopnator',\n },\n]",
|
|
45
|
+
{
|
|
46
|
+
files: [{ at: 'threads/slopnators/sl_no_law_names_this.ts', source: "export const slopnator = { on: [], holds: () => false }\n" }],
|
|
47
|
+
at: 'threads/slopnators/sl_no_law_names_this.ts',
|
|
48
|
+
},
|
|
49
|
+
],
|
|
50
|
+
right: [
|
|
51
|
+
"const CONSTRAINTS = [\n {\n id: 'no-comment',\n },\n]",
|
|
52
|
+
"const CONSTRAINTS = [\n {\n id: 'no-comment',\n },\n {\n id: 'unthreaded',\n },\n]",
|
|
53
|
+
'export const PLAIN = 1',
|
|
54
|
+
{
|
|
55
|
+
files: [{ at: 'threads/slopnators/not_a_walk.ts', source: 'export const HELPER = 1\n' }],
|
|
56
|
+
at: 'threads/slopnators/not_a_walk.ts',
|
|
57
|
+
},
|
|
58
|
+
],
|
|
59
|
+
},
|
|
60
|
+
};
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { inCode, listBounds, matched } from "../../core/rows.js";
|
|
2
|
+
import { PREFIXES } from "../../core/thread.js";
|
|
3
|
+
import { here, parses, unreadable } from "./sl_unthreaded.js";
|
|
4
|
+
const MEMBERS = 'MEMBERS';
|
|
5
|
+
/*
|
|
6
|
+
* Which prefixes a tree file's member list may hold.
|
|
7
|
+
*
|
|
8
|
+
* The file is the key because the list is named the same in both: `MEMBERS` in the operations tree
|
|
9
|
+
* holds what an app can be asked to do, and `MEMBERS` in the nodes tree holds the spans an operation
|
|
10
|
+
* is built from. Nothing in TypeScript separates them — both are arrays of rows carrying a string id
|
|
11
|
+
* — so an operation declared among the nodes, or a node among the operations, typechecks, attaches,
|
|
12
|
+
* seeds a body and reads as agreed by everything downstream.
|
|
13
|
+
*
|
|
14
|
+
* What an operation may wear is not written here. `@thread/core/thread` pairs each word an
|
|
15
|
+
* operation can be with the prefix its id takes, and `thread create` puts that prefix on — so the
|
|
16
|
+
* command that makes them and the law that refuses them read one list. Written out again here,
|
|
17
|
+
* a kind added over there would be a kind this refuses.
|
|
18
|
+
*/
|
|
19
|
+
const TAKES = {
|
|
20
|
+
'operations.ts': Object.values(PREFIXES),
|
|
21
|
+
'nodes.ts': ['nd_', 'sm_'],
|
|
22
|
+
};
|
|
23
|
+
const ID = /\bid:\s*'((?:[^'\\]|\\.)*)'/;
|
|
24
|
+
/*
|
|
25
|
+
* The id of every row sitting directly in the list, and no deeper.
|
|
26
|
+
*
|
|
27
|
+
* Depth is the whole of it: a member row states its own id, and the `seeds` and `derives` rows under
|
|
28
|
+
* it state theirs, which are neither operations nor nodes — `wf_send_invoice_body` is a body, not
|
|
29
|
+
* something an app can be asked to do. Reading every id in the list would refuse those children for
|
|
30
|
+
* lacking a prefix they were never meant to wear.
|
|
31
|
+
*
|
|
32
|
+
* The braces are read off `inCode`, not off the raw text. Counting characters directly desynced on a
|
|
33
|
+
* `describes:` holding a brace, and this package already ships the mask and the matcher for exactly
|
|
34
|
+
* that — a second, weaker walker beside them is the drift they exist to prevent.
|
|
35
|
+
*/
|
|
36
|
+
function directly(text, open, close) {
|
|
37
|
+
const code = inCode(text);
|
|
38
|
+
const found = [];
|
|
39
|
+
for (let at = open; at < close; at += 1) {
|
|
40
|
+
if (code[at] !== true || text[at] !== '{')
|
|
41
|
+
continue;
|
|
42
|
+
const ends = matched(text, code, at, '{', '}');
|
|
43
|
+
if (ends === -1 || ends > close)
|
|
44
|
+
break;
|
|
45
|
+
const said = ID.exec(text.slice(at, ends))?.[1];
|
|
46
|
+
if (said !== undefined)
|
|
47
|
+
found.push(said);
|
|
48
|
+
at = ends;
|
|
49
|
+
}
|
|
50
|
+
return found;
|
|
51
|
+
}
|
|
52
|
+
export const slopnator = {
|
|
53
|
+
holds: (_node, at) => {
|
|
54
|
+
const path = here(at.filename);
|
|
55
|
+
if (unreadable(path, at.text) !== undefined)
|
|
56
|
+
return false;
|
|
57
|
+
if (!parses(path))
|
|
58
|
+
return false;
|
|
59
|
+
const takes = TAKES[path.slice(path.lastIndexOf('/') + 1)];
|
|
60
|
+
if (takes === undefined)
|
|
61
|
+
return false;
|
|
62
|
+
const text = String(at.text);
|
|
63
|
+
const where = listBounds(text, MEMBERS);
|
|
64
|
+
if (where === undefined)
|
|
65
|
+
return false;
|
|
66
|
+
for (const said of directly(text, where.open, where.close)) {
|
|
67
|
+
if (takes.some((one) => said.startsWith(one)))
|
|
68
|
+
continue;
|
|
69
|
+
return (`\`${said}\` sits in \`${MEMBERS}\` here, and this list takes ${takes.map((one) => `\`${one}\``).join(', ')}. ` +
|
|
70
|
+
'A prefix is what says which of the two a row is, and a row wearing the wrong one is declared as ' +
|
|
71
|
+
'something it is not, seeds a body of the wrong shape, and is read as agreed by everything downstream');
|
|
72
|
+
}
|
|
73
|
+
return false;
|
|
74
|
+
},
|
|
75
|
+
proves: {
|
|
76
|
+
wrong: [
|
|
77
|
+
{
|
|
78
|
+
files: [
|
|
79
|
+
{
|
|
80
|
+
at: '_threads/operations.ts',
|
|
81
|
+
source: "import type { Member, Threaded } from '../../core/thread.ts'\n\nexport const OPERATIONS: Threaded = {\n id: 'operations',\n at: '_threads/operations.ts',\n describes: 'what an app can be asked to do',\n version: 1,\n}\n\nexport const MEMBERS: readonly Member[] = [\n {\n id: 'nd_teleport',\n at: '_threads/operations.ts',\n describes: 'a node wearing an operation list',\n version: 1,\n },\n]\n",
|
|
82
|
+
},
|
|
83
|
+
],
|
|
84
|
+
at: '_threads/operations.ts',
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
files: [
|
|
88
|
+
{
|
|
89
|
+
at: '_threads/nodes.ts',
|
|
90
|
+
source: "import type { Member, Threaded } from '../../core/thread.ts'\n\nexport const NODES: Threaded = {\n id: 'nodes',\n at: '_threads/nodes.ts',\n describes: 'the spans an operation is built from',\n version: 1,\n}\n\nexport const MEMBERS: readonly Member[] = [\n {\n id: 'wf_send_invoice',\n at: '_threads/nodes.ts',\n describes: 'an operation wearing a node list',\n version: 1,\n },\n]\n",
|
|
91
|
+
},
|
|
92
|
+
],
|
|
93
|
+
at: '_threads/nodes.ts',
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
files: [
|
|
97
|
+
{
|
|
98
|
+
at: '_threads/operations.ts',
|
|
99
|
+
source: "import type { Member, Threaded } from '../../core/thread.ts'\n\nexport const OPERATIONS: Threaded = {\n id: 'operations',\n at: '_threads/operations.ts',\n describes: 'what an app can be asked to do',\n version: 1,\n}\n\nexport const MEMBERS: readonly Member[] = [\n {\n id: 'sendInvoice',\n at: '_threads/operations.ts',\n describes: 'no prefix at all',\n version: 1,\n },\n]\n",
|
|
100
|
+
},
|
|
101
|
+
],
|
|
102
|
+
at: '_threads/operations.ts',
|
|
103
|
+
},
|
|
104
|
+
],
|
|
105
|
+
right: [
|
|
106
|
+
{
|
|
107
|
+
files: [
|
|
108
|
+
{
|
|
109
|
+
at: '_threads/operations.ts',
|
|
110
|
+
source: "import type { Member, Threaded } from '../../core/thread.ts'\n\nexport const OPERATIONS: Threaded = {\n id: 'operations',\n at: '_threads/operations.ts',\n describes: 'what an app can be asked to do',\n version: 1,\n}\n\nexport const MEMBERS: readonly Member[] = [\n {\n id: 'wf_send_invoice',\n at: '_threads/operations.ts',\n describes: 'sends an invoice',\n version: 1,\n seeds: [\n {\n id: 'wf_send_invoice_body',\n at: 'operations/wf_send_invoice.ts',\n describes: 'the operation itself',\n },\n ],\n },\n]\n",
|
|
111
|
+
},
|
|
112
|
+
],
|
|
113
|
+
at: '_threads/operations.ts',
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
files: [
|
|
117
|
+
{
|
|
118
|
+
at: '_threads/nodes.ts',
|
|
119
|
+
source: "import type { Member, Threaded } from '../../core/thread.ts'\n\nexport const NODES: Threaded = {\n id: 'nodes',\n at: '_threads/nodes.ts',\n describes: 'the spans an operation is built from',\n version: 1,\n}\n\nexport const MEMBERS: readonly Member[] = [\n {\n id: 'nd_teleport',\n at: '_threads/nodes.ts',\n describes: 'a step this app added',\n version: 1,\n },\n]\n",
|
|
120
|
+
},
|
|
121
|
+
],
|
|
122
|
+
at: '_threads/nodes.ts',
|
|
123
|
+
},
|
|
124
|
+
],
|
|
125
|
+
},
|
|
126
|
+
};
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { isBuiltin } from 'node:module';
|
|
2
|
+
import { parser } from 'typescript-eslint';
|
|
3
|
+
const CODE = /\.(?:ts|tsx|mts|cts|js|jsx|mjs|cjs)$/;
|
|
4
|
+
const MENTIONS = /\bimport\b|\brequire\s*\(/;
|
|
5
|
+
function reached(node, seen, found) {
|
|
6
|
+
if (typeof node !== 'object' || node === null)
|
|
7
|
+
return;
|
|
8
|
+
if (seen.has(node))
|
|
9
|
+
return;
|
|
10
|
+
seen.add(node);
|
|
11
|
+
if (Array.isArray(node)) {
|
|
12
|
+
for (const each of node)
|
|
13
|
+
reached(each, seen, found);
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
const one = node;
|
|
17
|
+
const type = one['type'];
|
|
18
|
+
if (type === 'ImportDeclaration' || type === 'ImportExpression' || type === 'ExportNamedDeclaration' || type === 'ExportAllDeclaration') {
|
|
19
|
+
const source = one['source'];
|
|
20
|
+
if (typeof source?.value === 'string')
|
|
21
|
+
found(source.value);
|
|
22
|
+
}
|
|
23
|
+
if (type === 'CallExpression') {
|
|
24
|
+
const callee = one['callee'];
|
|
25
|
+
if (callee?.type === 'Identifier' && callee.name === 'require') {
|
|
26
|
+
const first = one['arguments']?.[0];
|
|
27
|
+
if (typeof first?.value === 'string')
|
|
28
|
+
found(first.value);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
for (const held of Object.values(one))
|
|
32
|
+
reached(held, seen, found);
|
|
33
|
+
}
|
|
34
|
+
export const slopnator = {
|
|
35
|
+
holds: (_node, at) => {
|
|
36
|
+
if (!CODE.test(at.filename))
|
|
37
|
+
return false;
|
|
38
|
+
const text = String(at.text);
|
|
39
|
+
if (!MENTIONS.test(text))
|
|
40
|
+
return false;
|
|
41
|
+
let ast;
|
|
42
|
+
try {
|
|
43
|
+
ast = parser.parseForESLint(text, {
|
|
44
|
+
range: true,
|
|
45
|
+
ecmaVersion: 'latest',
|
|
46
|
+
sourceType: 'module',
|
|
47
|
+
ecmaFeatures: { jsx: true },
|
|
48
|
+
}).ast;
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
const taken = new Set();
|
|
54
|
+
reached(ast, new WeakSet(), (one) => {
|
|
55
|
+
if (isBuiltin(one))
|
|
56
|
+
taken.add(one);
|
|
57
|
+
});
|
|
58
|
+
if (taken.size === 0)
|
|
59
|
+
return false;
|
|
60
|
+
return `this reaches ${[...taken].sort().join(', ')} straight from the runtime, and a node is handed everything it may touch`;
|
|
61
|
+
},
|
|
62
|
+
proves: {
|
|
63
|
+
wrong: [
|
|
64
|
+
"import { readFileSync } from 'node:fs'\nexport const node = readFileSync\n",
|
|
65
|
+
"const { spawn } = require('child_process')\nexport const node = spawn\n",
|
|
66
|
+
"export const node = async () => await import('node:net')\n",
|
|
67
|
+
],
|
|
68
|
+
right: [
|
|
69
|
+
"import { nd_read } from '../../operations/nodes.ts'\nexport const node = nd_read(async (hands) => await hands.store.get('a', 'b'))\n",
|
|
70
|
+
"import type { In } from './input.ts'\nexport const node = null as unknown as In\n",
|
|
71
|
+
],
|
|
72
|
+
},
|
|
73
|
+
};
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import type { Slopnator } from '../slopnator.ts';
|
|
2
|
+
export interface Reach {
|
|
3
|
+
readonly list?: 'declares' | 'derives' | 'seeds';
|
|
4
|
+
readonly at?: string;
|
|
5
|
+
readonly id?: string;
|
|
6
|
+
readonly describes?: string;
|
|
7
|
+
readonly version?: number;
|
|
8
|
+
readonly governs?: readonly string[];
|
|
9
|
+
readonly wears?: readonly string[];
|
|
10
|
+
}
|
|
11
|
+
export interface Meaning {
|
|
12
|
+
readonly id: string;
|
|
13
|
+
readonly describes: string;
|
|
14
|
+
}
|
|
15
|
+
export declare const here: (filename: string) => string;
|
|
16
|
+
export declare function atsIn(text: string, only?: ReadonlySet<string>): readonly string[];
|
|
17
|
+
export declare const rowsIn: (text: string) => readonly Reach[];
|
|
18
|
+
export type Governed = {
|
|
19
|
+
readonly kind: 'file';
|
|
20
|
+
readonly at: string;
|
|
21
|
+
} | {
|
|
22
|
+
readonly kind: 'folder';
|
|
23
|
+
readonly under: string;
|
|
24
|
+
} | {
|
|
25
|
+
readonly kind: 'pattern';
|
|
26
|
+
readonly matching: string;
|
|
27
|
+
readonly under: string;
|
|
28
|
+
};
|
|
29
|
+
export declare function governedBy(one: string): Governed;
|
|
30
|
+
export declare const filesGoverned: (all: readonly string[]) => readonly string[];
|
|
31
|
+
export declare const foldersGoverned: (all: readonly string[]) => readonly string[];
|
|
32
|
+
export declare function governs(all: readonly string[], path: string): boolean;
|
|
33
|
+
export interface Declared {
|
|
34
|
+
readonly id: string;
|
|
35
|
+
readonly at: string;
|
|
36
|
+
readonly describes: string;
|
|
37
|
+
readonly governs: readonly string[];
|
|
38
|
+
readonly define?: string;
|
|
39
|
+
readonly tree?: string;
|
|
40
|
+
readonly member?: string;
|
|
41
|
+
}
|
|
42
|
+
export declare function islandsDeclared(): readonly Declared[];
|
|
43
|
+
export declare const declaresIsland: (text: string) => string | undefined;
|
|
44
|
+
export declare function meaningsIn(text: string): readonly Meaning[];
|
|
45
|
+
export interface Index {
|
|
46
|
+
readonly reached: ReadonlySet<string>;
|
|
47
|
+
readonly declared: ReadonlyMap<string, number>;
|
|
48
|
+
readonly meanings: ReadonlyMap<string, ReadonlySet<string>>;
|
|
49
|
+
}
|
|
50
|
+
export declare function index(): Index;
|
|
51
|
+
export declare function forget(): void;
|
|
52
|
+
export declare const threaded: () => ReadonlySet<string>;
|
|
53
|
+
export declare const parses: (path: string) => boolean;
|
|
54
|
+
export declare function unreadable(path: string, text: string): string | undefined;
|
|
55
|
+
export declare const names: (path: string, text: string) => readonly string[];
|
|
56
|
+
export declare const slopnator: Slopnator;
|