@prestack/thread 0.0.3 → 0.1.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 +310 -0
- package/dist/core/{asking.d.ts → answers.d.ts} +6 -14
- package/dist/core/{asking.js → answers.js} +13 -332
- package/dist/core/{asking.ts → answers.ts} +14 -357
- package/dist/core/attach.js +2 -2
- package/dist/core/attach.ts +2 -3
- package/dist/core/chain.js +4 -11
- package/dist/core/chain.ts +4 -3
- package/dist/core/client.d.ts +57 -0
- package/dist/core/client.js +243 -0
- package/dist/core/client.ts +279 -0
- package/dist/core/descend.d.ts +2 -0
- package/dist/core/descend.js +109 -16
- package/dist/core/descend.ts +120 -16
- package/dist/core/files.d.ts +45 -0
- package/dist/core/files.js +145 -0
- package/dist/core/files.ts +193 -0
- package/dist/core/init.d.ts +1 -0
- package/dist/core/init.js +10 -2
- package/dist/core/init.ts +8 -2
- package/dist/core/invoked.js +1 -1
- package/dist/core/invoked.ts +1 -1
- package/dist/core/rows.d.ts +2 -0
- package/dist/core/rows.js +65 -0
- package/dist/core/rows.ts +58 -0
- package/dist/core/tree.js +1 -1
- package/dist/core/tree.ts +1 -1
- package/dist/derivation/attach.js +0 -12
- package/dist/derivation/attach.ts +0 -12
- package/dist/derivation/generators/build.js +8 -7
- package/dist/derivation/generators/build.ts +8 -7
- package/dist/derivation/generators/release.d.ts +1 -1
- package/dist/derivation/generators/release.js +15 -23
- package/dist/derivation/generators/release.ts +15 -15
- package/dist/integrations/attach.js +1 -19
- package/dist/integrations/attach.ts +1 -19
- package/dist/integrations/generators/read.js +2 -2
- package/dist/integrations/generators/read.ts +4 -4
- package/dist/integrations/generators/vault.js +4 -12
- package/dist/integrations/generators/vault.ts +4 -4
- package/dist/integrations/module.js +5 -3
- package/dist/integrations/module.ts +5 -3
- package/dist/integrations/vault.js +5 -5
- package/dist/integrations/vault.ts +5 -5
- package/dist/integrity/attach.js +0 -12
- package/dist/integrity/attach.ts +0 -12
- package/dist/integrity/check-threads.js +1 -1
- package/dist/integrity/check-threads.ts +1 -1
- package/dist/integrity/constraint.d.ts +11 -2
- package/dist/integrity/constraint.js +163 -20
- package/dist/integrity/constraint.ts +174 -22
- package/dist/integrity/generators/compiler.js +7 -1
- package/dist/integrity/generators/compiler.ts +10 -1
- package/dist/integrity/generators/gate.js +23 -18
- package/dist/integrity/generators/gate.ts +29 -9
- package/dist/integrity/generators/publish.js +14 -26
- package/dist/integrity/generators/publish.ts +14 -17
- package/dist/integrity/generators/trial.d.ts +2 -2
- package/dist/integrity/generators/trial.js +108 -50
- package/dist/integrity/generators/trial.ts +107 -48
- package/dist/integrity/lint.js +3 -3
- package/dist/integrity/lint.ts +3 -3
- package/dist/integrity/lsp/diagnostics.js +1 -1
- package/dist/integrity/lsp/diagnostics.ts +1 -1
- package/dist/integrity/lsp/lint.js +1 -1
- package/dist/integrity/lsp/lint.ts +1 -1
- package/dist/integrity/slopnator.d.ts +1 -14
- package/dist/integrity/slopnator.js +8 -40
- package/dist/integrity/slopnator.ts +9 -51
- package/dist/integrity/slopnators/sl_broken_lineage.js +4 -2
- package/dist/integrity/slopnators/sl_broken_lineage.ts +4 -2
- package/dist/integrity/slopnators/sl_declared_elsewhere.js +24 -3
- package/dist/integrity/slopnators/sl_declared_elsewhere.ts +24 -3
- package/dist/integrity/slopnators/sl_derived_drift.js +10 -4
- package/dist/integrity/slopnators/sl_derived_drift.ts +10 -4
- package/dist/integrity/slopnators/sl_inert_island.js +7 -13
- package/dist/integrity/slopnators/sl_inert_island.ts +7 -16
- package/dist/integrity/slopnators/sl_inert_slopnator.js +8 -8
- package/dist/integrity/slopnators/sl_inert_slopnator.ts +8 -8
- package/dist/integrity/slopnators/sl_missing_row.js +14 -9
- package/dist/integrity/slopnators/sl_missing_row.ts +14 -10
- package/dist/integrity/slopnators/sl_no_child_process.js +8 -8
- package/dist/integrity/slopnators/sl_no_child_process.ts +8 -8
- package/dist/integrity/slopnators/sl_no_import_cycle.js +1 -1
- package/dist/integrity/slopnators/sl_no_import_cycle.ts +1 -1
- package/dist/integrity/slopnators/sl_no_such_command.js +20 -5
- package/dist/integrity/slopnators/sl_no_such_command.ts +22 -5
- package/dist/integrity/slopnators/sl_only_takes_a_literal.js +8 -8
- package/dist/integrity/slopnators/sl_only_takes_a_literal.ts +8 -8
- package/dist/integrity/slopnators/sl_stale_path.js +91 -18
- package/dist/integrity/slopnators/sl_stale_path.ts +97 -18
- package/dist/integrity/slopnators/sl_twice_meant.js +25 -2
- package/dist/integrity/slopnators/sl_twice_meant.ts +28 -2
- package/dist/integrity/slopnators/sl_twice_parented.js +26 -5
- package/dist/integrity/slopnators/sl_twice_parented.ts +26 -5
- package/dist/integrity/slopnators/sl_undeclared_node.js +18 -2
- package/dist/integrity/slopnators/sl_undeclared_node.ts +18 -2
- package/dist/integrity/slopnators/sl_undeclared_operation.js +3 -1
- package/dist/integrity/slopnators/sl_undeclared_operation.ts +3 -1
- package/dist/integrity/slopnators/sl_undeclared_variant.js +6 -2
- package/dist/integrity/slopnators/sl_undeclared_variant.ts +6 -2
- package/dist/integrity/slopnators/sl_ungoverned_island.js +3 -4
- package/dist/integrity/slopnators/sl_ungoverned_island.ts +3 -4
- package/dist/integrity/slopnators/sl_unkept_rows.js +1 -1
- package/dist/integrity/slopnators/sl_unkept_rows.ts +1 -1
- package/dist/integrity/slopnators/sl_unpaired_member.js +13 -8
- package/dist/integrity/slopnators/sl_unpaired_member.ts +16 -8
- package/dist/integrity/slopnators/sl_unpaired_slopnator.js +16 -3
- package/dist/integrity/slopnators/sl_unpaired_slopnator.ts +16 -3
- package/dist/integrity/slopnators/sl_unthreaded.d.ts +1 -0
- package/dist/integrity/slopnators/sl_unthreaded.js +18 -10
- package/dist/integrity/slopnators/sl_unthreaded.ts +19 -11
- package/dist/integrity/slopnators/sl_untranslated_key.js +1 -1
- package/dist/integrity/slopnators/sl_untranslated_key.ts +1 -1
- package/dist/integrity/slopnators/sl_unversioned.js +16 -2
- package/dist/integrity/slopnators/sl_unversioned.ts +16 -2
- package/dist/integrity/sweep.js +17 -3
- package/dist/integrity/sweep.ts +20 -3
- package/dist/knowledge/attach.js +0 -6
- package/dist/knowledge/attach.ts +0 -6
- package/dist/knowledge/generators/docsgen.js +12 -3
- package/dist/knowledge/generators/docsgen.ts +12 -2
- package/dist/knowledge/generators/glossary.js +1 -1
- package/dist/knowledge/generators/glossary.ts +1 -1
- package/dist/operations/attach.js +2 -26
- package/dist/operations/attach.ts +2 -27
- package/dist/operations/dispatch.js +2 -3
- package/dist/operations/dispatch.ts +2 -3
- package/dist/operations/generators/capability.js +4 -7
- package/dist/operations/generators/capability.ts +3 -6
- package/dist/operations/generators/compile.js +8 -13
- package/dist/operations/generators/compile.ts +8 -4
- package/dist/operations/generators/permits.js +4 -13
- package/dist/operations/generators/permits.ts +3 -3
- package/dist/operations/generators/seed.js +5 -2
- package/dist/operations/generators/seed.ts +6 -2
- package/dist/operations/generators/trial.js +10 -10
- package/dist/operations/generators/trial.ts +10 -10
- package/dist/operations/http.js +4 -1
- package/dist/operations/http.ts +6 -1
- package/package.json +2 -2
- package/dist/core/cli.d.ts +0 -2
- package/dist/core/cli.js +0 -1761
- package/dist/core/cli.ts +0 -2004
- package/dist/core/generators/meta.d.ts +0 -2
- package/dist/core/generators/meta.js +0 -829
- package/dist/core/generators/meta.ts +0 -1006
- package/dist/derivation/generators/derivation.d.ts +0 -3
- package/dist/derivation/generators/derivation.js +0 -273
- package/dist/derivation/generators/derivation.ts +0 -347
- package/dist/derivation/makers/release.d.ts +0 -1
- package/dist/derivation/makers/release.js +0 -107
- package/dist/derivation/makers/release.ts +0 -135
- package/dist/integrations/generators/secret.d.ts +0 -1
- package/dist/integrations/generators/secret.js +0 -233
- package/dist/integrations/generators/secret.ts +0 -289
- package/dist/integrations/generators/target.d.ts +0 -1
- package/dist/integrations/generators/target.js +0 -775
- package/dist/integrations/generators/target.ts +0 -944
- package/dist/integrity/generators/rule.d.ts +0 -2
- package/dist/integrity/generators/rule.js +0 -213
- package/dist/integrity/generators/rule.ts +0 -271
- package/dist/integrity/generators/seal.d.ts +0 -4
- package/dist/integrity/generators/seal.js +0 -101
- package/dist/integrity/generators/seal.ts +0 -117
- package/dist/integrity/sealed.d.ts +0 -1
- package/dist/integrity/sealed.js +0 -56
- package/dist/integrity/sealed.ts +0 -56
- package/dist/knowledge/generators/skills.d.ts +0 -1
- package/dist/knowledge/generators/skills.js +0 -222
- package/dist/knowledge/generators/skills.ts +0 -263
- package/dist/operations/generators/grant.d.ts +0 -1
- package/dist/operations/generators/grant.js +0 -267
- package/dist/operations/generators/grant.ts +0 -336
- package/dist/operations/generators/operation.d.ts +0 -1
- package/dist/operations/generators/operation.js +0 -811
- package/dist/operations/generators/operation.ts +0 -972
- package/dist/operations/makers/capability.d.ts +0 -1
- package/dist/operations/makers/capability.js +0 -117
- package/dist/operations/makers/capability.ts +0 -166
- package/dist/operations/makers/trial.d.ts +0 -1
- package/dist/operations/makers/trial.js +0 -413
- package/dist/operations/makers/trial.ts +0 -520
|
@@ -1,811 +0,0 @@
|
|
|
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 { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
|
|
11
|
-
import { dirname, join, resolve } from 'node:path';
|
|
12
|
-
import { argv, exit, stderr, stdout } from 'node:process';
|
|
13
|
-
import { pathToFileURL } from 'node:url';
|
|
14
|
-
import { ENDS, RECEIPT, accepted, answer, asking, given as pipedIn, guidance, issue, marked, named as answerNames, needed, only, prompted, refuse, scoped, slug, spend, within, wrapped, wordsIn, } from "../../core/asking.js";
|
|
15
|
-
import { generators, offers as offered, settling } from "../../core/chain.js";
|
|
16
|
-
import { descend, under } from "../../core/descend.js";
|
|
17
|
-
import { afterRow, holdsRow, listBounds, seedsIn, withRequires, withRow, withoutRow } from "../../core/rows.js";
|
|
18
|
-
import { PREFIXES } from "../../core/thread.js";
|
|
19
|
-
import { named as packageOf } from "../../core/scope.js";
|
|
20
|
-
import { findTree, inTree } from "../../core/tree.js";
|
|
21
|
-
import { PUBLIC_ROLE } from "../../core/actor.js";
|
|
22
|
-
import { declaredWords, undeclared } from "../access.js";
|
|
23
|
-
import { TAKES, WOVEN, said } from "../attach.js";
|
|
24
|
-
import { ROUTE, routeAt } from "../capabilities.js";
|
|
25
|
-
import { MEMBERS_LIST, OPERATIONS_FILE } from "../operations.js";
|
|
26
|
-
import { SUPPLY, supplies } from "../seams.js";
|
|
27
|
-
import { nodeText, scaffold } from "./seed.js";
|
|
28
|
-
const TREE = findTree();
|
|
29
|
-
const OPERATIONS_ID = 'operations';
|
|
30
|
-
const NODES_ID = 'nodes';
|
|
31
|
-
const UNDER = 'operations';
|
|
32
|
-
const VERSION = 1;
|
|
33
|
-
const IDENTIFIER = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
34
|
-
const here = (path) => within(TREE.at, path);
|
|
35
|
-
const isType = (pass, id) => under(pass, id, NODES_ID);
|
|
36
|
-
function types(pass) {
|
|
37
|
-
const found = new Set();
|
|
38
|
-
const named = new Set();
|
|
39
|
-
for (const threadings of pass.threaded.values()) {
|
|
40
|
-
for (const one of threadings)
|
|
41
|
-
if (one.row.id !== undefined)
|
|
42
|
-
named.add(one.row.id);
|
|
43
|
-
}
|
|
44
|
-
for (const one of pass.coined.keys()) {
|
|
45
|
-
if (one !== NODES_ID && !supplies(one) && !named.has(one) && under(pass, one, NODES_ID))
|
|
46
|
-
found.add(one);
|
|
47
|
-
}
|
|
48
|
-
return [...found].sort();
|
|
49
|
-
}
|
|
50
|
-
function capabilities(pass) {
|
|
51
|
-
const found = new Set();
|
|
52
|
-
for (const one of pass.coined.keys())
|
|
53
|
-
if (supplies(one) && under(pass, one, NODES_ID))
|
|
54
|
-
found.add(one);
|
|
55
|
-
return [...found].sort();
|
|
56
|
-
}
|
|
57
|
-
function existing(pass) {
|
|
58
|
-
const bodies = new Set();
|
|
59
|
-
const nodes = new Map();
|
|
60
|
-
for (const threadings of pass.threaded.values()) {
|
|
61
|
-
for (const one of threadings) {
|
|
62
|
-
if (!under(pass, one.by, OPERATIONS_ID) || isType(pass, one.by))
|
|
63
|
-
continue;
|
|
64
|
-
if (one.list === 'derives' && one.row.by === 'thread compile')
|
|
65
|
-
bodies.add(one.by);
|
|
66
|
-
if (one.list === 'seeds' && (one.row.requires ?? []).length > 0) {
|
|
67
|
-
nodes.set(one.by, [...(nodes.get(one.by) ?? []), one.row]);
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
return [...bodies].sort().map((id) => ({
|
|
72
|
-
id,
|
|
73
|
-
describes: pass.coined.get(id)?.said[0] ?? '',
|
|
74
|
-
requires: (pass.stated.get(id)?.requires ?? []).filter((one) => handed.includes(one)),
|
|
75
|
-
nodes: nodes.get(id) ?? [],
|
|
76
|
-
}));
|
|
77
|
-
}
|
|
78
|
-
const pass = descend();
|
|
79
|
-
const known = types(pass);
|
|
80
|
-
if (known.length === 0) {
|
|
81
|
-
refuse(`this app declares no node types, and a node is built from one. Attach ${packageOf('operations')} first.`);
|
|
82
|
-
}
|
|
83
|
-
const reachable = await (async () => {
|
|
84
|
-
const path = inTree(TREE, 'targets.ts');
|
|
85
|
-
if (!existsSync(path))
|
|
86
|
-
return [];
|
|
87
|
-
try {
|
|
88
|
-
const taken = (await import(__rewriteRelativeImportExtension(pathToFileURL(path).href)));
|
|
89
|
-
const calls = new Set((taken.CALLS ?? []).map((one) => one.id));
|
|
90
|
-
return (taken.TARGETS ?? [])
|
|
91
|
-
.filter((one) => one.takes.some((each) => calls.has(each)))
|
|
92
|
-
.map((one) => one.id)
|
|
93
|
-
.sort();
|
|
94
|
-
}
|
|
95
|
-
catch {
|
|
96
|
-
return [];
|
|
97
|
-
}
|
|
98
|
-
})();
|
|
99
|
-
const declared = await generators(TREE);
|
|
100
|
-
const offers = offered(declared);
|
|
101
|
-
const sentence = (one) => pass.coined.get(one)?.said[0] ?? pass.stated.get(one)?.describes ?? '';
|
|
102
|
-
const handed = [...capabilities(pass), ...reachable];
|
|
103
|
-
const already = existing(pass);
|
|
104
|
-
const at = here(inTree(TREE, OPERATIONS_FILE));
|
|
105
|
-
const mark = (one) => marked([
|
|
106
|
-
one.id,
|
|
107
|
-
one.describes,
|
|
108
|
-
one.requires.join(','),
|
|
109
|
-
...one.nodes.map((row) => `${String(row.id)}:${(row.requires ?? []).join(',')}:${String(row.describes)}`),
|
|
110
|
-
]);
|
|
111
|
-
const listing = already.length === 0
|
|
112
|
-
? 'no operations yet.'
|
|
113
|
-
: [
|
|
114
|
-
`${String(already.length)} operation(s):`,
|
|
115
|
-
...already.flatMap((one) => [
|
|
116
|
-
` [${mark(one)}] ${one.id} — ${String(one.nodes.length)} node(s) — requires ${one.requires.length === 0 ? 'nothing' : one.requires.join(', ')} — ${one.describes}`,
|
|
117
|
-
...one.nodes.map((row, order) => ` ${String(order + 1)}. ${String(row.id)} — ${(row.requires ?? []).join(', ')} — ${String(row.describes)}`),
|
|
118
|
-
]),
|
|
119
|
-
].join('\n');
|
|
120
|
-
const MINE = 'operation';
|
|
121
|
-
const NAME = {
|
|
122
|
-
at: 'name',
|
|
123
|
-
guide: 'the word it is called by — the prefix is put on for you, and <name> on the command line skips this',
|
|
124
|
-
asks: 'name: ',
|
|
125
|
-
};
|
|
126
|
-
const TYPE = { at: 'type', guide: `one of ${Object.keys(PREFIXES).join(', ')} — the prefix on the id says which` };
|
|
127
|
-
const DOES = { at: 'does', guide: 'what it does, in one sentence — a row without one is refused', asks: 'what does it do? ' };
|
|
128
|
-
const REQUIRES = {
|
|
129
|
-
at: 'requires',
|
|
130
|
-
guide: 'what it needs, comma separated — the words under "what an operation may require" below. Blank for nothing',
|
|
131
|
-
asks: ' what does it need (comma separated, blank for nothing): ',
|
|
132
|
-
};
|
|
133
|
-
const TO = {
|
|
134
|
-
at: 'to',
|
|
135
|
-
guide: 'who may run it — roles, comma separated. Blank writes no grant, and then nobody may run it and its trial ' +
|
|
136
|
-
'will not reach it',
|
|
137
|
-
};
|
|
138
|
-
const NODES = { at: 'nodes', guide: 'how many nodes to ask for — <nodes> on the command line skips this', asks: 'how many nodes: ' };
|
|
139
|
-
const INPUT = { at: 'input', guide: 'piped only — the JSON its first scenario is tried with, `{}` when left off' };
|
|
140
|
-
const ANSWERS = { at: 'answers', guide: 'piped only — what the seams answer, per call' };
|
|
141
|
-
const NEW = [NAME, TYPE, DOES, REQUIRES, TO, NODES, INPUT, ANSWERS];
|
|
142
|
-
const NODE_NAME = { at: 'node', guide: 'its name — blank here ends it' };
|
|
143
|
-
const NODE_DOES = { at: 'does', guide: 'what it does, in one sentence', asks: ' what does it do? ' };
|
|
144
|
-
const NODE_TYPE = { at: 'type', guide: `one of ${known.join(', ')}`, asks: ` which type — ${known.join(', ')}: ` };
|
|
145
|
-
const NODE_BODY = {
|
|
146
|
-
at: 'body',
|
|
147
|
-
guide: `the block, then a line holding only ${ENDS} — a first line of ${ENDS} leaves it unwritten, and it throws when it runs`,
|
|
148
|
-
};
|
|
149
|
-
const AFTER = { at: 'after', guide: 'where it goes — 0 to run first, or the node it follows. Blank for last' };
|
|
150
|
-
const NODE_NEW = [NODE_NAME, NODE_DOES, NODE_TYPE, NODE_BODY];
|
|
151
|
-
const ADDS = [AFTER];
|
|
152
|
-
const NODE = [...NODE_NEW, ...ADDS];
|
|
153
|
-
const MARK = {
|
|
154
|
-
at: 'mark',
|
|
155
|
-
guide: 'which operation — its mark from the list above. <mark> on the command line skips this',
|
|
156
|
-
asks: 'which operation — its mark from the list above: ',
|
|
157
|
-
};
|
|
158
|
-
const HOW = { at: 'how', guide: 'add, write or needs', asks: 'add a node, write one, or change what it needs — add, write or needs: ' };
|
|
159
|
-
const WHICH = { at: 'which', guide: 'which node — 1 to N' };
|
|
160
|
-
const OPENS = [MARK, HOW];
|
|
161
|
-
const WRITES = [WHICH, NODE_BODY];
|
|
162
|
-
const EDIT = [...OPENS, REQUIRES, ...WRITES];
|
|
163
|
-
const ASKS = {
|
|
164
|
-
new: ['do', ...answerNames(NEW)],
|
|
165
|
-
edit: ['do', ...answerNames(EDIT)],
|
|
166
|
-
};
|
|
167
|
-
const NODE_ASKS = answerNames(NODE);
|
|
168
|
-
const words = wordsIn(['new', 'edit', 'read', 'remove']);
|
|
169
|
-
const verb = words[0];
|
|
170
|
-
if (verb === 'read') {
|
|
171
|
-
const which = words[1];
|
|
172
|
-
const one = already.find((each) => mark(each) === which);
|
|
173
|
-
if (one === undefined) {
|
|
174
|
-
refuse(`\`${which ?? '(nothing)'}\` is not a mark this tree issued. Run \`thread create ${MINE}\` to see them — each is printed in brackets beside the operation it belongs to.`);
|
|
175
|
-
}
|
|
176
|
-
stdout.write(`${one.id} — ${one.describes}\n`);
|
|
177
|
-
stdout.write(`requires ${one.requires.length === 0 ? 'nothing' : one.requires.join(', ')}\n`);
|
|
178
|
-
for (const [order, row] of one.nodes.entries()) {
|
|
179
|
-
stdout.write(`\n ${String(order + 1)}. ${String(row.id)} — ${(row.requires ?? []).join(', ')}\n`);
|
|
180
|
-
stdout.write(` ${String(row.describes)}\n`);
|
|
181
|
-
if (row.at === undefined)
|
|
182
|
-
continue;
|
|
183
|
-
stdout.write(` ${row.at}\n`);
|
|
184
|
-
let body = '';
|
|
185
|
-
try {
|
|
186
|
-
body = readFileSync(resolve(TREE.at, row.at), 'utf8');
|
|
187
|
-
}
|
|
188
|
-
catch {
|
|
189
|
-
body = '(nothing is there)\n';
|
|
190
|
-
}
|
|
191
|
-
for (const line of body.replace(/\n+$/, '').split('\n'))
|
|
192
|
-
stdout.write(` ${line}\n`);
|
|
193
|
-
}
|
|
194
|
-
exit(0);
|
|
195
|
-
}
|
|
196
|
-
stdout.write(`${listing}\n`);
|
|
197
|
-
function method(nonce) {
|
|
198
|
-
const asks = () => [
|
|
199
|
-
'the three things this makes, and what each takes:',
|
|
200
|
-
'',
|
|
201
|
-
` thread create ${MINE} new <name> <nodes> ${RECEIPT} ${nonce}`,
|
|
202
|
-
' <name> optional — asked for if left off. Lowercased, and anything that is not a letter',
|
|
203
|
-
' or a digit becomes _, so "Send Invoice" is send_invoice.',
|
|
204
|
-
' <nodes> optional — how many nodes to ask for. Left off or 0 asks until a node name is',
|
|
205
|
-
' answered blank.',
|
|
206
|
-
'',
|
|
207
|
-
` thread create ${MINE} edit <mark> ${RECEIPT} ${nonce}`,
|
|
208
|
-
' <mark> optional — asked for if left off. The six characters in brackets beside an',
|
|
209
|
-
' operation above. It is derived from that operation\'s own rows, so it changes',
|
|
210
|
-
' the moment the operation does.',
|
|
211
|
-
'',
|
|
212
|
-
` thread create ${MINE} read <mark>`,
|
|
213
|
-
' No receipt, and it writes nothing. Prints one operation, its rows, and every node file.',
|
|
214
|
-
'',
|
|
215
|
-
...wrapped(`${RECEIPT} is spent by one run, and is refused if ${at} changed after it was issued — what ` +
|
|
216
|
-
'is listed above is what you are writing against, and handing one back says you read it.', ' '),
|
|
217
|
-
];
|
|
218
|
-
const named = [
|
|
219
|
-
'what an operation is called:',
|
|
220
|
-
'',
|
|
221
|
-
' You send the word. The prefix is put on for you, and there is no field holding either —',
|
|
222
|
-
' the prefix on the id is which one a row is.',
|
|
223
|
-
'',
|
|
224
|
-
...Object.entries(PREFIXES).map(([word, prefix]) => ` ${word.padEnd(9)} → ${prefix}<name>`),
|
|
225
|
-
];
|
|
226
|
-
const piping = [
|
|
227
|
-
'what a program sends, rather than what a person is answering:',
|
|
228
|
-
'',
|
|
229
|
-
' One `key: value` per line, in any order. Blank lines and lines opening with # are skipped.',
|
|
230
|
-
` \`body:\` opens a block running to a line holding only ${ENDS}, and what is written on the`,
|
|
231
|
-
' `body:` line itself is part of it. A new `node:` begins the next node.',
|
|
232
|
-
'',
|
|
233
|
-
' A body is the WHOLE argument its node type is called with — an arrow function, not the',
|
|
234
|
-
' statements inside one: async (hands, held) => { ... }',
|
|
235
|
-
'',
|
|
236
|
-
` \`new\` takes: ${(ASKS.new ?? []).map((one) => `\`${one}\``).join(', ')}`,
|
|
237
|
-
` \`edit\` takes: ${(ASKS.edit ?? []).map((one) => `\`${one}\``).join(', ')}`,
|
|
238
|
-
` each node takes: ${NODE_ASKS.map((one) => `\`${one}\``).join(', ')}`,
|
|
239
|
-
];
|
|
240
|
-
const news = [
|
|
241
|
-
'what `new` asks, in this order:',
|
|
242
|
-
'',
|
|
243
|
-
...guidance(NEW),
|
|
244
|
-
'',
|
|
245
|
-
' then, for each node:',
|
|
246
|
-
'',
|
|
247
|
-
...guidance(NODE_NEW).map((line) => ` ${line}`),
|
|
248
|
-
];
|
|
249
|
-
const edits = [
|
|
250
|
-
'what `edit` asks:',
|
|
251
|
-
'',
|
|
252
|
-
...guidance(OPENS),
|
|
253
|
-
'',
|
|
254
|
-
` needs asks \`${REQUIRES.at}\` again, as \`new\` does. It rewrites \`${REQUIRES.at}\` on that operation's`,
|
|
255
|
-
' row and writes every one of its nodes again from it. This is how a capability is',
|
|
256
|
-
' added to or taken off an operation.',
|
|
257
|
-
'',
|
|
258
|
-
' write asks these:',
|
|
259
|
-
...guidance(WRITES).map((line) => ` ${line}`),
|
|
260
|
-
'',
|
|
261
|
-
' add asks the same node keys as `new`, and then, for each node:',
|
|
262
|
-
...guidance(ADDS).map((line) => ` ${line}`),
|
|
263
|
-
];
|
|
264
|
-
const seams = [
|
|
265
|
-
'what an operation may require, and what requiring one hands its nodes:',
|
|
266
|
-
'',
|
|
267
|
-
...handed.flatMap((one) => [
|
|
268
|
-
` ${one} → hands.${supplies(one) ? SUPPLY[one] : one}`,
|
|
269
|
-
...wrapped(sentence(one), ' '),
|
|
270
|
-
...(supplies(one)
|
|
271
|
-
? []
|
|
272
|
-
: wrapped(`Requiring it hands a node one function per call it takes, each already knowing its verb, its path, where the target is and what opens it — so the node writes hands.${one}.<call>({ json }) and never a url, a method, a header or the name of an environment variable.`, ' ')),
|
|
273
|
-
'',
|
|
274
|
-
]),
|
|
275
|
-
' `requires` is written on the operation\'s own row and nowhere else — nothing else names a',
|
|
276
|
-
' capability, and dispatch hands each node those and nothing more. `takes` is written for you',
|
|
277
|
-
` as ['<id>_takes']: the file holding the input schema its input is parsed against, the`,
|
|
278
|
-
' estimate it reserves against a meter, and the row its permission is checked on.',
|
|
279
|
-
];
|
|
280
|
-
const built = [
|
|
281
|
-
'what a node may be built from, and the block each one is handed:',
|
|
282
|
-
'',
|
|
283
|
-
' Every block ends its parameters with (hands, held) — the scaffold under each type shows',
|
|
284
|
-
' what comes before them. `held.input` is the parsed input and `held.<node>` is what an',
|
|
285
|
-
' earlier node returned. `hands.actor` is who asked, and `hands.<seam>` is each capability',
|
|
286
|
-
' the operation required, above. Nothing runs a node but dispatch.',
|
|
287
|
-
'',
|
|
288
|
-
...known.flatMap((one) => [
|
|
289
|
-
` ${one}`,
|
|
290
|
-
...wrapped(pass.coined.get(one)?.said[0] ?? '', ' '),
|
|
291
|
-
...scaffold(one, '<name>').split('\n').map((line) => ` ${line}`),
|
|
292
|
-
'',
|
|
293
|
-
]),
|
|
294
|
-
];
|
|
295
|
-
return [asks(), named, piping, news, edits, seams, built].map((one) => one.join('\n')).join('\n\n');
|
|
296
|
-
}
|
|
297
|
-
const flagged = argv.indexOf(RECEIPT);
|
|
298
|
-
if (flagged === -1) {
|
|
299
|
-
const nonce = issue(TREE.at, listing);
|
|
300
|
-
stdout.write(`\n${method(nonce)}\n`);
|
|
301
|
-
stdout.write(`\ncreate: receipt ${nonce} — spent on use, and stale the moment ${at} changes.\n`);
|
|
302
|
-
stdout.write(`create: thread create ${MINE} new <name> <nodes> ${RECEIPT} ${nonce}\n`);
|
|
303
|
-
stdout.write(`create: thread create ${MINE} edit <mark> ${RECEIPT} ${nonce}\n`);
|
|
304
|
-
stdout.write(`create: thread create ${MINE} remove <mark> ${RECEIPT} ${nonce}\n`);
|
|
305
|
-
stdout.write(`create: reading needs no receipt — thread create ${MINE} read <mark>\n`);
|
|
306
|
-
exit(0);
|
|
307
|
-
}
|
|
308
|
-
spend(TREE.at, listing, argv[flagged + 1], at);
|
|
309
|
-
if (verb === 'remove') {
|
|
310
|
-
const which = words[1];
|
|
311
|
-
const one = already.find((each) => mark(each) === which);
|
|
312
|
-
if (one === undefined) {
|
|
313
|
-
refuse(`\`${which ?? '(nothing)'}\` is not a mark this tree issued. Run \`thread create ${MINE}\` to see them — each is printed in brackets beside the operation it belongs to.`);
|
|
314
|
-
}
|
|
315
|
-
const held = readFileSync(inTree(TREE, OPERATIONS_FILE), 'utf8');
|
|
316
|
-
const bounds = listBounds(held, MEMBERS_LIST);
|
|
317
|
-
if (bounds === undefined)
|
|
318
|
-
refuse(`${at} states no \`${MEMBERS_LIST}\` list to remove an operation from`);
|
|
319
|
-
writeFileSync(inTree(TREE, OPERATIONS_FILE), withoutRow(held, bounds, one.id), 'utf8');
|
|
320
|
-
stdout.write(`create: removed ${one.id} from ${at}.\n`);
|
|
321
|
-
for (const gone of [`${UNDER}/${one.id}`, `${UNDER}/${one.id}.ts`, routeAt(one.id)]) {
|
|
322
|
-
const where = resolve(TREE.at, gone);
|
|
323
|
-
if (!existsSync(where))
|
|
324
|
-
continue;
|
|
325
|
-
rmSync(where, { recursive: true, force: true });
|
|
326
|
-
stdout.write(`create: deleted ${gone}\n`);
|
|
327
|
-
}
|
|
328
|
-
stdout.write(`create: the grant naming ${one.id} is \`thread grant\`'s row and is left standing — take it off with ` +
|
|
329
|
-
`\`thread grant\` so one maker is not writing another's sheet.\n`);
|
|
330
|
-
exit(0);
|
|
331
|
-
}
|
|
332
|
-
const held = pipedIn();
|
|
333
|
-
const { own, each: piling } = scoped(held, NODE_NAME.at);
|
|
334
|
-
const { piped, ask, line, shut } = asking(own);
|
|
335
|
-
if (piped) {
|
|
336
|
-
const doing = verb ?? answer(own, 'do') ?? '';
|
|
337
|
-
const allowed = ASKS[doing];
|
|
338
|
-
if (allowed === undefined) {
|
|
339
|
-
refuse(`there is nothing called "${doing}" to do here — say new, edit, remove, or read, which needs no receipt`);
|
|
340
|
-
}
|
|
341
|
-
only(own, allowed, `\`${doing}\``);
|
|
342
|
-
for (const one of piling)
|
|
343
|
-
only(one, NODE_ASKS, 'a node');
|
|
344
|
-
}
|
|
345
|
-
const doing = verb ?? (await ask('do', 'new or edit: '));
|
|
346
|
-
if (doing !== 'new' && doing !== 'edit') {
|
|
347
|
-
shut();
|
|
348
|
-
refuse(`there is nothing called "${doing}" to do here — say new, edit, remove, or read, which needs no receipt`);
|
|
349
|
-
}
|
|
350
|
-
async function written(from, name, scope = own) {
|
|
351
|
-
if (piped) {
|
|
352
|
-
const body = answer(scope, NODE_BODY.at);
|
|
353
|
-
return body === undefined || body === '' ? undefined : body;
|
|
354
|
-
}
|
|
355
|
-
stdout.write(` body — what a ${from} is handed looks like this:\n`);
|
|
356
|
-
for (const line of scaffold(from, name).split('\n'))
|
|
357
|
-
stdout.write(` ${line}\n`);
|
|
358
|
-
stdout.write(` write yours, then a line holding only ${ENDS} — a first line of ${ENDS} leaves it unwritten, and it throws when it runs.\n`);
|
|
359
|
-
const held = [];
|
|
360
|
-
for (;;) {
|
|
361
|
-
const next = await line();
|
|
362
|
-
if (next === undefined)
|
|
363
|
-
break;
|
|
364
|
-
if (next.trim() === ENDS)
|
|
365
|
-
break;
|
|
366
|
-
held.push(next);
|
|
367
|
-
}
|
|
368
|
-
const body = held.join('\n').trim();
|
|
369
|
-
return body === '' ? undefined : body;
|
|
370
|
-
}
|
|
371
|
-
async function asked(order, held, scope) {
|
|
372
|
-
if (piped) {
|
|
373
|
-
if (scope === undefined)
|
|
374
|
-
return undefined;
|
|
375
|
-
const at = String(scope[0]?.at ?? 0);
|
|
376
|
-
const name = slug(scope[0]?.value ?? '');
|
|
377
|
-
if (name === '')
|
|
378
|
-
refuse(`the node opened on line ${at} is not named. ${accepted(scope)}. Nothing was written.`);
|
|
379
|
-
if (!IDENTIFIER.test(name)) {
|
|
380
|
-
refuse(`\`${name}\` on line ${at} cannot be the name of a node — it is written as a file and an export, so it starts with a letter. Nothing was written.`);
|
|
381
|
-
}
|
|
382
|
-
if (held.has(name))
|
|
383
|
-
refuse(`\`${name}\` on line ${at} is already a node of this operation. Nothing was written.`);
|
|
384
|
-
const does = needed(scope, NODE_DOES.at, `the node \`${name}\``);
|
|
385
|
-
const from = needed(scope, NODE_TYPE.at, `the node \`${name}\``);
|
|
386
|
-
if (!known.includes(from)) {
|
|
387
|
-
refuse(`\`${from}\` on line ${at} is not a node type this app declares — it declares ${known.join(', ')}. Nothing was written.`);
|
|
388
|
-
}
|
|
389
|
-
return { id: name, does, from, body: await written(from, name, scope) };
|
|
390
|
-
}
|
|
391
|
-
for (;;) {
|
|
392
|
-
const name = slug(await ask(NODE_NAME.at, `node ${String(order)} — name: `));
|
|
393
|
-
if (name === '')
|
|
394
|
-
return undefined;
|
|
395
|
-
if (!IDENTIFIER.test(name)) {
|
|
396
|
-
stdout.write(` "${name}" cannot be the name of a node — it is written as a file and an export, so it starts with a letter.\n`);
|
|
397
|
-
continue;
|
|
398
|
-
}
|
|
399
|
-
if (held.has(name)) {
|
|
400
|
-
stdout.write(` \`${name}\` is already a node of this operation.\n`);
|
|
401
|
-
continue;
|
|
402
|
-
}
|
|
403
|
-
const does = await ask(NODE_DOES.at, prompted(NODE_DOES));
|
|
404
|
-
if (does === '') {
|
|
405
|
-
stdout.write(' a node says what it does — that sentence is what a canvas shows and what its log is filed under.\n');
|
|
406
|
-
continue;
|
|
407
|
-
}
|
|
408
|
-
const from = await ask(NODE_TYPE.at, prompted(NODE_TYPE));
|
|
409
|
-
if (!known.includes(from)) {
|
|
410
|
-
stdout.write(` \`${from}\` is not a node type this app declares.\n`);
|
|
411
|
-
continue;
|
|
412
|
-
}
|
|
413
|
-
return { id: name, does, from, body: await written(from, name) };
|
|
414
|
-
}
|
|
415
|
-
}
|
|
416
|
-
async function needs() {
|
|
417
|
-
if (piped) {
|
|
418
|
-
const answered = answer(own, REQUIRES.at) ?? '';
|
|
419
|
-
if (answered === '')
|
|
420
|
-
return [];
|
|
421
|
-
const words = [...new Set(answered.split(',').map((one) => one.trim()).filter((one) => one !== ''))];
|
|
422
|
-
const stray = words.filter((one) => !handed.includes(one));
|
|
423
|
-
if (stray.length === 0)
|
|
424
|
-
return words;
|
|
425
|
-
refuse(`this app coins no capability called ${stray.join(', ')} — a node is handed what dispatch can supply, ` +
|
|
426
|
-
`which here is ${handed.join(', ')}. Nothing was written.`);
|
|
427
|
-
}
|
|
428
|
-
stdout.write(` what it can be handed: ${handed.join(', ')}\n`);
|
|
429
|
-
for (;;) {
|
|
430
|
-
const answered = await ask(REQUIRES.at, prompted(REQUIRES));
|
|
431
|
-
if (answered === '')
|
|
432
|
-
return [];
|
|
433
|
-
const words = [...new Set(answered.split(',').map((one) => one.trim()).filter((one) => one !== ''))];
|
|
434
|
-
const stray = words.filter((one) => !handed.includes(one));
|
|
435
|
-
if (stray.length === 0)
|
|
436
|
-
return words;
|
|
437
|
-
stdout.write(` this app coins no capability called ${stray.join(', ')} — a node is handed what dispatch can supply.\n`);
|
|
438
|
-
}
|
|
439
|
-
}
|
|
440
|
-
async function placed(order, scope = own) {
|
|
441
|
-
if (order.length === 0)
|
|
442
|
-
return 0;
|
|
443
|
-
if (piped) {
|
|
444
|
-
const answered = answer(scope, AFTER.at) ?? '';
|
|
445
|
-
if (answered === '')
|
|
446
|
-
return order.length;
|
|
447
|
-
const at = Number(answered);
|
|
448
|
-
if (!Number.isInteger(at) || at < 0 || at > order.length) {
|
|
449
|
-
refuse(`there is no place ${answered} in a sequence of ${String(order.length)}. Nothing was written.`);
|
|
450
|
-
}
|
|
451
|
-
return at;
|
|
452
|
-
}
|
|
453
|
-
for (;;) {
|
|
454
|
-
const answered = await ask(AFTER.at, ` where does it go — 0 to run first, or the node it follows (1–${String(order.length)}, blank for last): `);
|
|
455
|
-
if (answered === '')
|
|
456
|
-
return order.length;
|
|
457
|
-
const at = Number(answered);
|
|
458
|
-
if (!Number.isInteger(at) || at < 0 || at > order.length) {
|
|
459
|
-
stdout.write(` there is no place ${answered} in a sequence of ${String(order.length)}.\n`);
|
|
460
|
-
continue;
|
|
461
|
-
}
|
|
462
|
-
return at;
|
|
463
|
-
}
|
|
464
|
-
}
|
|
465
|
-
function after(text, where, follows, source) {
|
|
466
|
-
const held = afterRow(text, where, follows, source);
|
|
467
|
-
if (held === undefined)
|
|
468
|
-
refuse(`\`${follows}\` states no row here to place a node after`);
|
|
469
|
-
return held;
|
|
470
|
-
}
|
|
471
|
-
const node = (id, one) => ({
|
|
472
|
-
id: one.id,
|
|
473
|
-
at: `${UNDER}/${id}/${one.id}.ts`,
|
|
474
|
-
describes: one.does,
|
|
475
|
-
requires: [one.from],
|
|
476
|
-
});
|
|
477
|
-
function running(what, why) {
|
|
478
|
-
const ran = spawnSync('npx', ['thread', ...what], { cwd: TREE.at, encoding: 'utf8' });
|
|
479
|
-
stdout.write(`${`${ran.stdout ?? ''}${ran.stderr ?? ''}`.trim()}\n`);
|
|
480
|
-
if (ran.status !== 0) {
|
|
481
|
-
stderr.write(`create: ${why} The rows are written and stand; what is above is what to fix.\n`);
|
|
482
|
-
exit(1);
|
|
483
|
-
}
|
|
484
|
-
}
|
|
485
|
-
function recording() {
|
|
486
|
-
const was = spawnSync('npx', ['thread', 'trials', '--check'], { cwd: TREE.at, encoding: 'utf8' });
|
|
487
|
-
const said = `${was.stdout ?? ''}${was.stderr ?? ''}`.trim();
|
|
488
|
-
if (was.status !== 0 && said !== '') {
|
|
489
|
-
stdout.write(`\ncreate: what this changed about what the operation does —\n${said}\n\n`);
|
|
490
|
-
}
|
|
491
|
-
running(['trials'], 'it is declared, seeded, compiled, and the trials would not record.');
|
|
492
|
-
}
|
|
493
|
-
function trying(of) {
|
|
494
|
-
const typed = answer(own, INPUT.at);
|
|
495
|
-
const scripted = answer(own, ANSWERS.at);
|
|
496
|
-
const permitted = answer(own, TO.at);
|
|
497
|
-
const said = [
|
|
498
|
-
`name: s1`,
|
|
499
|
-
`input: ${typed ?? '{}'}`,
|
|
500
|
-
...(permitted === undefined || permitted.trim() === '' ? [] : [`roles: ${permitted}`]),
|
|
501
|
-
...(scripted === undefined ? [] : [`answers: ${scripted}`]),
|
|
502
|
-
].join('\n');
|
|
503
|
-
const ran = piped
|
|
504
|
-
? spawnSync('npx', ['thread', 'run', 'trial', of], { cwd: TREE.at, encoding: 'utf8', input: `${said}\n` })
|
|
505
|
-
: spawnSync('npx', ['thread', 'run', 'trial', of], { cwd: TREE.at, stdio: 'inherit' });
|
|
506
|
-
if (piped)
|
|
507
|
-
stdout.write(`${`${ran.stdout ?? ''}${ran.stderr ?? ''}`.trim()}\n`);
|
|
508
|
-
return ran.status === 0;
|
|
509
|
-
}
|
|
510
|
-
function granting(of, may) {
|
|
511
|
-
if (!offers.has('thread grant'))
|
|
512
|
-
return;
|
|
513
|
-
if (may.length === 0) {
|
|
514
|
-
stdout.write(`create: nobody was named as allowed to run ${of}, so no grant was written and no caller may ask for it. ` +
|
|
515
|
-
'Its trial will not reach it — a run that measured nothing has proven nothing — so `thread gate` will be red ' +
|
|
516
|
-
`until you say who may. Run \`thread grant\` when you have decided.\n`);
|
|
517
|
-
return;
|
|
518
|
-
}
|
|
519
|
-
const listed = spawnSync('npx', ['thread', 'grant'], { cwd: TREE.at, encoding: 'utf8' });
|
|
520
|
-
const ticket = /receipt ([0-9a-f]+)/.exec(`${listed.stdout ?? ''}${listed.stderr ?? ''}`)?.[1];
|
|
521
|
-
if (ticket === undefined) {
|
|
522
|
-
stdout.write(`create: \`thread grant\` issued no receipt, so the grant for ${of} was not written. Run \`thread grant\` yourself — ` +
|
|
523
|
-
'everything else about the operation stands.\n');
|
|
524
|
-
return;
|
|
525
|
-
}
|
|
526
|
-
const sheet = inTree(TREE, 'entities.ts');
|
|
527
|
-
const keeps = existsSync(sheet) && /\bid: '/.test(readFileSync(sheet, 'utf8'));
|
|
528
|
-
const answers = [
|
|
529
|
-
`Lets ${may.join(', ')} run ${of}.`,
|
|
530
|
-
...(keeps ? [''] : []),
|
|
531
|
-
of,
|
|
532
|
-
may.join(', '),
|
|
533
|
-
'',
|
|
534
|
-
'n',
|
|
535
|
-
].join('\n');
|
|
536
|
-
const ran = spawnSync('npx', ['thread', 'grant', 'new', `${of.replace(/^[a-z]+_/, '')}-callers`, '--receipt', ticket], {
|
|
537
|
-
cwd: TREE.at,
|
|
538
|
-
encoding: 'utf8',
|
|
539
|
-
input: `${answers}\n`,
|
|
540
|
-
});
|
|
541
|
-
stdout.write(`${`${ran.stdout ?? ''}${ran.stderr ?? ''}`.trim()}\n`);
|
|
542
|
-
if (ran.status !== 0) {
|
|
543
|
-
stderr.write(`create: ${of} is declared and seeded, and the grant was refused above. Nobody may run it until one is ` +
|
|
544
|
-
'written, so its trial will not reach it. Run `thread grant` yourself.\n');
|
|
545
|
-
exit(1);
|
|
546
|
-
}
|
|
547
|
-
}
|
|
548
|
-
function finish(bodies, of, fresh, may = []) {
|
|
549
|
-
const seeded = spawnSync('npx', ['thread', 'seed'], { cwd: TREE.at, encoding: 'utf8' });
|
|
550
|
-
stdout.write(`${`${seeded.stdout ?? ''}${seeded.stderr ?? ''}`.trim()}\n`);
|
|
551
|
-
if (seeded.status !== 0) {
|
|
552
|
-
stderr.write('create: it is declared and the seeder refused it. The rows are written; nothing was put on disk for them.\n');
|
|
553
|
-
exit(1);
|
|
554
|
-
}
|
|
555
|
-
for (const [where, one] of bodies) {
|
|
556
|
-
const path = resolve(TREE.at, where);
|
|
557
|
-
mkdirSync(dirname(path), { recursive: true });
|
|
558
|
-
writeFileSync(path, nodeText(where, one.name, one.takes, one.from, one.requires, one.body), 'utf8');
|
|
559
|
-
stdout.write(`create: wrote the block of ${one.name} into ${where}\n`);
|
|
560
|
-
}
|
|
561
|
-
if (fresh)
|
|
562
|
-
granting(of, may);
|
|
563
|
-
const made = spawnSync('npx', ['thread', 'compile'], { cwd: TREE.at, encoding: 'utf8' });
|
|
564
|
-
stdout.write(`${`${made.stdout ?? ''}${made.stderr ?? ''}`.trim()}\n`);
|
|
565
|
-
if (made.status !== 0) {
|
|
566
|
-
stderr.write('create: it is declared, seeded and permitted, and the compiler refused it. The rows and the grant are written ' +
|
|
567
|
-
'and stand — fix the block with `thread create operation edit`, and what the compiler refused is above.\n');
|
|
568
|
-
exit(1);
|
|
569
|
-
}
|
|
570
|
-
const MAKES = `thread create ${MINE}`;
|
|
571
|
-
for (const said of settling(TREE.at, declared, ['thread trials', MAKES]).ran)
|
|
572
|
-
stdout.write(`${said}\n`);
|
|
573
|
-
const proven = fresh ? trying(of) : (recording(), true);
|
|
574
|
-
const settled = settling(TREE.at, declared, [MAKES]);
|
|
575
|
-
for (const said of settled.ran)
|
|
576
|
-
stdout.write(`${said}\n`);
|
|
577
|
-
if (settled.unsettled.length > 0) {
|
|
578
|
-
stderr.write(`create: ${of} is declared and written, and ${settled.unsettled.join(', ')} would not settle after ` +
|
|
579
|
-
`${String(settled.rounds)} round(s). Run it yourself and read what it says — a command claiming nothing is ` +
|
|
580
|
-
'left to run while a check is still red is how a red gate becomes normal.\n');
|
|
581
|
-
exit(1);
|
|
582
|
-
}
|
|
583
|
-
if (!proven && spawnSync('npx', ['thread', 'trials', '--check'], { cwd: TREE.at, encoding: 'utf8' }).status !== 0) {
|
|
584
|
-
stderr.write(`create: ${of} is declared, seeded, compiled and everything derived from it is written — and it is not proven. ` +
|
|
585
|
-
`The trial is above. Give it what it needs, then \`thread run trial ${of}\`.\n`);
|
|
586
|
-
exit(1);
|
|
587
|
-
}
|
|
588
|
-
const compiler = compilerIn(TREE.at);
|
|
589
|
-
if (compiler === null) {
|
|
590
|
-
stdout.write('create: it is declared and seeded, and nothing here can typecheck it — this app has neither ' +
|
|
591
|
-
'`tsc6` nor `tsc` in its own `.bin`. Install a typescript, then run `thread compile`. ' +
|
|
592
|
-
'The rows are written and stand.\n');
|
|
593
|
-
exit(1);
|
|
594
|
-
}
|
|
595
|
-
const held = spawnSync(compiler, ['--noEmit'], { cwd: TREE.at, encoding: 'utf8' });
|
|
596
|
-
if (held.status === 0) {
|
|
597
|
-
stdout.write('create: it compiles. Nothing is left to run.\n');
|
|
598
|
-
exit(0);
|
|
599
|
-
}
|
|
600
|
-
stdout.write(`${`${held.stdout ?? ''}${held.stderr ?? ''}`.trim()}\n`);
|
|
601
|
-
stderr.write('create: it is declared and seeded, and the compiler refuses what is above. The rows are written and stand; what it names is what to fix.\n');
|
|
602
|
-
exit(1);
|
|
603
|
-
}
|
|
604
|
-
function compilerIn(at) {
|
|
605
|
-
const bin = join(at, 'node_modules', '.bin');
|
|
606
|
-
for (const name of ['tsc6', 'tsc']) {
|
|
607
|
-
if (existsSync(join(bin, name)))
|
|
608
|
-
return join(bin, name);
|
|
609
|
-
}
|
|
610
|
-
return null;
|
|
611
|
-
}
|
|
612
|
-
function needing(text, where, id, requires) {
|
|
613
|
-
const held = withRequires(text, where, id, requires);
|
|
614
|
-
if (held === undefined) {
|
|
615
|
-
refuse(`\`${id}\` states no row here to change, or no \`version\` to put a \`requires\` beside`);
|
|
616
|
-
}
|
|
617
|
-
return held;
|
|
618
|
-
}
|
|
619
|
-
const path = inTree(TREE, OPERATIONS_FILE);
|
|
620
|
-
if (doing === 'edit') {
|
|
621
|
-
const which = words[1] ?? (await ask(MARK.at, prompted(MARK)));
|
|
622
|
-
const one = already.find((held) => mark(held) === which);
|
|
623
|
-
if (one === undefined) {
|
|
624
|
-
shut();
|
|
625
|
-
refuse(`\`${which}\` is not a mark on that list. Each operation's mark is printed beside it and is derived from its own rows, so naming one means having read the line it is on — and it changes the moment that operation does.`);
|
|
626
|
-
}
|
|
627
|
-
stdout.write(`${one.id} — ${one.describes}\n`);
|
|
628
|
-
for (const [order, row] of one.nodes.entries()) {
|
|
629
|
-
stdout.write(` ${String(order + 1)}. ${String(row.id)} — ${String(row.describes)} — ${(row.requires ?? []).join(', ')}\n`);
|
|
630
|
-
}
|
|
631
|
-
stdout.write(`it requires ${one.requires.length === 0 ? 'nothing' : one.requires.join(', ')}\n`);
|
|
632
|
-
const how = await ask(HOW.at, prompted(HOW));
|
|
633
|
-
if (how === 'needs') {
|
|
634
|
-
const wanted = await needs();
|
|
635
|
-
shut();
|
|
636
|
-
const before = readFileSync(path, 'utf8');
|
|
637
|
-
const list = listBounds(before, MEMBERS_LIST);
|
|
638
|
-
if (list === undefined)
|
|
639
|
-
refuse(`${at} states no \`${MEMBERS_LIST}\` list`);
|
|
640
|
-
writeFileSync(path, needing(before, list, one.id, wanted), 'utf8');
|
|
641
|
-
stdout.write(`create: \`${one.id}\` requires ${wanted.length === 0 ? 'nothing' : wanted.join(', ')} in ${at}. Every node of it is written again from that.\n`);
|
|
642
|
-
finish(new Map(), one.id, false);
|
|
643
|
-
}
|
|
644
|
-
if (how === 'write') {
|
|
645
|
-
const numbered = Number(await ask(WHICH.at, `which node — 1 to ${String(one.nodes.length)}: `));
|
|
646
|
-
const row = one.nodes[numbered - 1];
|
|
647
|
-
if (row === undefined || row.at === undefined) {
|
|
648
|
-
shut();
|
|
649
|
-
refuse(`there is no node ${String(numbered)} in \`${one.id}\`, which has ${String(one.nodes.length)}`);
|
|
650
|
-
}
|
|
651
|
-
const from = (row.requires ?? []).find((each) => known.includes(each));
|
|
652
|
-
if (from === undefined) {
|
|
653
|
-
shut();
|
|
654
|
-
refuse(`the node \`${String(row.id)}\` requires no type this app declares, so nothing knows what shape its block takes`);
|
|
655
|
-
}
|
|
656
|
-
stdout.write(` ${row.at} holds:\n`);
|
|
657
|
-
try {
|
|
658
|
-
for (const line of readFileSync(resolve(TREE.at, row.at), 'utf8').replace(/\n+$/, '').split('\n')) {
|
|
659
|
-
stdout.write(` ${line}\n`);
|
|
660
|
-
}
|
|
661
|
-
}
|
|
662
|
-
catch {
|
|
663
|
-
stdout.write(' (nothing is there)\n');
|
|
664
|
-
}
|
|
665
|
-
const body = await written(from, String(row.id));
|
|
666
|
-
shut();
|
|
667
|
-
if (body === undefined)
|
|
668
|
-
refuse(`\`${String(row.id)}\` was left as it was — nothing was written`);
|
|
669
|
-
finish(new Map([[row.at, { name: String(row.id), takes: `${UNDER}/${one.id}/input.ts`, from, requires: one.requires, body }]]), one.id, false);
|
|
670
|
-
}
|
|
671
|
-
if (how !== 'add') {
|
|
672
|
-
shut();
|
|
673
|
-
refuse(`there is nothing called "${how}" to do to an operation — say add, write or needs`);
|
|
674
|
-
}
|
|
675
|
-
const order = one.nodes.map((row) => String(row.id));
|
|
676
|
-
const taken = new Set(order);
|
|
677
|
-
const added = [];
|
|
678
|
-
const bodies = new Map();
|
|
679
|
-
for (let turn = 0;; turn += 1) {
|
|
680
|
-
const scope = piped ? piling[turn] : undefined;
|
|
681
|
-
if (piped && scope === undefined)
|
|
682
|
-
break;
|
|
683
|
-
const held = await asked(order.length + 1, taken, scope);
|
|
684
|
-
if (held === undefined)
|
|
685
|
-
break;
|
|
686
|
-
const where = await placed(order, scope ?? own);
|
|
687
|
-
taken.add(held.id);
|
|
688
|
-
order.splice(where, 0, held.id);
|
|
689
|
-
const row = node(one.id, held);
|
|
690
|
-
if (held.body !== undefined) {
|
|
691
|
-
bodies.set(row.at, {
|
|
692
|
-
name: held.id,
|
|
693
|
-
takes: `${UNDER}/${one.id}/input.ts`,
|
|
694
|
-
from: held.from,
|
|
695
|
-
requires: one.requires,
|
|
696
|
-
body: held.body,
|
|
697
|
-
});
|
|
698
|
-
}
|
|
699
|
-
added.push({ row, follows: where === 0 ? `${one.id}_takes` : String(order[where - 1]) });
|
|
700
|
-
}
|
|
701
|
-
shut();
|
|
702
|
-
if (added.length === 0)
|
|
703
|
-
refuse(`\`${one.id}\` was left as it was — nothing was written`);
|
|
704
|
-
let text = readFileSync(path, 'utf8');
|
|
705
|
-
const seeding = (held) => {
|
|
706
|
-
const list = listBounds(held, MEMBERS_LIST);
|
|
707
|
-
if (list === undefined)
|
|
708
|
-
refuse(`${at} states no \`${MEMBERS_LIST}\` list`);
|
|
709
|
-
const within = seedsIn(held, list, one.id);
|
|
710
|
-
if (within === undefined)
|
|
711
|
-
refuse(`\`${one.id}\` states no \`seeds\` list to add a node to`);
|
|
712
|
-
return within;
|
|
713
|
-
};
|
|
714
|
-
for (const held of added)
|
|
715
|
-
text = after(text, seeding(text), held.follows, said(held.row, ' '));
|
|
716
|
-
writeFileSync(path, text, 'utf8');
|
|
717
|
-
stdout.write(`create: added ${String(added.length)} node(s) to ${one.id} in ${at}.\n`);
|
|
718
|
-
finish(bodies, one.id, false);
|
|
719
|
-
}
|
|
720
|
-
const name = slug(words[1] ?? (await ask(NAME.at, prompted(NAME))));
|
|
721
|
-
if (name === '') {
|
|
722
|
-
shut();
|
|
723
|
-
refuse('an operation has to be called something');
|
|
724
|
-
}
|
|
725
|
-
const carried = Object.values(PREFIXES).find((one) => name.startsWith(one));
|
|
726
|
-
if (carried !== undefined) {
|
|
727
|
-
shut();
|
|
728
|
-
refuse(`\`${name}\` already opens with \`${carried}\`, and the prefix is put on for you — this would ` +
|
|
729
|
-
`declare \`${carried}${name}\`. Send the word alone: \`${name.slice(carried.length)}\`.`);
|
|
730
|
-
}
|
|
731
|
-
const kind = await ask(TYPE.at, prompted(TYPE));
|
|
732
|
-
const prefix = Object.entries(PREFIXES).find(([word]) => word === kind)?.[1];
|
|
733
|
-
if (prefix === undefined) {
|
|
734
|
-
shut();
|
|
735
|
-
refuse(`there is no such type as "${kind}". An operation is a ${Object.keys(PREFIXES).join(', a ')}.`);
|
|
736
|
-
}
|
|
737
|
-
const id = `${prefix}${name}`;
|
|
738
|
-
const describes = await ask(DOES.at, prompted(DOES));
|
|
739
|
-
if (describes === '') {
|
|
740
|
-
shut();
|
|
741
|
-
refuse(`\`${id}\` was given no meaning, and a row without one is a name nobody can read`);
|
|
742
|
-
}
|
|
743
|
-
const requires = await needs();
|
|
744
|
-
const keeping = await (async () => {
|
|
745
|
-
try {
|
|
746
|
-
const at = inTree(TREE, 'entities.ts');
|
|
747
|
-
if (!existsSync(at))
|
|
748
|
-
return [];
|
|
749
|
-
const sheet = (await import(__rewriteRelativeImportExtension(pathToFileURL(at).href)));
|
|
750
|
-
const list = sheet['ENTITIES'];
|
|
751
|
-
return Array.isArray(list) ? list : [];
|
|
752
|
-
}
|
|
753
|
-
catch {
|
|
754
|
-
return [];
|
|
755
|
-
}
|
|
756
|
-
})();
|
|
757
|
-
const allowed = declaredWords(keeping, 'roles', PUBLIC_ROLE);
|
|
758
|
-
const answered = await ask(TO.at, ` who may run it — ${allowed.join(', ')} (blank for nobody yet): `);
|
|
759
|
-
const wrong = undeclared(answered, allowed, 'roles');
|
|
760
|
-
if (wrong !== undefined)
|
|
761
|
-
refuse(wrong);
|
|
762
|
-
const permitted = answered
|
|
763
|
-
.split(',')
|
|
764
|
-
.map((one) => one.trim())
|
|
765
|
-
.filter((one) => one !== '');
|
|
766
|
-
const counted = Number(words[2] ?? (await ask(NODES.at, prompted(NODES))));
|
|
767
|
-
const nodes = [];
|
|
768
|
-
const bodies = new Map();
|
|
769
|
-
const taken = new Set();
|
|
770
|
-
for (let order = 1; counted > 0 ? order <= counted : true; order += 1) {
|
|
771
|
-
const scope = piped ? piling[order - 1] : undefined;
|
|
772
|
-
if (piped && scope === undefined)
|
|
773
|
-
break;
|
|
774
|
-
const held = await asked(order, taken, scope);
|
|
775
|
-
if (held === undefined)
|
|
776
|
-
break;
|
|
777
|
-
taken.add(held.id);
|
|
778
|
-
const row = node(id, held);
|
|
779
|
-
if (held.body !== undefined) {
|
|
780
|
-
bodies.set(row.at, { name: held.id, takes: `${UNDER}/${id}/input.ts`, from: held.from, requires, body: held.body });
|
|
781
|
-
}
|
|
782
|
-
nodes.push(row);
|
|
783
|
-
}
|
|
784
|
-
shut();
|
|
785
|
-
if (nodes.length === 0)
|
|
786
|
-
refuse(`\`${id}\` was given no nodes, and an operation is its nodes — nothing was written`);
|
|
787
|
-
const rowFor = () => ({
|
|
788
|
-
id,
|
|
789
|
-
at,
|
|
790
|
-
describes,
|
|
791
|
-
version: VERSION,
|
|
792
|
-
...(requires.length === 0 ? {} : { requires }),
|
|
793
|
-
takes: [`${id}_takes`],
|
|
794
|
-
seeds: [
|
|
795
|
-
{ id: `${id}_takes`, at: `${UNDER}/${id}/input.ts`, describes: TAKES },
|
|
796
|
-
...nodes,
|
|
797
|
-
],
|
|
798
|
-
derives: [
|
|
799
|
-
{ id: `${id}_body`, at: `${UNDER}/${id}.ts`, describes: WOVEN, by: 'thread compile', runs: 'thread compile --check' },
|
|
800
|
-
{ id: `${id}_route`, at: routeAt(id), describes: ROUTE, by: 'thread capabilities', runs: 'thread capabilities --check' },
|
|
801
|
-
],
|
|
802
|
-
});
|
|
803
|
-
const text = readFileSync(path, 'utf8');
|
|
804
|
-
const where = listBounds(text, MEMBERS_LIST);
|
|
805
|
-
if (where === undefined)
|
|
806
|
-
refuse(`${at} states no \`${MEMBERS_LIST}\` list to declare an operation in`);
|
|
807
|
-
if (holdsRow(text, where, id))
|
|
808
|
-
refuse(`\`${id}\` is already declared in ${at}`);
|
|
809
|
-
writeFileSync(path, withRow(text, where, said(rowFor(), ' ')), 'utf8');
|
|
810
|
-
stdout.write(`create: declared ${id} in ${at}, with ${String(nodes.length)} node(s), requiring ${requires.length === 0 ? 'nothing' : requires.join(', ')}.\n`);
|
|
811
|
-
finish(bodies, id, true, permitted);
|