@prestack/thread 0.1.6 → 0.1.8
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 +174 -0
- package/dist/core/attach.js +40 -6
- package/dist/core/attach.ts +38 -6
- package/dist/core/descend.js +13 -17
- package/dist/core/descend.ts +13 -15
- package/dist/integrations/attach.js +5 -3
- package/dist/integrations/attach.ts +8 -4
- package/dist/integrations/cli.js +18 -2
- package/dist/integrations/cli.ts +19 -2
- package/dist/integrations/disk.js +4 -3
- package/dist/integrations/disk.ts +4 -3
- package/dist/integrations/generators/vault.ts +0 -1
- package/dist/integrations/target.d.ts +1 -1
- package/dist/integrations/target.ts +1 -1
- package/dist/integrity/generators/gate.ts +1 -2
- package/dist/integrity/generators/publish.ts +0 -1
- package/dist/integrity/lint.js +0 -6
- package/dist/integrity/lint.ts +0 -10
- package/dist/integrity/slopnator.ts +1 -1
- package/dist/integrity/slopnators/sl_derived_drift.js +0 -2
- package/dist/integrity/slopnators/sl_derived_drift.ts +0 -3
- package/dist/integrity/slopnators/sl_stale_path.js +10 -3
- package/dist/integrity/slopnators/sl_stale_path.ts +10 -3
- package/dist/integrity/slopnators/sl_undeclared_crossing.js +8 -5
- package/dist/integrity/slopnators/sl_undeclared_crossing.ts +12 -5
- package/dist/integrity/slopnators/sl_unlisted_program.js +7 -1
- package/dist/integrity/slopnators/sl_unlisted_program.ts +7 -1
- package/dist/integrity/slopnators/sl_unthreaded.js +0 -2
- package/dist/integrity/slopnators/sl_unthreaded.ts +0 -5
- package/dist/integrity/sweep.ts +0 -2
- package/dist/knowledge/attach.js +1 -18
- package/dist/knowledge/attach.ts +1 -19
- package/dist/knowledge/generators/docsgen.ts +1 -1
- package/dist/operations/generators/capability.js +20 -9
- package/dist/operations/generators/capability.ts +19 -12
- package/dist/operations/generators/compile.js +41 -28
- package/dist/operations/generators/compile.ts +48 -33
- package/dist/operations/generators/permits.js +6 -3
- package/dist/operations/generators/permits.ts +7 -5
- package/dist/operations/generators/trial.ts +1 -1
- package/package.json +1 -1
package/dist/knowledge/attach.js
CHANGED
|
@@ -238,23 +238,6 @@ export const CONCEPTS: readonly Concept[] = [
|
|
|
238
238
|
},
|
|
239
239
|
]
|
|
240
240
|
`;
|
|
241
|
-
const OWN = [
|
|
242
|
-
{
|
|
243
|
-
into: 'knowledge.ts',
|
|
244
|
-
list: 'declares',
|
|
245
|
-
id: 'skills',
|
|
246
|
-
source: [
|
|
247
|
-
' {',
|
|
248
|
-
" id: 'skills',",
|
|
249
|
-
" at: '_threads/_generated/skills.ts',",
|
|
250
|
-
" describes: 'One row per skill `thread skills` writes, so every `skills/<noun>.md` is declared by the command that wrote it instead of being refused as a file nothing named. It sits beside the files it names because the nouns are not known until the tree is read, and a list somebody keeps by hand would go stale the first time a package brings a new maker.',",
|
|
251
|
-
' version: 1,',
|
|
252
|
-
" by: 'thread skills',",
|
|
253
|
-
" runs: 'thread skills --check',",
|
|
254
|
-
' },',
|
|
255
|
-
].join('\n'),
|
|
256
|
-
},
|
|
257
|
-
];
|
|
258
241
|
const SEEDED = {
|
|
259
242
|
attaches: ['@prestack/thread/derivation/attach'],
|
|
260
243
|
id: 'knowledge',
|
|
@@ -330,5 +313,5 @@ export const ATTACH = {
|
|
|
330
313
|
source: CONCEPTS_SOURCE,
|
|
331
314
|
},
|
|
332
315
|
],
|
|
333
|
-
rows:
|
|
316
|
+
rows: SHIPPED,
|
|
334
317
|
};
|
package/dist/knowledge/attach.ts
CHANGED
|
@@ -246,24 +246,6 @@ export const CONCEPTS: readonly Concept[] = [
|
|
|
246
246
|
]
|
|
247
247
|
`
|
|
248
248
|
|
|
249
|
-
const OWN: readonly { readonly into: string; readonly list: string; readonly id: string; readonly source: string }[] = [
|
|
250
|
-
{
|
|
251
|
-
into: 'knowledge.ts',
|
|
252
|
-
list: 'declares',
|
|
253
|
-
id: 'skills',
|
|
254
|
-
source: [
|
|
255
|
-
' {',
|
|
256
|
-
" id: 'skills',",
|
|
257
|
-
" at: '_threads/_generated/skills.ts',",
|
|
258
|
-
" describes: 'One row per skill `thread skills` writes, so every `skills/<noun>.md` is declared by the command that wrote it instead of being refused as a file nothing named. It sits beside the files it names because the nouns are not known until the tree is read, and a list somebody keeps by hand would go stale the first time a package brings a new maker.',",
|
|
259
|
-
' version: 1,',
|
|
260
|
-
" by: 'thread skills',",
|
|
261
|
-
" runs: 'thread skills --check',",
|
|
262
|
-
' },',
|
|
263
|
-
].join('\n'),
|
|
264
|
-
},
|
|
265
|
-
]
|
|
266
|
-
|
|
267
249
|
const SEEDED = {
|
|
268
250
|
attaches: ['@prestack/thread/derivation/attach'] as const,
|
|
269
251
|
id: 'knowledge',
|
|
@@ -341,5 +323,5 @@ export const ATTACH: Attaching = {
|
|
|
341
323
|
source: CONCEPTS_SOURCE,
|
|
342
324
|
},
|
|
343
325
|
],
|
|
344
|
-
rows:
|
|
326
|
+
rows: SHIPPED,
|
|
345
327
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { existsSync, readFileSync, writeFileSync } from '../../integrations/disk.ts'
|
|
2
|
-
import {
|
|
2
|
+
import { resolve } from 'node:path'
|
|
3
3
|
import { argv, stderr, stdout } from 'node:process'
|
|
4
4
|
|
|
5
5
|
import { exit } from '../../core/invoked.ts'
|
|
@@ -535,6 +535,7 @@ export function reaching(targets, calls, stores, vaulted, carried) {
|
|
|
535
535
|
const file = (one.declares ?? [])[0]?.at;
|
|
536
536
|
return file === undefined ? [] : [{ from: beside(REACHING_AT, file), as: 'c' + String(at), of: one.id }];
|
|
537
537
|
});
|
|
538
|
+
const used = new Set();
|
|
538
539
|
const opened = targets
|
|
539
540
|
.map((one) => {
|
|
540
541
|
const reach = reachOf(one.id);
|
|
@@ -545,32 +546,43 @@ export function reaching(targets, calls, stores, vaulted, carried) {
|
|
|
545
546
|
const each = calls
|
|
546
547
|
.filter((call) => call.of === one.id && one.takes.includes(call.id))
|
|
547
548
|
.map((call) => quoted(call.id.replace(/^cl_/, '')) + ': ' + held.as + '[' + quoted(call.path) + ']');
|
|
549
|
+
if (each.length > 0)
|
|
550
|
+
used.add(held.as);
|
|
548
551
|
return ' ' + one.id + ': { ' + each.join(', ') + ' },';
|
|
549
552
|
}
|
|
550
553
|
if (reach === 'disk') {
|
|
551
554
|
const through = one.requires.find((each) => reachOf(each) !== undefined);
|
|
552
|
-
|
|
555
|
+
if (through !== undefined)
|
|
556
|
+
return '';
|
|
557
|
+
used.add('HERE');
|
|
558
|
+
return ' ' + one.id + ': HERE,';
|
|
553
559
|
}
|
|
554
560
|
if (reach === 'cli') {
|
|
555
561
|
const each = calls
|
|
556
562
|
.filter((call) => call.of === one.id && one.takes.includes(call.id))
|
|
557
563
|
.map((call) => quoted(call.id.replace(/^cl_/, '')) + ': ' + quoted(call.path));
|
|
564
|
+
used.add('running');
|
|
558
565
|
return ' ' + one.id + ': running(' + quoted(one.id.replace(/^cli_/, '')) + ', { ' + each.join(', ') + ' }, process.cwd()),';
|
|
559
566
|
}
|
|
560
|
-
if (reach === 'https')
|
|
567
|
+
if (reach === 'https') {
|
|
568
|
+
used.add('sendingHttp');
|
|
561
569
|
return ' ' + one.id + ': sendingHttp(),';
|
|
570
|
+
}
|
|
562
571
|
const held = named.find((each) => each.of === one.id);
|
|
563
|
-
|
|
572
|
+
if (held === undefined)
|
|
573
|
+
return '';
|
|
574
|
+
used.add(held.as);
|
|
575
|
+
return ' ' + one.id + ': ' + held.as + ',';
|
|
564
576
|
})
|
|
565
577
|
.filter((each) => each !== '');
|
|
566
578
|
return [
|
|
567
|
-
'import { HERE } from ' + quoted(runtime(REACHING_AT, '../../integrations/disk.ts')),
|
|
568
|
-
'import { running } from ' + quoted(runtime(REACHING_AT, '../../integrations/cli.ts')),
|
|
569
|
-
'import { sendingHttp } from ' + quoted(runtime(REACHING_AT, '../../integrations/https.ts')),
|
|
579
|
+
...(used.has('HERE') ? ['import { HERE } from ' + quoted(runtime(REACHING_AT, '../../integrations/disk.ts'))] : []),
|
|
580
|
+
...(used.has('running') ? ['import { running } from ' + quoted(runtime(REACHING_AT, '../../integrations/cli.ts'))] : []),
|
|
581
|
+
...(used.has('sendingHttp') ? ['import { sendingHttp } from ' + quoted(runtime(REACHING_AT, '../../integrations/https.ts'))] : []),
|
|
570
582
|
'import type { Counting } from ' +
|
|
571
583
|
quoted(carried ? packageOf('operations') + '/operations/operations' : runtime(REACHING_AT, '../operations.ts')),
|
|
572
|
-
...named.map((each) => 'import { store as ' + each.as + ' } from ' + quoted(each.from)),
|
|
573
|
-
...adapted.map((each) => 'import { calls as ' + each.as + ' } from ' + quoted(each.from)),
|
|
584
|
+
...named.filter((each) => used.has(each.as)).map((each) => 'import { store as ' + each.as + ' } from ' + quoted(each.from)),
|
|
585
|
+
...adapted.filter((each) => used.has(each.as)).map((each) => 'import { calls as ' + each.as + ' } from ' + quoted(each.from)),
|
|
574
586
|
...(vaulted ? ['import { vault } from ' + quoted(beside(REACHING_AT, VAULT_AT))] : []),
|
|
575
587
|
'',
|
|
576
588
|
'declare module ' +
|
|
@@ -807,7 +819,6 @@ if (invoked(import.meta.url)) {
|
|
|
807
819
|
}
|
|
808
820
|
const checking = argv.includes('--check');
|
|
809
821
|
let adrift = 0;
|
|
810
|
-
const keeping = existsSync(resolve(ROOT, ENTITIES_AT));
|
|
811
822
|
if (found.length === 0 && refusals.length === 0) {
|
|
812
823
|
console.log('capabilities: this app declares no operation, so nothing is reachable through one yet.');
|
|
813
824
|
exit(adrift > 0 ? 1 : 0);
|
|
@@ -11,7 +11,7 @@ import { named as packageOf } from '../../core/scope.ts'
|
|
|
11
11
|
import { PREFIXES } from '../../core/thread.ts'
|
|
12
12
|
|
|
13
13
|
import { secrecyOf, type Secret } from '../../integrations/secret.ts'
|
|
14
|
-
import { reachOf, servesBrowser,
|
|
14
|
+
import { reachOf, servesBrowser, targetOf, type Call, type Target, type Webhook } from '../../integrations/target.ts'
|
|
15
15
|
|
|
16
16
|
import {
|
|
17
17
|
CAPABILITIES_AT,
|
|
@@ -33,7 +33,6 @@ import {
|
|
|
33
33
|
type Surface,
|
|
34
34
|
} from '../capabilities.ts'
|
|
35
35
|
import { ANONYMOUS, ENVIRONMENT } from '../../core/actor.ts'
|
|
36
|
-
import { PERMITS_AT } from '../access.ts'
|
|
37
36
|
import { DISPATCH_AT } from '../dispatch.ts'
|
|
38
37
|
import { STREAMED, TOLD } from '../../integrations/https.ts'
|
|
39
38
|
import { holdsRow, listBounds, marked, nestedBounds, rowsIn, valuesIn, withField, withRow, withTopField, withoutRow } from '../../core/rows.ts'
|
|
@@ -642,6 +641,7 @@ export function reaching(
|
|
|
642
641
|
return file === undefined ? [] : [{ from: beside(REACHING_AT, file), as: 'c' + String(at), of: one.id }]
|
|
643
642
|
})
|
|
644
643
|
|
|
644
|
+
const used = new Set<string>()
|
|
645
645
|
const opened = targets
|
|
646
646
|
.map((one) => {
|
|
647
647
|
const reach = reachOf(one.id)
|
|
@@ -651,32 +651,41 @@ export function reaching(
|
|
|
651
651
|
const each = calls
|
|
652
652
|
.filter((call) => call.of === one.id && one.takes.includes(call.id))
|
|
653
653
|
.map((call) => quoted(call.id.replace(/^cl_/, '')) + ': ' + held.as + '[' + quoted(call.path) + ']')
|
|
654
|
+
if (each.length > 0) used.add(held.as)
|
|
654
655
|
return ' ' + one.id + ': { ' + each.join(', ') + ' },'
|
|
655
656
|
}
|
|
656
657
|
if (reach === 'disk') {
|
|
657
658
|
const through = one.requires.find((each) => reachOf(each) !== undefined)
|
|
658
|
-
|
|
659
|
+
if (through !== undefined) return ''
|
|
660
|
+
used.add('HERE')
|
|
661
|
+
return ' ' + one.id + ': HERE,'
|
|
659
662
|
}
|
|
660
663
|
if (reach === 'cli') {
|
|
661
664
|
const each = calls
|
|
662
665
|
.filter((call) => call.of === one.id && one.takes.includes(call.id))
|
|
663
666
|
.map((call) => quoted(call.id.replace(/^cl_/, '')) + ': ' + quoted(call.path))
|
|
667
|
+
used.add('running')
|
|
664
668
|
return ' ' + one.id + ': running(' + quoted(one.id.replace(/^cli_/, '')) + ', { ' + each.join(', ') + ' }, process.cwd()),'
|
|
665
669
|
}
|
|
666
|
-
if (reach === 'https')
|
|
670
|
+
if (reach === 'https') {
|
|
671
|
+
used.add('sendingHttp')
|
|
672
|
+
return ' ' + one.id + ': sendingHttp(),'
|
|
673
|
+
}
|
|
667
674
|
const held = named.find((each) => each.of === one.id)
|
|
668
|
-
|
|
675
|
+
if (held === undefined) return ''
|
|
676
|
+
used.add(held.as)
|
|
677
|
+
return ' ' + one.id + ': ' + held.as + ','
|
|
669
678
|
})
|
|
670
679
|
.filter((each) => each !== '')
|
|
671
680
|
|
|
672
681
|
return [
|
|
673
|
-
'import { HERE } from ' + quoted(runtime(REACHING_AT, '../../integrations/disk.ts')),
|
|
674
|
-
'import { running } from ' + quoted(runtime(REACHING_AT, '../../integrations/cli.ts')),
|
|
675
|
-
'import { sendingHttp } from ' + quoted(runtime(REACHING_AT, '../../integrations/https.ts')),
|
|
682
|
+
...(used.has('HERE') ? ['import { HERE } from ' + quoted(runtime(REACHING_AT, '../../integrations/disk.ts'))] : []),
|
|
683
|
+
...(used.has('running') ? ['import { running } from ' + quoted(runtime(REACHING_AT, '../../integrations/cli.ts'))] : []),
|
|
684
|
+
...(used.has('sendingHttp') ? ['import { sendingHttp } from ' + quoted(runtime(REACHING_AT, '../../integrations/https.ts'))] : []),
|
|
676
685
|
'import type { Counting } from ' +
|
|
677
686
|
quoted(carried ? packageOf('operations') + '/operations/operations' : runtime(REACHING_AT, '../operations.ts')),
|
|
678
|
-
...named.map((each) => 'import { store as ' + each.as + ' } from ' + quoted(each.from)),
|
|
679
|
-
...adapted.map((each) => 'import { calls as ' + each.as + ' } from ' + quoted(each.from)),
|
|
687
|
+
...named.filter((each) => used.has(each.as)).map((each) => 'import { store as ' + each.as + ' } from ' + quoted(each.from)),
|
|
688
|
+
...adapted.filter((each) => used.has(each.as)).map((each) => 'import { calls as ' + each.as + ' } from ' + quoted(each.from)),
|
|
680
689
|
...(vaulted ? ['import { vault } from ' + quoted(beside(REACHING_AT, VAULT_AT))] : []),
|
|
681
690
|
'',
|
|
682
691
|
'declare module ' +
|
|
@@ -951,8 +960,6 @@ if (invoked(import.meta.url)) {
|
|
|
951
960
|
const checking = argv.includes('--check')
|
|
952
961
|
let adrift = 0
|
|
953
962
|
|
|
954
|
-
const keeping = existsSync(resolve(ROOT, ENTITIES_AT))
|
|
955
|
-
|
|
956
963
|
if (found.length === 0 && refusals.length === 0) {
|
|
957
964
|
console.log('capabilities: this app declares no operation, so nothing is reachable through one yet.')
|
|
958
965
|
exit(adrift > 0 ? 1 : 0)
|
|
@@ -152,7 +152,8 @@ function answers(node, found) {
|
|
|
152
152
|
answers(one, found);
|
|
153
153
|
});
|
|
154
154
|
}
|
|
155
|
-
function spliced(text,
|
|
155
|
+
function spliced(text, shape, roles) {
|
|
156
|
+
const { names, mine } = shape;
|
|
156
157
|
const read = arrowIn(text, roles);
|
|
157
158
|
if (typeof read === 'string')
|
|
158
159
|
return read;
|
|
@@ -190,6 +191,7 @@ function spliced(text, names, mine, roles) {
|
|
|
190
191
|
return;
|
|
191
192
|
}
|
|
192
193
|
edits.push({ from: up.getStart(), to: up.getEnd(), text: said });
|
|
194
|
+
shape.heard.add(said);
|
|
193
195
|
return;
|
|
194
196
|
}
|
|
195
197
|
because = `reaches for \`${read.held}\` itself rather than one thing on it, and once every node is a local there is no such thing to hand it`;
|
|
@@ -232,19 +234,20 @@ function spliced(text, names, mine, roles) {
|
|
|
232
234
|
return { form: 'call', text: whole, whole };
|
|
233
235
|
}
|
|
234
236
|
const ANSWERS = `${OWN}ANSWER`;
|
|
235
|
-
|
|
237
|
+
const linesOf = (one, name, unread) => (unread ? `${one.text.replace(`${ANSWERS} = `, 'void (')})` : one.text.replace(ANSWERS, name)).split('\n');
|
|
238
|
+
function answered(name, one, opened, kind, unread) {
|
|
236
239
|
if (one.form === 'expression')
|
|
237
|
-
return [`${kind} ${name}: unknown = ${one.text}`];
|
|
240
|
+
return [unread ? `void (${one.text})` : `${kind} ${name}: unknown = ${one.text}`];
|
|
238
241
|
if (one.form === 'statements') {
|
|
239
|
-
return [`let ${name}: unknown
|
|
242
|
+
return [...(unread ? [] : [`let ${name}: unknown`]), '{', ...linesOf(one, name, unread).map((line) => ` ${line}`), '}'];
|
|
240
243
|
}
|
|
241
|
-
return [`${kind} ${name}: unknown = await ${asFn(one.whole, opened)}(opened)`];
|
|
244
|
+
return [`${unread ? '' : `${kind} ${name}: unknown = `}await ${asFn(one.whole, opened)}(opened)`];
|
|
242
245
|
}
|
|
243
246
|
const asFn = (text, opened, more = '') => `((${text}) as (${more}opened: ${opened}) => Promise<unknown>)`;
|
|
244
247
|
function forked(name, said, shape) {
|
|
245
248
|
if (said.length !== 2)
|
|
246
249
|
return `takes a chooser and the ways it chooses between, and was handed ${String(said.length)} thing(s)`;
|
|
247
|
-
const chose = spliced(said[0] ?? '', shape
|
|
250
|
+
const chose = spliced(said[0] ?? '', shape, ROLES.block);
|
|
248
251
|
if (typeof chose === 'string')
|
|
249
252
|
return `chooses with a block that ${chose}`;
|
|
250
253
|
const held = inside(said[1] ?? '', '{', '}');
|
|
@@ -252,25 +255,26 @@ function forked(name, said, shape) {
|
|
|
252
255
|
const named = ways?.every((one) => one !== undefined && PLAIN.test(one.key)) === true ? ways : undefined;
|
|
253
256
|
if (named === undefined || named.length === 0)
|
|
254
257
|
return 'names its ways in a way this cannot read — write each one as `name: block`';
|
|
258
|
+
const unread = shape.unread.has(name);
|
|
255
259
|
const picked = `${OWN}${name}_said`;
|
|
256
260
|
const runs = [
|
|
257
|
-
`let ${name}: unknown
|
|
261
|
+
...(unread ? [] : [`let ${name}: unknown`]),
|
|
258
262
|
chose.form === 'expression'
|
|
259
263
|
? `const ${picked}: string = await (${chose.text})`
|
|
260
264
|
: `const ${picked}: string = await ((${said[0] ?? ''}) as (opened: ${shape.reads}) => Promise<string>)(opened)`,
|
|
261
265
|
`switch (${picked}) {`,
|
|
262
266
|
];
|
|
263
267
|
for (const way of named) {
|
|
264
|
-
const each = spliced(way?.held ?? '', shape
|
|
268
|
+
const each = spliced(way?.held ?? '', shape, ROLES.block);
|
|
265
269
|
if (typeof each === 'string')
|
|
266
270
|
return `has a way \`${way?.key ?? ''}\` that ${each}`;
|
|
267
271
|
runs.push(` case ${(way?.key ?? '').startsWith("'") ? (way?.key ?? '') : quoted(way?.key ?? '')}: {`);
|
|
268
272
|
if (each.form === 'expression')
|
|
269
|
-
runs.push(` ${name} = ${each.text}`);
|
|
273
|
+
runs.push(unread ? ` void (${each.text})` : ` ${name} = ${each.text}`);
|
|
270
274
|
else if (each.form === 'statements')
|
|
271
|
-
runs.push(...each
|
|
275
|
+
runs.push(...linesOf(each, name, unread).map((line) => ` ${line}`));
|
|
272
276
|
else
|
|
273
|
-
runs.push(` ${name} = await ${asFn(each.whole, shape.all)}(opened)`);
|
|
277
|
+
runs.push(` ${unread ? '' : `${name} = `}await ${asFn(each.whole, shape.all)}(opened)`);
|
|
274
278
|
runs.push(' break');
|
|
275
279
|
runs.push(' }');
|
|
276
280
|
}
|
|
@@ -285,23 +289,24 @@ const SHAPES = {
|
|
|
285
289
|
nd_each: (name, said, shape) => {
|
|
286
290
|
if (said.length !== 2)
|
|
287
291
|
return `takes what to run over and the block to run, and was handed ${String(said.length)} thing(s)`;
|
|
288
|
-
const over = spliced(said[0] ?? '', shape
|
|
289
|
-
const block = spliced(said[1] ?? '', shape
|
|
292
|
+
const over = spliced(said[0] ?? '', shape, ROLES.over);
|
|
293
|
+
const block = spliced(said[1] ?? '', shape, ROLES.item);
|
|
290
294
|
if (typeof block === 'string')
|
|
291
295
|
return `runs a block that ${block}`;
|
|
292
296
|
if (typeof over === 'string')
|
|
293
297
|
return `runs over a block that ${over}`;
|
|
298
|
+
const unread = shape.unread.has(name);
|
|
294
299
|
const item = `${OWN}${name}_item`;
|
|
295
300
|
const all = `${OWN}${name}_all`;
|
|
296
301
|
const each = `${OWN}${name}_each`;
|
|
297
302
|
return {
|
|
298
303
|
runs: [
|
|
299
|
-
`const ${all}: unknown[] = []
|
|
304
|
+
...(unread ? [] : [`const ${all}: unknown[] = []`]),
|
|
300
305
|
`const ${each} = (${block.whole}) as (item: unknown, opened: ${shape.all}) => Promise<unknown>`,
|
|
301
306
|
`for (const ${item} of ${over.form === 'expression' ? `(${over.text})` : `((${over.whole}) as () => readonly unknown[])()`} as readonly unknown[]) {`,
|
|
302
|
-
` ${all}.push(await ${each}(${item}, opened))`,
|
|
307
|
+
unread ? ` await ${each}(${item}, opened)` : ` ${all}.push(await ${each}(${item}, opened))`,
|
|
303
308
|
'}',
|
|
304
|
-
`const ${name}: unknown = ${all}
|
|
309
|
+
...(unread ? [] : [`const ${name}: unknown = ${all}`]),
|
|
305
310
|
],
|
|
306
311
|
};
|
|
307
312
|
},
|
|
@@ -316,17 +321,17 @@ const SHAPES = {
|
|
|
316
321
|
return 'was handed no blocks to run at once';
|
|
317
322
|
const calls = [];
|
|
318
323
|
for (const [order, one] of every.entries()) {
|
|
319
|
-
const each = spliced(one, shape
|
|
324
|
+
const each = spliced(one, shape, ROLES.block);
|
|
320
325
|
if (typeof each === 'string')
|
|
321
326
|
return `runs a block at position ${String(order + 1)} that ${each}`;
|
|
322
327
|
calls.push(each.form === 'expression' ? `(async () => ${each.text})()` : `${asFn(each.whole, shape.all)}(opened)`);
|
|
323
328
|
}
|
|
324
|
-
return { runs: [`const ${name}: unknown = await Promise.all([${calls.join(', ')}])`] };
|
|
329
|
+
return { runs: [`${shape.unread.has(name) ? '' : `const ${name}: unknown = `}await Promise.all([${calls.join(', ')}])`] };
|
|
325
330
|
},
|
|
326
331
|
nd_repeat: (name, said, shape) => {
|
|
327
332
|
if (said.length !== 2)
|
|
328
333
|
return `takes a ceiling and the block to repeat, and was handed ${String(said.length)} thing(s)`;
|
|
329
|
-
const block = spliced(said[1] ?? '', shape
|
|
334
|
+
const block = spliced(said[1] ?? '', shape, ROLES.again);
|
|
330
335
|
if (typeof block === 'string')
|
|
331
336
|
return `repeats a block that ${block}`;
|
|
332
337
|
const ceiling = `${OWN}${name}_ceiling`;
|
|
@@ -335,16 +340,17 @@ const SHAPES = {
|
|
|
335
340
|
const done = `${OWN}${name}_done`;
|
|
336
341
|
const pass = `${OWN}${name}_pass`;
|
|
337
342
|
const answer = `${OWN}${name}_said`;
|
|
343
|
+
const unread = shape.unread.has(name);
|
|
338
344
|
return {
|
|
339
345
|
runs: [
|
|
340
|
-
`let ${name}: unknown
|
|
346
|
+
...(unread ? [] : [`let ${name}: unknown`]),
|
|
341
347
|
`const ${ceiling}: number = ${said[0] ?? ''}`,
|
|
342
348
|
`const ${again} = (${block.whole}) as (pass: number, carried: unknown, opened: ${shape.all}) => Promise<{ readonly done: boolean; readonly value: unknown }>`,
|
|
343
349
|
`let ${carried}: unknown`,
|
|
344
350
|
`let ${done} = false`,
|
|
345
351
|
`for (let ${pass} = 0; ${pass} < ${ceiling}; ${pass} += 1) {`,
|
|
346
352
|
` const ${answer} = await ${again}(${pass}, ${carried}, opened)`,
|
|
347
|
-
` if (${answer}.done) { ${name} = ${answer}.value; ${done} = true; break }`,
|
|
353
|
+
` if (${answer}.done) { ${unread ? '' : `${name} = ${answer}.value; `}${done} = true; break }`,
|
|
348
354
|
` ${carried} = ${answer}.value`,
|
|
349
355
|
'}',
|
|
350
356
|
`if (!${done}) throw new Error(\`${name} reached its ceiling of \${String(${ceiling})} passes without answering done\`)`,
|
|
@@ -357,21 +363,22 @@ const STOPS = "Symbol.for('thread.operation.stopped')";
|
|
|
357
363
|
function plainly(name, said, shape, opened, ends) {
|
|
358
364
|
if (said.length !== 1)
|
|
359
365
|
return `takes one block, and was handed ${String(said.length)} thing(s)`;
|
|
360
|
-
const one = spliced(said[0] ?? '', shape
|
|
366
|
+
const one = spliced(said[0] ?? '', shape, ROLES.block);
|
|
361
367
|
if (typeof one === 'string')
|
|
362
368
|
return one;
|
|
369
|
+
const unread = shape.unread.has(name);
|
|
363
370
|
if (!ends)
|
|
364
|
-
return { runs: answered(name, one, opened, 'const') };
|
|
371
|
+
return { runs: answered(name, one, opened, 'const', unread) };
|
|
365
372
|
return {
|
|
366
373
|
runs: [
|
|
367
|
-
...answered(name, one, opened, 'const'),
|
|
374
|
+
...answered(name, one, opened, 'const', unread),
|
|
368
375
|
`throw Object.assign(new Error(${quoted(`${name} ended the operation`)}), { [${STOPS}]: true, value: ${name} })`,
|
|
369
376
|
],
|
|
370
377
|
};
|
|
371
378
|
}
|
|
372
379
|
const saidIn = (held) => held !== undefined && ts.isStringLiteral(held) ? held.text : undefined;
|
|
373
380
|
const sourceIn = (held) => held?.getText();
|
|
374
|
-
function compiled(at, id, one, known) {
|
|
381
|
+
function compiled(at, id, one, known, unread) {
|
|
375
382
|
const because = problem(id, one);
|
|
376
383
|
if (because !== undefined)
|
|
377
384
|
return { because };
|
|
@@ -394,6 +401,7 @@ function compiled(at, id, one, known) {
|
|
|
394
401
|
const mine = new Set(['input', ...one.nodes.map((row) => String(row.id))]);
|
|
395
402
|
const runs = [];
|
|
396
403
|
const names = new Set();
|
|
404
|
+
const heard = new Set();
|
|
397
405
|
let last;
|
|
398
406
|
for (const [order, row] of one.nodes.entries()) {
|
|
399
407
|
const name = row.id;
|
|
@@ -418,7 +426,7 @@ function compiled(at, id, one, known) {
|
|
|
418
426
|
if (held === undefined) {
|
|
419
427
|
return { because: `the node \`${name}\` states no block this can read in ${row.at} — run \`thread seed\` and write it again` };
|
|
420
428
|
}
|
|
421
|
-
const shape = { all: `Opened<${only}>`, reads: `Reads<Opened<${only}>>`, names, mine };
|
|
429
|
+
const shape = { all: `Opened<${only}>`, reads: `Reads<Opened<${only}>>`, names, mine, heard, unread: unread ?? new Set() };
|
|
422
430
|
const made = (SHAPES[from] ?? ownType)(name, parts(held), shape);
|
|
423
431
|
if (typeof made === 'string')
|
|
424
432
|
return { because: `the node \`${name}\` ${made}` };
|
|
@@ -428,12 +436,17 @@ function compiled(at, id, one, known) {
|
|
|
428
436
|
names.add(name);
|
|
429
437
|
last = from === 'nd_stop' ? undefined : name;
|
|
430
438
|
}
|
|
439
|
+
if (unread === undefined) {
|
|
440
|
+
const final = one.nodes.at(-1)?.id;
|
|
441
|
+
return compiled(at, id, one, known, new Set([...mine].filter((name) => name !== final && !heard.has(name))));
|
|
442
|
+
}
|
|
443
|
+
const typed = ['Opened', 'Reads'].filter((word) => runs.some((line) => line.includes(`${word}<`)));
|
|
431
444
|
return {
|
|
432
445
|
text: [
|
|
433
446
|
"import type { z } from 'zod'",
|
|
434
447
|
'',
|
|
435
448
|
`import type { Operation } from ${quoted(runtime(at, '../operations.ts'))}`,
|
|
436
|
-
|
|
449
|
+
...(typed.length === 0 ? [] : [`import type { ${typed.join(', ')} } from ${quoted(runtime(at, '../operations.ts'))}`]),
|
|
437
450
|
'',
|
|
438
451
|
`import { input, resource } from ${quoted(beside(at, takes))}`,
|
|
439
452
|
'',
|
|
@@ -448,7 +461,7 @@ function compiled(at, id, one, known) {
|
|
|
448
461
|
...(quota === undefined ? [] : [` quota: ${quota},`]),
|
|
449
462
|
'',
|
|
450
463
|
' run: async (opened, held) => {',
|
|
451
|
-
' const input = held.input',
|
|
464
|
+
...(unread.has('input') ? [] : [' const input = held.input']),
|
|
452
465
|
` let ${ENTERED} = ''`,
|
|
453
466
|
' try {',
|
|
454
467
|
...runs,
|