@prestack/thread 0.1.3 → 0.1.5
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 +563 -0
- package/dist/core/actor.d.ts +1 -1
- package/dist/core/actor.js +5 -4
- package/dist/core/actor.ts +5 -4
- package/dist/core/attach.d.ts +0 -7
- package/dist/core/attach.js +38 -67
- package/dist/core/attach.ts +34 -80
- package/dist/core/chain.js +13 -17
- package/dist/core/chain.ts +11 -15
- package/dist/core/condition.d.ts +4 -4
- package/dist/core/condition.js +2 -2
- package/dist/core/descend.d.ts +4 -2
- package/dist/core/descend.js +91 -23
- package/dist/core/descend.ts +90 -21
- package/dist/core/errors/catalogue.d.ts +52 -0
- package/dist/{operations/errors/errors.js → core/errors/catalogue.js} +1 -1
- package/dist/{operations/errors/errors.ts → core/errors/catalogue.ts} +1 -1
- package/dist/{operations/errors/catalog.d.ts → core/errors/error.d.ts} +2 -3
- package/dist/{operations/errors/catalog.js → core/errors/error.js} +1 -16
- package/dist/{operations/errors/catalog.ts → core/errors/error.ts} +3 -19
- package/dist/core/errors/namespaces.d.ts +3 -0
- package/dist/core/errors/namespaces.js +15 -0
- package/dist/core/errors/namespaces.ts +19 -0
- package/dist/core/finding.js +2 -2
- package/dist/core/finding.ts +1 -2
- package/dist/core/init.d.ts +1 -1
- package/dist/core/init.js +51 -5
- package/dist/core/init.ts +58 -5
- package/dist/core/invoked.d.ts +1 -1
- package/dist/core/invoked.js +3 -1
- package/dist/core/invoked.ts +5 -2
- package/dist/core/round.js +36 -22
- package/dist/core/round.ts +26 -9
- package/dist/core/rows.d.ts +4 -0
- package/dist/core/rows.js +31 -2
- package/dist/core/rows.ts +33 -1
- package/dist/core/scope.d.ts +1 -0
- package/dist/core/scope.js +1 -0
- package/dist/core/scope.ts +2 -0
- package/dist/core/thread.d.ts +3 -3
- package/dist/core/tree.d.ts +4 -1
- package/dist/core/tree.js +26 -6
- package/dist/core/tree.ts +29 -6
- package/dist/core/types.d.ts +3 -3
- package/dist/core/types.js +3 -1
- package/dist/core/types.ts +2 -1
- package/dist/derivation/attach.js +3 -2
- package/dist/derivation/attach.ts +2 -1
- package/dist/derivation/generators/build.js +6 -6
- package/dist/derivation/generators/build.ts +1 -1
- package/dist/derivation/generators/check.js +1 -1
- package/dist/derivation/generators/determinism.js +1 -1
- package/dist/derivation/generators/release.js +60 -47
- package/dist/derivation/generators/release.ts +66 -43
- package/dist/derivation/release.d.ts +7 -3
- package/dist/derivation/release.js +14 -1
- package/dist/derivation/release.ts +22 -2
- package/dist/integrations/attach.d.ts +4 -0
- package/dist/integrations/attach.js +37 -56
- package/dist/integrations/attach.ts +54 -60
- package/dist/integrations/cli.d.ts +37 -0
- package/dist/integrations/cli.js +440 -0
- package/dist/integrations/cli.ts +543 -0
- package/dist/{core/files.d.ts → integrations/disk.d.ts} +16 -24
- package/dist/integrations/disk.js +435 -0
- package/dist/integrations/disk.ts +465 -0
- package/dist/integrations/generators/read.js +6 -6
- package/dist/integrations/generators/read.ts +4 -4
- package/dist/integrations/generators/vault.js +10 -11
- package/dist/integrations/generators/vault.ts +4 -5
- package/dist/{operations/http.d.ts → integrations/https.d.ts} +10 -6
- package/dist/integrations/https.js +170 -0
- package/dist/integrations/https.ts +216 -0
- package/dist/integrations/pglite.d.ts +26 -0
- package/dist/integrations/pglite.js +143 -0
- package/dist/integrations/pglite.ts +206 -0
- package/dist/integrations/secret.d.ts +8 -2
- package/dist/integrations/secret.js +3 -5
- package/dist/integrations/secret.ts +10 -6
- package/dist/integrations/target.d.ts +155 -14
- package/dist/integrations/target.js +171 -50
- package/dist/integrations/target.ts +377 -61
- package/dist/integrations/vault.d.ts +1 -0
- package/dist/integrations/vault.js +20 -5
- package/dist/integrations/vault.ts +17 -5
- package/dist/integrity/attach.js +8 -77
- package/dist/integrity/attach.ts +7 -76
- package/dist/integrity/check-threads.js +5 -5
- package/dist/integrity/check-threads.ts +2 -2
- package/dist/integrity/constraint.d.ts +0 -1
- package/dist/integrity/constraint.js +267 -366
- package/dist/integrity/constraint.ts +267 -375
- package/dist/integrity/generators/compiler.js +14 -17
- package/dist/integrity/generators/compiler.ts +10 -15
- package/dist/integrity/generators/gate.js +15 -15
- package/dist/integrity/generators/gate.ts +10 -10
- package/dist/integrity/generators/issues.js +7 -7
- package/dist/integrity/generators/publish.js +39 -57
- package/dist/integrity/generators/publish.ts +44 -55
- package/dist/integrity/generators/trial.d.ts +3 -3
- package/dist/integrity/generators/trial.js +133 -165
- package/dist/integrity/generators/trial.ts +145 -169
- package/dist/integrity/lint.js +9 -13
- package/dist/integrity/lint.ts +7 -10
- package/dist/integrity/lsp/bin.js +5 -4
- package/dist/integrity/lsp/bin.ts +3 -1
- package/dist/integrity/lsp/diagnostics.js +3 -3
- package/dist/integrity/lsp/diagnostics.ts +1 -1
- package/dist/integrity/lsp/lint.js +19 -30
- package/dist/integrity/lsp/lint.ts +16 -32
- package/dist/integrity/lsp/server.js +3 -3
- package/dist/integrity/slopnator.d.ts +3 -13
- package/dist/integrity/slopnator.js +59 -38
- package/dist/integrity/slopnator.ts +54 -38
- package/dist/integrity/slopnators/sl_broken_lineage.js +2 -2
- package/dist/integrity/slopnators/sl_declared_elsewhere.js +1 -1
- package/dist/integrity/slopnators/sl_derived_drift.js +4 -4
- package/dist/integrity/slopnators/sl_derived_drift.ts +1 -1
- package/dist/integrity/slopnators/sl_door_in_a_node.js +220 -0
- package/dist/integrity/slopnators/sl_door_in_a_node.ts +250 -0
- package/dist/integrity/slopnators/sl_inert_island.js +10 -8
- package/dist/integrity/slopnators/sl_inert_island.ts +8 -6
- package/dist/integrity/slopnators/sl_missing_row.js +6 -51
- package/dist/integrity/slopnators/sl_missing_row.ts +4 -50
- package/dist/integrity/slopnators/sl_no_barrel.js +1 -1
- package/dist/integrity/slopnators/sl_no_child_process.js +36 -18
- package/dist/integrity/slopnators/sl_no_child_process.ts +44 -18
- package/dist/integrity/slopnators/sl_no_dispatch_bypass.js +80 -149
- package/dist/integrity/slopnators/sl_no_dispatch_bypass.ts +95 -147
- package/dist/integrity/slopnators/sl_no_import_cycle.js +15 -4
- package/dist/integrity/slopnators/sl_no_import_cycle.ts +20 -4
- package/dist/integrity/slopnators/sl_no_such_command.js +2 -2
- package/dist/integrity/slopnators/sl_no_such_command.ts +1 -1
- package/dist/integrity/slopnators/sl_outside_an_operation.js +20 -0
- package/dist/integrity/slopnators/sl_outside_an_operation.ts +25 -0
- package/dist/integrity/slopnators/sl_screen_holds_only_declared.d.ts +2 -0
- package/dist/integrity/slopnators/sl_screen_holds_only_declared.js +61 -0
- package/dist/integrity/slopnators/sl_screen_holds_only_declared.ts +70 -0
- package/dist/integrity/slopnators/sl_secret_literal.js +1 -33
- package/dist/integrity/slopnators/sl_secret_literal.ts +1 -33
- package/dist/integrity/slopnators/sl_stale_path.js +5 -22
- package/dist/integrity/slopnators/sl_stale_path.ts +4 -20
- package/dist/integrity/slopnators/sl_twice_meant.js +1 -1
- package/dist/integrity/slopnators/sl_twice_parented.js +29 -5
- package/dist/integrity/slopnators/sl_twice_parented.ts +28 -4
- package/dist/integrity/slopnators/sl_undeclared_crossing.d.ts +4 -1
- package/dist/integrity/slopnators/sl_undeclared_crossing.js +426 -62
- package/dist/integrity/slopnators/sl_undeclared_crossing.ts +454 -70
- package/dist/integrity/slopnators/sl_undeclared_destination.js +59 -25
- package/dist/integrity/slopnators/sl_undeclared_destination.ts +69 -21
- package/dist/integrity/slopnators/sl_undeclared_list.d.ts +2 -0
- package/dist/integrity/slopnators/sl_undeclared_list.js +363 -0
- package/dist/integrity/slopnators/sl_undeclared_list.ts +358 -0
- package/dist/integrity/slopnators/sl_undeclared_node.js +1 -11
- package/dist/integrity/slopnators/sl_undeclared_node.ts +0 -10
- package/dist/integrity/slopnators/sl_undeclared_operation.js +111 -29
- package/dist/integrity/slopnators/sl_undeclared_operation.ts +134 -26
- package/dist/integrity/slopnators/sl_undeclared_variant.js +2 -2
- package/dist/integrity/slopnators/sl_undeclared_webhook.d.ts +2 -0
- package/dist/integrity/slopnators/sl_undeclared_webhook.js +90 -0
- package/dist/integrity/slopnators/sl_undeclared_webhook.ts +108 -0
- package/dist/integrity/slopnators/sl_underived_route.d.ts +2 -0
- package/dist/integrity/slopnators/sl_underived_route.js +61 -0
- package/dist/integrity/slopnators/sl_underived_route.ts +72 -0
- package/dist/integrity/slopnators/sl_ungoverned_island.js +3 -3
- package/dist/integrity/slopnators/sl_ungoverned_island.ts +1 -1
- package/dist/integrity/slopnators/sl_unlisted_program.d.ts +2 -0
- package/dist/integrity/slopnators/sl_unlisted_program.js +39 -0
- package/dist/integrity/slopnators/sl_unlisted_program.ts +40 -0
- package/dist/integrity/slopnators/sl_unnamed_island.js +1 -1
- package/dist/integrity/slopnators/sl_unpaired_member.js +3 -3
- package/dist/integrity/slopnators/sl_unpaired_member.ts +1 -1
- package/dist/integrity/slopnators/sl_unpaired_slopnator.js +4 -4
- package/dist/integrity/slopnators/sl_unpaired_slopnator.ts +1 -1
- package/dist/integrity/slopnators/sl_unprefixed_member.js +5 -31
- package/dist/integrity/slopnators/sl_unprefixed_member.ts +2 -28
- package/dist/integrity/slopnators/sl_unthreaded.js +11 -7
- package/dist/integrity/slopnators/sl_unthreaded.ts +6 -7
- package/dist/integrity/slopnators/sl_untranslated_key.js +2 -25
- package/dist/integrity/slopnators/sl_untranslated_key.ts +1 -25
- package/dist/integrity/slopnators/sl_unvaulted_secret.d.ts +2 -0
- package/dist/integrity/slopnators/sl_unvaulted_secret.js +107 -0
- package/dist/integrity/slopnators/sl_unvaulted_secret.ts +132 -0
- package/dist/integrity/slopnators/sl_unversioned.js +6 -4
- package/dist/integrity/slopnators/sl_unversioned.ts +5 -3
- package/dist/integrity/sweep.d.ts +1 -0
- package/dist/integrity/sweep.js +105 -42
- package/dist/integrity/sweep.ts +115 -42
- package/dist/knowledge/attach.js +4 -3
- package/dist/knowledge/attach.ts +3 -2
- package/dist/knowledge/generators/docsgen.js +8 -6
- package/dist/knowledge/generators/docsgen.ts +4 -2
- package/dist/knowledge/generators/glossary.js +4 -4
- package/dist/knowledge/generators/glossary.ts +1 -1
- package/dist/operations/access.d.ts +23 -0
- package/dist/operations/access.js +28 -1
- package/dist/operations/access.ts +43 -0
- package/dist/operations/attach.js +33 -113
- package/dist/operations/attach.ts +51 -117
- package/dist/operations/capabilities.d.ts +14 -5
- package/dist/operations/capabilities.js +13 -4
- package/dist/operations/capabilities.ts +27 -8
- package/dist/operations/dispatch.d.ts +8 -2
- package/dist/operations/dispatch.js +128 -185
- package/dist/operations/dispatch.ts +138 -191
- package/dist/operations/error-results.d.ts +1 -1
- package/dist/operations/error-results.js +2 -2
- package/dist/operations/error-results.ts +2 -2
- package/dist/operations/generators/capability.d.ts +11 -1
- package/dist/operations/generators/capability.js +481 -224
- package/dist/operations/generators/capability.ts +562 -233
- package/dist/operations/generators/compile.js +66 -39
- package/dist/operations/generators/compile.ts +78 -39
- package/dist/operations/generators/permits.js +11 -12
- package/dist/operations/generators/permits.ts +3 -4
- package/dist/operations/generators/seed.d.ts +1 -0
- package/dist/operations/generators/seed.js +19 -17
- package/dist/operations/generators/seed.ts +22 -14
- package/dist/operations/generators/trial.d.ts +6 -8
- package/dist/operations/generators/trial.js +200 -70
- package/dist/operations/generators/trial.ts +230 -93
- package/dist/operations/nodes.d.ts +15 -16
- package/dist/operations/nodes.ts +16 -17
- package/dist/operations/operations.d.ts +40 -4
- package/dist/operations/operations.js +1 -0
- package/dist/operations/operations.ts +61 -4
- package/dist/operations/result.d.ts +2 -2
- package/dist/operations/result.ts +2 -2
- package/package.json +1 -4
- package/dist/core/answers.d.ts +0 -171
- package/dist/core/answers.js +0 -789
- package/dist/core/answers.ts +0 -999
- package/dist/core/client.d.ts +0 -63
- package/dist/core/client.js +0 -249
- package/dist/core/client.ts +0 -289
- package/dist/core/files.js +0 -171
- package/dist/core/files.ts +0 -222
- package/dist/integrations/module.d.ts +0 -6
- package/dist/integrations/module.js +0 -153
- package/dist/integrations/module.ts +0 -191
- package/dist/integrity/slopnators/sl_unkept_rows.js +0 -104
- package/dist/integrity/slopnators/sl_unkept_rows.ts +0 -111
- package/dist/integrity/slopnators/sl_unseamed_reach.js +0 -73
- package/dist/integrity/slopnators/sl_unseamed_reach.ts +0 -79
- package/dist/operations/cost.d.ts +0 -14
- package/dist/operations/cost.js +0 -37
- package/dist/operations/cost.ts +0 -58
- package/dist/operations/errors/errors.d.ts +0 -52
- package/dist/operations/http.js +0 -124
- package/dist/operations/http.ts +0 -169
- package/dist/operations/memory.d.ts +0 -104
- package/dist/operations/memory.js +0 -365
- package/dist/operations/memory.ts +0 -470
- package/dist/operations/metering.d.ts +0 -55
- package/dist/operations/metering.js +0 -247
- package/dist/operations/metering.ts +0 -328
- package/dist/operations/queue-policy.d.ts +0 -3
- package/dist/operations/queue-policy.js +0 -8
- package/dist/operations/queue-policy.ts +0 -10
- package/dist/operations/seams.d.ts +0 -239
- package/dist/operations/seams.js +0 -45
- package/dist/operations/seams.ts +0 -364
- package/dist/operations/store.d.ts +0 -14
- package/dist/operations/store.js +0 -1
- package/dist/operations/store.ts +0 -29
- /package/dist/{operations → core}/errors/classes.d.ts +0 -0
- /package/dist/{operations → core}/errors/classes.js +0 -0
- /package/dist/{operations → core}/errors/classes.ts +0 -0
- /package/dist/integrity/slopnators/{sl_unkept_rows.d.ts → sl_door_in_a_node.d.ts} +0 -0
- /package/dist/integrity/slopnators/{sl_unseamed_reach.d.ts → sl_outside_an_operation.d.ts} +0 -0
|
@@ -1,16 +1,17 @@
|
|
|
1
|
-
import { existsSync, readFileSync, writeFileSync } from
|
|
1
|
+
import { existsSync, readFileSync, writeFileSync } from '../../integrations/disk.js';
|
|
2
2
|
import { dirname, relative, resolve } from 'node:path';
|
|
3
3
|
import { argv, stdout } from 'node:process';
|
|
4
4
|
import { fileURLToPath } from 'node:url';
|
|
5
|
-
import
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
5
|
+
import ts from 'typescript';
|
|
6
|
+
import { ROOT, descend, under } from '../../core/descend.js';
|
|
7
|
+
import { runChecker } from '../../core/finding.js';
|
|
8
|
+
import { exit, invoked } from '../../core/invoked.js';
|
|
9
|
+
import { PREFIXES } from '../../core/thread.js';
|
|
9
10
|
const COMPILES = 'thread compile';
|
|
10
11
|
const OPERATIONS_ID = 'operations';
|
|
11
12
|
const NODES_ID = 'nodes';
|
|
12
13
|
const NAMED = /\btype:\s*'((?:[^'\\]|\\.)*)'/;
|
|
13
|
-
import { quoted } from
|
|
14
|
+
import { quoted } from '../../core/rows.js';
|
|
14
15
|
export { quoted };
|
|
15
16
|
const slash = (one) => one.split('\\').join('/');
|
|
16
17
|
export const beside = (from, to) => {
|
|
@@ -30,6 +31,7 @@ const FRESH = {
|
|
|
30
31
|
const SHIPPED = new Set(Object.keys(FRESH));
|
|
31
32
|
const isType = (pass, id) => under(pass, id, NODES_ID);
|
|
32
33
|
const isOurs = (pass, id) => under(pass, id, OPERATIONS_ID);
|
|
34
|
+
const isOperation = (pass, id) => Object.values(PREFIXES).some((one) => id.startsWith(one)) && isOurs(pass, id);
|
|
33
35
|
export function types(pass) {
|
|
34
36
|
const found = new Set(SHIPPED);
|
|
35
37
|
const named = new Set();
|
|
@@ -39,7 +41,7 @@ export function types(pass) {
|
|
|
39
41
|
named.add(one.row.id);
|
|
40
42
|
}
|
|
41
43
|
for (const one of pass.coined.keys()) {
|
|
42
|
-
if (one !== NODES_ID && !
|
|
44
|
+
if (one !== NODES_ID && !named.has(one) && under(pass, one, NODES_ID))
|
|
43
45
|
found.add(one);
|
|
44
46
|
}
|
|
45
47
|
return found;
|
|
@@ -49,6 +51,7 @@ export function builtFrom(row, known) {
|
|
|
49
51
|
return found.length === 1 ? found[0] : undefined;
|
|
50
52
|
}
|
|
51
53
|
const reaches = (pass, one) => pass.stated.has(one) && !under(pass, one, NODES_ID);
|
|
54
|
+
const waiting = (held) => held !== undefined && ts.isNumericLiteral(held) ? Number(held.text.replace(/_/g, '')) : undefined;
|
|
52
55
|
export function operations(pass, known) {
|
|
53
56
|
const found = new Map();
|
|
54
57
|
const held = (id) => {
|
|
@@ -62,15 +65,16 @@ export function operations(pass, known) {
|
|
|
62
65
|
takes: [],
|
|
63
66
|
body: undefined,
|
|
64
67
|
wants: new Set(stated?.takes ?? []),
|
|
65
|
-
requires: asked.filter((one) =>
|
|
66
|
-
stray: asked.filter((one) => !
|
|
68
|
+
requires: asked.filter((one) => reaches(pass, one)),
|
|
69
|
+
stray: asked.filter((one) => !reaches(pass, one)),
|
|
70
|
+
timeout: waiting(stated?.fields.get('timeout')),
|
|
67
71
|
};
|
|
68
72
|
found.set(id, made);
|
|
69
73
|
return made;
|
|
70
74
|
};
|
|
71
75
|
for (const [at, threadings] of pass.threaded) {
|
|
72
76
|
for (const one of threadings) {
|
|
73
|
-
if (
|
|
77
|
+
if (!isOperation(pass, one.by))
|
|
74
78
|
continue;
|
|
75
79
|
if (one.list === 'derives' && one.row.by === COMPILES)
|
|
76
80
|
held(one.by).body = at;
|
|
@@ -89,14 +93,14 @@ export const problem = (id, one) => {
|
|
|
89
93
|
if (one.nodes.length === 0)
|
|
90
94
|
return `\`${id}\` is an operation that seeds no nodes, and an operation is its nodes`;
|
|
91
95
|
if (one.takes.length === 0) {
|
|
92
|
-
return (`\`${id}\` names nothing it takes, and an operation takes one thing — its input
|
|
96
|
+
return (`\`${id}\` names nothing it takes, and an operation takes one thing — its input and the row it touches. ` +
|
|
93
97
|
`Write \`takes: ['${id}_takes']\` on its row beside the file that row seeds`);
|
|
94
98
|
}
|
|
95
99
|
if (one.takes.length > 1)
|
|
96
100
|
return `\`${id}\` takes ${String(one.takes.length)} things, and it takes one`;
|
|
97
101
|
if (one.stray.length > 0) {
|
|
98
102
|
return (`\`${id}\` requires ${one.stray.join(', ')}, and dispatch can supply neither a capability nor a target by that ` +
|
|
99
|
-
`name. What an operation may require is
|
|
103
|
+
`name. What an operation may require is a target this app declares — ` +
|
|
100
104
|
'declare one with `thread create target`');
|
|
101
105
|
}
|
|
102
106
|
return undefined;
|
|
@@ -104,12 +108,10 @@ export const problem = (id, one) => {
|
|
|
104
108
|
const takesFile = (at) => [
|
|
105
109
|
"import { z } from 'zod'",
|
|
106
110
|
'',
|
|
107
|
-
|
|
111
|
+
'import type { Row } from ' + quoted(runtime(at, '../../integrations/target.ts')),
|
|
108
112
|
'',
|
|
109
113
|
'export const input = z.unknown()',
|
|
110
114
|
'',
|
|
111
|
-
'export const estimate = 1',
|
|
112
|
-
'',
|
|
113
115
|
'export const resource = (): Row | null => null',
|
|
114
116
|
'',
|
|
115
117
|
'export type In = z.infer<typeof input>',
|
|
@@ -147,9 +149,9 @@ export function nodeText(at, name, takes, from, requires, body) {
|
|
|
147
149
|
const nodeFile = (at, row, takes, from, requires, body) => nodeText(at, row.id ?? '', takes, from, requires, body);
|
|
148
150
|
const ownType = (at, id) => [
|
|
149
151
|
`import type { Held } from ${quoted(runtime(at, '../nodes.ts'))}`,
|
|
150
|
-
|
|
152
|
+
'import type { Opened } from ' + quoted(runtime(at, '../operations.ts')),
|
|
151
153
|
'',
|
|
152
|
-
'export const node = <I, R extends
|
|
154
|
+
'export const node = <I, R extends string = never>(block: (opened: Opened<R>, held: Held<I>) => Promise<unknown>) => ({',
|
|
153
155
|
` type: ${quoted(id)} as const,`,
|
|
154
156
|
'})',
|
|
155
157
|
'',
|
|
@@ -1,13 +1,14 @@
|
|
|
1
|
-
import { existsSync, readFileSync, writeFileSync } from '../../
|
|
1
|
+
import { existsSync, readFileSync, writeFileSync } from '../../integrations/disk.ts'
|
|
2
2
|
import { dirname, relative, resolve } from 'node:path'
|
|
3
3
|
import { argv, stdout } from 'node:process'
|
|
4
4
|
import { fileURLToPath } from 'node:url'
|
|
5
5
|
|
|
6
|
+
import ts from 'typescript'
|
|
7
|
+
|
|
6
8
|
import { ROOT, descend, under, type Pass, type Row } from '../../core/descend.ts'
|
|
7
9
|
import { runChecker, type Finding } from '../../core/finding.ts'
|
|
8
10
|
import { exit, invoked } from '../../core/invoked.ts'
|
|
9
|
-
|
|
10
|
-
import { SUPPLIES, supplies, type Supplied } from '../seams.ts'
|
|
11
|
+
import { PREFIXES } from '../../core/thread.ts'
|
|
11
12
|
|
|
12
13
|
const COMPILES = 'thread compile'
|
|
13
14
|
|
|
@@ -47,6 +48,9 @@ const isType = (pass: Pass, id: string): boolean => under(pass, id, NODES_ID)
|
|
|
47
48
|
|
|
48
49
|
const isOurs = (pass: Pass, id: string): boolean => under(pass, id, OPERATIONS_ID)
|
|
49
50
|
|
|
51
|
+
const isOperation = (pass: Pass, id: string): boolean =>
|
|
52
|
+
Object.values(PREFIXES).some((one) => id.startsWith(one)) && isOurs(pass, id)
|
|
53
|
+
|
|
50
54
|
export function types(pass: Pass): ReadonlySet<string> {
|
|
51
55
|
const found = new Set<string>(SHIPPED)
|
|
52
56
|
const named = new Set<string>()
|
|
@@ -55,7 +59,7 @@ export function types(pass: Pass): ReadonlySet<string> {
|
|
|
55
59
|
}
|
|
56
60
|
|
|
57
61
|
for (const one of pass.coined.keys()) {
|
|
58
|
-
if (one !== NODES_ID && !
|
|
62
|
+
if (one !== NODES_ID && !named.has(one) && under(pass, one, NODES_ID)) found.add(one)
|
|
59
63
|
}
|
|
60
64
|
return found
|
|
61
65
|
}
|
|
@@ -72,10 +76,15 @@ interface Woven {
|
|
|
72
76
|
|
|
73
77
|
readonly requires: readonly string[]
|
|
74
78
|
readonly stray: readonly string[]
|
|
79
|
+
|
|
80
|
+
readonly timeout?: number
|
|
75
81
|
}
|
|
76
82
|
|
|
77
83
|
const reaches = (pass: Pass, one: string): boolean => pass.stated.has(one) && !under(pass, one, NODES_ID)
|
|
78
84
|
|
|
85
|
+
const waiting = (held: ts.Expression | undefined): number | undefined =>
|
|
86
|
+
held !== undefined && ts.isNumericLiteral(held) ? Number(held.text.replace(/_/g, '')) : undefined
|
|
87
|
+
|
|
79
88
|
export function operations(pass: Pass, known: ReadonlySet<string>): ReadonlyMap<string, Woven> {
|
|
80
89
|
const found = new Map<string, Woven & { nodes: Row[]; takes: Row[]; body?: string; wants: ReadonlySet<string> }>()
|
|
81
90
|
const held = (id: string): { nodes: Row[]; takes: Row[]; body?: string; wants: ReadonlySet<string> } => {
|
|
@@ -88,8 +97,9 @@ export function operations(pass: Pass, known: ReadonlySet<string>): ReadonlyMap<
|
|
|
88
97
|
takes: [] as Row[],
|
|
89
98
|
body: undefined as string | undefined,
|
|
90
99
|
wants: new Set(stated?.takes ?? []),
|
|
91
|
-
requires: asked.filter((one) =>
|
|
92
|
-
stray: asked.filter((one) => !
|
|
100
|
+
requires: asked.filter((one) => reaches(pass, one)),
|
|
101
|
+
stray: asked.filter((one) => !reaches(pass, one)),
|
|
102
|
+
timeout: waiting(stated?.fields.get('timeout')),
|
|
93
103
|
}
|
|
94
104
|
found.set(id, made)
|
|
95
105
|
return made
|
|
@@ -97,7 +107,7 @@ export function operations(pass: Pass, known: ReadonlySet<string>): ReadonlyMap<
|
|
|
97
107
|
|
|
98
108
|
for (const [at, threadings] of pass.threaded) {
|
|
99
109
|
for (const one of threadings) {
|
|
100
|
-
if (
|
|
110
|
+
if (!isOperation(pass, one.by)) continue
|
|
101
111
|
|
|
102
112
|
if (one.list === 'derives' && one.row.by === COMPILES) held(one.by).body = at
|
|
103
113
|
else if (one.list === 'seeds') {
|
|
@@ -114,7 +124,7 @@ export const problem = (id: string, one: Woven): string | undefined => {
|
|
|
114
124
|
if (one.nodes.length === 0) return `\`${id}\` is an operation that seeds no nodes, and an operation is its nodes`
|
|
115
125
|
if (one.takes.length === 0) {
|
|
116
126
|
return (
|
|
117
|
-
`\`${id}\` names nothing it takes, and an operation takes one thing — its input
|
|
127
|
+
`\`${id}\` names nothing it takes, and an operation takes one thing — its input and the row it touches. ` +
|
|
118
128
|
`Write \`takes: ['${id}_takes']\` on its row beside the file that row seeds`
|
|
119
129
|
)
|
|
120
130
|
}
|
|
@@ -122,7 +132,7 @@ export const problem = (id: string, one: Woven): string | undefined => {
|
|
|
122
132
|
if (one.stray.length > 0) {
|
|
123
133
|
return (
|
|
124
134
|
`\`${id}\` requires ${one.stray.join(', ')}, and dispatch can supply neither a capability nor a target by that ` +
|
|
125
|
-
`name. What an operation may require is
|
|
135
|
+
`name. What an operation may require is a target this app declares — ` +
|
|
126
136
|
'declare one with `thread create target`'
|
|
127
137
|
)
|
|
128
138
|
}
|
|
@@ -133,12 +143,10 @@ const takesFile = (at: string): string =>
|
|
|
133
143
|
[
|
|
134
144
|
"import { z } from 'zod'",
|
|
135
145
|
'',
|
|
136
|
-
|
|
146
|
+
'import type { Row } from ' + quoted(runtime(at, '../../integrations/target.ts')),
|
|
137
147
|
'',
|
|
138
148
|
'export const input = z.unknown()',
|
|
139
149
|
'',
|
|
140
|
-
'export const estimate = 1',
|
|
141
|
-
'',
|
|
142
150
|
'export const resource = (): Row | null => null',
|
|
143
151
|
'',
|
|
144
152
|
'export type In = z.infer<typeof input>',
|
|
@@ -191,9 +199,9 @@ const nodeFile = (at: string, row: Row, takes: string, from: string, requires: r
|
|
|
191
199
|
const ownType = (at: string, id: string): string =>
|
|
192
200
|
[
|
|
193
201
|
`import type { Held } from ${quoted(runtime(at, '../nodes.ts'))}`,
|
|
194
|
-
|
|
202
|
+
'import type { Opened } from ' + quoted(runtime(at, '../operations.ts')),
|
|
195
203
|
'',
|
|
196
|
-
'export const node = <I, R extends
|
|
204
|
+
'export const node = <I, R extends string = never>(block: (opened: Opened<R>, held: Held<I>) => Promise<unknown>) => ({',
|
|
197
205
|
` type: ${quoted(id)} as const,`,
|
|
198
206
|
'})',
|
|
199
207
|
'',
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { type Actor } from '../../core/actor.ts';
|
|
2
|
-
import { type
|
|
2
|
+
import { type Call, type Row, type Target } from '../../integrations/target.ts';
|
|
3
|
+
import { type Scripted } from '../../integrations/https.ts';
|
|
3
4
|
import { TRIALS_AT, type Operation } from '../operations.ts';
|
|
4
5
|
import type { Result } from '../result.ts';
|
|
5
|
-
import type {
|
|
6
|
+
import type { Vault } from '../../integrations/secret.ts';
|
|
6
7
|
export { TRIALS_AT };
|
|
7
8
|
export interface Scenario {
|
|
8
9
|
readonly id: string;
|
|
@@ -13,8 +14,9 @@ export interface Scenario {
|
|
|
13
14
|
readonly entity: string;
|
|
14
15
|
readonly row: Row;
|
|
15
16
|
}[];
|
|
16
|
-
readonly answers?: Readonly<Record<string,
|
|
17
|
+
readonly answers?: Readonly<Record<string, Scripted>>;
|
|
17
18
|
readonly views?: Readonly<Record<string, readonly unknown[]>>;
|
|
19
|
+
readonly files?: Readonly<Record<string, string>>;
|
|
18
20
|
}
|
|
19
21
|
export interface Trial {
|
|
20
22
|
readonly operation: string;
|
|
@@ -23,7 +25,6 @@ export interface Trial {
|
|
|
23
25
|
readonly measured: boolean;
|
|
24
26
|
readonly result: unknown;
|
|
25
27
|
readonly kept: unknown;
|
|
26
|
-
readonly observed: unknown;
|
|
27
28
|
readonly called: unknown;
|
|
28
29
|
}
|
|
29
30
|
export interface Outcome {
|
|
@@ -40,10 +41,7 @@ export interface Run {
|
|
|
40
41
|
readonly failed: number;
|
|
41
42
|
readonly measured: number;
|
|
42
43
|
}
|
|
43
|
-
export declare function runScenario(one: Operation<unknown>, scenario: Scenario, dispatch: (operation: Operation<unknown>, input: unknown, actor: Actor,
|
|
44
|
-
readonly entities: ReadonlyMap<string, ReadonlySet<string>>;
|
|
45
|
-
readonly keys: ReadonlyMap<string, string>;
|
|
46
|
-
}): Promise<Trial>;
|
|
44
|
+
export declare function runScenario(one: Operation<unknown>, scenario: Scenario, dispatch: (operation: Operation<unknown>, input: unknown, actor: Actor, opened: Readonly<Record<string, unknown>>, opening: Vault) => Promise<Result<unknown>>, stood?: Readonly<Record<string, string>>, targets?: readonly Target[], calls?: readonly Call[]): Promise<Trial | string>;
|
|
47
45
|
export declare function scenariosFor(one: Operation<unknown>, takes: string, granted?: readonly string[]): Promise<readonly Scenario[] | string>;
|
|
48
46
|
export declare function compared(was: readonly Trial[], now: readonly Trial[]): Run;
|
|
49
47
|
export declare function unproven(run: Run): readonly string[];
|
|
@@ -6,21 +6,21 @@ var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExte
|
|
|
6
6
|
}
|
|
7
7
|
return path;
|
|
8
8
|
};
|
|
9
|
-
import { existsSync, readFileSync, writeFileSync } from
|
|
9
|
+
import { existsSync, readFileSync, writeFileSync } from '../../integrations/disk.js';
|
|
10
10
|
import { resolve } from 'node:path';
|
|
11
11
|
import { argv, stdout } from 'node:process';
|
|
12
12
|
import { pathToFileURL } from 'node:url';
|
|
13
|
-
import { ANONYMOUS } from
|
|
14
|
-
import { ROOT,
|
|
15
|
-
import { exit, invoked } from
|
|
16
|
-
import {
|
|
17
|
-
import {
|
|
18
|
-
import {
|
|
19
|
-
import {
|
|
20
|
-
import {
|
|
13
|
+
import { ANONYMOUS } from '../../core/actor.js';
|
|
14
|
+
import { ROOT, descend } from '../../core/descend.js';
|
|
15
|
+
import { exit, invoked } from '../../core/invoked.js';
|
|
16
|
+
import { reachOf, standsHere } from '../../integrations/target.js';
|
|
17
|
+
import { recordingHttp } from '../../integrations/https.js';
|
|
18
|
+
import { standing } from '../../integrations/pglite.js';
|
|
19
|
+
import { TRIALS_AT } from '../operations.js';
|
|
20
|
+
import { PERMITS_AT } from '../access.js';
|
|
21
|
+
import { operations, types } from './seed.js';
|
|
21
22
|
export { TRIALS_AT };
|
|
22
23
|
const DISPATCH_AT = 'operations/_dispatch.ts';
|
|
23
|
-
const ENTITIES_AT = `${TREE}entities.ts`;
|
|
24
24
|
const ACCESS_AT = '_threads/access.ts';
|
|
25
25
|
const WHEN = 1_767_225_600_000;
|
|
26
26
|
const compare = (a, b) => (a < b ? -1 : a > b ? 1 : 0);
|
|
@@ -51,68 +51,191 @@ const asking = (scenario) => ({
|
|
|
51
51
|
...(scenario.actor ?? ANONYMOUS),
|
|
52
52
|
run: 'trial',
|
|
53
53
|
replay: `trial_${scenario.id}`,
|
|
54
|
+
moment: new Date(WHEN).toISOString(),
|
|
54
55
|
});
|
|
55
|
-
function
|
|
56
|
-
const made =
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
56
|
+
function programmed(word, names) {
|
|
57
|
+
const made = {};
|
|
58
|
+
for (const order of names) {
|
|
59
|
+
made[order] = () => {
|
|
60
|
+
throw new Error(word + ' was asked to run ' + order + ', and this scenario says nothing about what that did. A trial stands a ' +
|
|
61
|
+
'disk up out of the files its scenario states, and there is nowhere yet to state what a program or a package answered, so ' +
|
|
62
|
+
'a scenario cannot give either an order. This says that rather than inventing a result for one.');
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
return made;
|
|
66
|
+
}
|
|
67
|
+
function packaged(word, names, script, into) {
|
|
68
|
+
const spent = new Map();
|
|
69
|
+
const made = {};
|
|
70
|
+
for (const name of names) {
|
|
71
|
+
made[name] = async (given) => {
|
|
72
|
+
const key = word + ' ' + name;
|
|
73
|
+
into.push({ target: word, method: 'CALL', path: name, headers: {}, body: JSON.stringify(stable(given ?? null)) });
|
|
74
|
+
const held = script[key];
|
|
75
|
+
if (held === undefined) {
|
|
76
|
+
throw new Error('this scenario scripts no answer for "' + key + '", so nothing here knows what ' + word + ' would hand back. Add ' +
|
|
77
|
+
'that key to the scenario\'s answers, with what the call returns written as JSON in its body.');
|
|
78
|
+
}
|
|
79
|
+
const each = Array.isArray(held) ? held : undefined;
|
|
80
|
+
const at = spent.get(key) ?? 0;
|
|
81
|
+
spent.set(key, at + 1);
|
|
82
|
+
const answer = each === undefined ? held : each[at];
|
|
83
|
+
if (answer === undefined) {
|
|
84
|
+
throw new Error('this scenario scripts ' + String(each?.length ?? 0) + ' answer(s) for "' + key + '" and it has now been called ' +
|
|
85
|
+
String(at + 1) + ' time(s). A node that calls the same thing again needs one answer per call, in the order they come back.');
|
|
86
|
+
}
|
|
87
|
+
if (answer.status !== 200)
|
|
88
|
+
throw new Error(answer.body ?? word + ' ' + name + ' failed with ' + String(answer.status));
|
|
89
|
+
return answer.body === undefined ? undefined : JSON.parse(answer.body);
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
return made;
|
|
93
|
+
}
|
|
94
|
+
function disked(files) {
|
|
95
|
+
const held = new Map(Object.entries(files));
|
|
96
|
+
const under = (at) => [...held.keys()].some((one) => one.startsWith(at + '/'));
|
|
97
|
+
const gone = (at) => new Error(at + ' is not in this scenario, and a trial answers a file order against the files its scenario states');
|
|
98
|
+
return {
|
|
99
|
+
kind: (at) => (held.has(at) ? 'file' : under(at) ? 'folder' : 'none'),
|
|
100
|
+
read: (at) => {
|
|
101
|
+
const said = held.get(at);
|
|
102
|
+
if (said === undefined)
|
|
103
|
+
throw gone(at);
|
|
104
|
+
return said;
|
|
105
|
+
},
|
|
106
|
+
write: (at, said) => {
|
|
107
|
+
held.set(at, said);
|
|
108
|
+
},
|
|
109
|
+
list: (at) => {
|
|
110
|
+
const inside = new Map();
|
|
111
|
+
for (const one of held.keys()) {
|
|
112
|
+
if (!one.startsWith(at + '/'))
|
|
113
|
+
continue;
|
|
114
|
+
const rest = one.slice(at.length + 1);
|
|
115
|
+
const cut = rest.indexOf('/');
|
|
116
|
+
const name = cut === -1 ? rest : rest.slice(0, cut);
|
|
117
|
+
inside.set(name, { name, kind: cut === -1 ? 'file' : 'folder' });
|
|
118
|
+
}
|
|
119
|
+
return [...inside.values()];
|
|
120
|
+
},
|
|
121
|
+
stat: (at) => {
|
|
122
|
+
const said = held.get(at);
|
|
123
|
+
if (said === undefined)
|
|
124
|
+
throw gone(at);
|
|
125
|
+
return { size: said.length, modified: 0 };
|
|
70
126
|
},
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
return `00000000-0000-4000-8000-${String(drawn).padStart(12, '0')}`;
|
|
127
|
+
remove: (at) => {
|
|
128
|
+
held.delete(at);
|
|
74
129
|
},
|
|
130
|
+
real: (at) => at,
|
|
75
131
|
};
|
|
132
|
+
}
|
|
133
|
+
function vaulting(stood) {
|
|
134
|
+
return {
|
|
135
|
+
async resolve(refs) {
|
|
136
|
+
const found = {};
|
|
137
|
+
for (const ref of refs) {
|
|
138
|
+
const value = stood[ref];
|
|
139
|
+
if (value === undefined) {
|
|
140
|
+
throw new Error(ref + ' is not a secret this app declares, so a trial has nothing to stand in for it. Declare it with ' +
|
|
141
|
+
'`thread create secret`, or stop requiring the target that asks for it.');
|
|
142
|
+
}
|
|
143
|
+
found[ref] = value;
|
|
144
|
+
}
|
|
145
|
+
return found;
|
|
146
|
+
},
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
async function standingStore(row) {
|
|
150
|
+
const said = row.derives?.find((each) => each.id === row.id + '_store')?.at;
|
|
151
|
+
if (said === undefined) {
|
|
152
|
+
return row.id + ' keeps rows and its row derives no store, so a trial has nothing to stand up. Run `thread schema`';
|
|
153
|
+
}
|
|
154
|
+
const at = resolve(ROOT, said);
|
|
155
|
+
if (!existsSync(at))
|
|
156
|
+
return said + ' is not there, so ' + row.id + ' cannot be stood up. Run `thread schema`';
|
|
157
|
+
const made = (await import(__rewriteRelativeImportExtension(pathToFileURL(at).href)));
|
|
158
|
+
const held = made.kept;
|
|
159
|
+
if (held === undefined) {
|
|
160
|
+
return said + ' says nothing about what ' + row.id + ' keeps or with what schema. Run `thread schema`';
|
|
161
|
+
}
|
|
162
|
+
return standing(held);
|
|
163
|
+
}
|
|
164
|
+
async function openedFor(one, scenario, stood, targets = [], calls = []) {
|
|
76
165
|
const recorder = recordingHttp(scenario.answers ?? {});
|
|
77
|
-
|
|
78
|
-
|
|
166
|
+
const packages = [];
|
|
167
|
+
const opening = vaulting(stood);
|
|
168
|
+
const opened = {};
|
|
169
|
+
const stores = [];
|
|
170
|
+
const answering = (row) => calls.filter((each) => each.of === row.id && row.takes.includes(each.id)).map((each) => each.id.replace(/^cl_/, ''));
|
|
171
|
+
for (const word of one.requires) {
|
|
172
|
+
const row = targets.find((each) => each.id === word);
|
|
173
|
+
if (row === undefined || !standsHere(row, targets))
|
|
174
|
+
continue;
|
|
175
|
+
const reach = reachOf(row.id);
|
|
176
|
+
if (reach === 'disk')
|
|
177
|
+
opened[word] = disked(scenario.files ?? {});
|
|
178
|
+
else if (reach === 'cli')
|
|
179
|
+
opened[word] = programmed(word, answering(row));
|
|
180
|
+
else if (reach === 'https')
|
|
181
|
+
opened[word] = recorder;
|
|
182
|
+
else if (reach === 'sdk')
|
|
183
|
+
opened[word] = packaged(word, answering(row), scenario.answers ?? {}, packages);
|
|
184
|
+
else {
|
|
185
|
+
const held = await standingStore(row);
|
|
186
|
+
if (typeof held === 'string')
|
|
187
|
+
return held;
|
|
188
|
+
opened[word] = held.store;
|
|
189
|
+
stores.push(held);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
return { opened, opening, stores, calls: () => [...recorder['calls'], ...packages] };
|
|
79
193
|
}
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
return
|
|
84
|
-
|
|
194
|
+
const BEFORE = new Set(['denied', 'invalid']);
|
|
195
|
+
function entered(result) {
|
|
196
|
+
if (typeof result !== 'object' || result === null)
|
|
197
|
+
return false;
|
|
198
|
+
const held = result;
|
|
199
|
+
if (held.ok === true)
|
|
200
|
+
return true;
|
|
201
|
+
return held.ok === false && !BEFORE.has(String(held.code));
|
|
85
202
|
}
|
|
86
203
|
function denied(result) {
|
|
87
204
|
if (typeof result !== 'object' || result === null)
|
|
88
205
|
return false;
|
|
89
206
|
const held = result;
|
|
90
|
-
return held.ok === false && held.code
|
|
207
|
+
return held.ok === false && BEFORE.has(String(held.code));
|
|
91
208
|
}
|
|
92
|
-
export async function runScenario(one, scenario, dispatch, stood = {},
|
|
93
|
-
const made =
|
|
209
|
+
export async function runScenario(one, scenario, dispatch, stood = {}, targets = [], calls = []) {
|
|
210
|
+
const made = await openedFor(one, scenario, stood, targets, calls);
|
|
211
|
+
if (typeof made === 'string')
|
|
212
|
+
return made;
|
|
94
213
|
const actor = asking(scenario);
|
|
95
|
-
for (const
|
|
96
|
-
const held = made.
|
|
97
|
-
if (held === undefined)
|
|
98
|
-
|
|
99
|
-
|
|
214
|
+
for (const each of scenario.rows ?? []) {
|
|
215
|
+
const held = made.stores.find((store) => store.keeps.includes(each.entity));
|
|
216
|
+
if (held === undefined) {
|
|
217
|
+
return ('`' + one.id + '` is tried with a row of "' + each.entity + '" and no target it requires keeps that entity, ' +
|
|
218
|
+
'so there is nowhere to put it. Require the target that keeps it, or take that row off the scenario');
|
|
219
|
+
}
|
|
220
|
+
await held.seed(actor, [each]);
|
|
100
221
|
}
|
|
101
222
|
let result;
|
|
102
223
|
try {
|
|
103
|
-
result = await dispatch(one, scenario
|
|
224
|
+
result = await dispatch(one, scenario['input'], actor, made.opened, made.opening);
|
|
104
225
|
}
|
|
105
226
|
catch (thrown) {
|
|
106
227
|
result = { threw: thrown instanceof Error ? thrown.message : String(thrown) };
|
|
107
228
|
}
|
|
229
|
+
const kept = {};
|
|
230
|
+
for (const held of made.stores)
|
|
231
|
+
Object.assign(kept, await held.taken(actor));
|
|
108
232
|
return {
|
|
109
233
|
operation: one.id,
|
|
110
234
|
scenario: scenario.id,
|
|
111
235
|
refused: denied(result),
|
|
112
|
-
measured: entered(
|
|
236
|
+
measured: entered(result),
|
|
113
237
|
result,
|
|
114
|
-
kept
|
|
115
|
-
observed: made.sink,
|
|
238
|
+
kept,
|
|
116
239
|
called: made.calls(),
|
|
117
240
|
};
|
|
118
241
|
}
|
|
@@ -157,14 +280,14 @@ export function compared(was, now) {
|
|
|
157
280
|
`measured and no refusal was proven either. What it answered was ${JSON.stringify(one.result)}`);
|
|
158
281
|
}
|
|
159
282
|
if (one.refused && one.measured) {
|
|
160
|
-
failures.push('this
|
|
161
|
-
'
|
|
283
|
+
failures.push('this run was refused before the operation could start and REACHED THE OPERATION anyway — dispatch let a ' +
|
|
284
|
+
'refused request through to the compiled function, which is the gate not holding rather than a trial drifting');
|
|
162
285
|
}
|
|
163
286
|
if (older === undefined) {
|
|
164
287
|
failures.push('no trial holds this scenario. Run `thread trials` and commit the result');
|
|
165
288
|
}
|
|
166
289
|
else {
|
|
167
|
-
for (const part of ['result', 'kept', '
|
|
290
|
+
for (const part of ['result', 'kept', 'called']) {
|
|
168
291
|
const then = printed(older[part]);
|
|
169
292
|
const said = printed(one[part]);
|
|
170
293
|
if (then === said)
|
|
@@ -219,7 +342,7 @@ export function unproven(run) {
|
|
|
219
342
|
const how = [
|
|
220
343
|
denied === 0
|
|
221
344
|
? ''
|
|
222
|
-
: `${String(denied)} were
|
|
345
|
+
: `${String(denied)} were refused before the operation could start — by the gate, or because what they were given is not what it takes — which proves the refusal and nothing whatever about the operation`,
|
|
223
346
|
neither === 0 ? '' : `${String(neither)} neither reached it nor were denied`,
|
|
224
347
|
].filter((one) => one !== '');
|
|
225
348
|
said.push(`\`${operation}\` is UNPROVEN — ${String(outcomes.length)} scenario(s) ran and not one of them reached it: ${how.join(', and ')}. ` +
|
|
@@ -227,7 +350,7 @@ export function unproven(run) {
|
|
|
227
350
|
}
|
|
228
351
|
return said;
|
|
229
352
|
}
|
|
230
|
-
async function
|
|
353
|
+
async function standIns() {
|
|
231
354
|
const at = resolve(ROOT, '_threads/secrets.ts');
|
|
232
355
|
if (!existsSync(at))
|
|
233
356
|
return {};
|
|
@@ -243,10 +366,24 @@ async function standing() {
|
|
|
243
366
|
}
|
|
244
367
|
const found = {};
|
|
245
368
|
for (const one of held) {
|
|
246
|
-
found[one.id] = one.sends === undefined ? `https://${one.id.replace(/_/g, '-')}.invalid` : `${one.id}-stood-in-for`;
|
|
369
|
+
found[one.id] = one.sends === undefined && one.signs === undefined ? `https://${one.id.replace(/_/g, '-')}.invalid` : `${one.id}-stood-in-for`;
|
|
247
370
|
}
|
|
248
371
|
return found;
|
|
249
372
|
}
|
|
373
|
+
async function reaching() {
|
|
374
|
+
const at = resolve(ROOT, '_threads/targets.ts');
|
|
375
|
+
if (!existsSync(at))
|
|
376
|
+
return { targets: [], calls: [] };
|
|
377
|
+
try {
|
|
378
|
+
const taken = (await import(__rewriteRelativeImportExtension(pathToFileURL(at).href)));
|
|
379
|
+
return { targets: taken.TARGETS ?? [], calls: taken.CALLS ?? [] };
|
|
380
|
+
}
|
|
381
|
+
catch (thrown) {
|
|
382
|
+
return (`_threads/targets.ts is there and could not be read: ${thrown instanceof Error ? thrown.message : String(thrown)}. ` +
|
|
383
|
+
'Which disk a trial hands an operation is read from that sheet, so every trial would run with no disk at all and ' +
|
|
384
|
+
'the failures would name the operation instead of the sheet');
|
|
385
|
+
}
|
|
386
|
+
}
|
|
250
387
|
export async function record() {
|
|
251
388
|
const pass = descend();
|
|
252
389
|
const known = types(pass);
|
|
@@ -255,9 +392,13 @@ export async function record() {
|
|
|
255
392
|
const declared = [...operations(pass, known)].filter(([, one]) => one.body !== undefined);
|
|
256
393
|
if (declared.length === 0)
|
|
257
394
|
return { trials: [], refusals: [] };
|
|
258
|
-
const stood = await
|
|
395
|
+
const stood = await standIns();
|
|
259
396
|
if (typeof stood === 'string')
|
|
260
397
|
return { trials: [], refusals: [stood] };
|
|
398
|
+
const reached = await reaching();
|
|
399
|
+
if (typeof reached === 'string')
|
|
400
|
+
return { trials: [], refusals: [reached] };
|
|
401
|
+
const { targets, calls } = reached;
|
|
261
402
|
const at = resolve(ROOT, DISPATCH_AT);
|
|
262
403
|
if (!existsSync(at)) {
|
|
263
404
|
return {
|
|
@@ -272,18 +413,6 @@ export async function record() {
|
|
|
272
413
|
if (dispatch === undefined) {
|
|
273
414
|
return { trials: [], refusals: [`${DISPATCH_AT} exports no \`dispatch\`, so nothing here can run an operation`] };
|
|
274
415
|
}
|
|
275
|
-
const kept = resolve(ROOT, ENTITIES_AT);
|
|
276
|
-
const entities = new Map();
|
|
277
|
-
const keys = new Map();
|
|
278
|
-
if (existsSync(kept)) {
|
|
279
|
-
const sheet = (await import(__rewriteRelativeImportExtension(pathToFileURL(kept).href)));
|
|
280
|
-
for (const one of sheet.ENTITIES ?? []) {
|
|
281
|
-
entities.set(one.id, new Set([...(one.holds ?? []).map((each) => each.id), ...(one.links ?? []).map((each) => `${each.id}_id`)]));
|
|
282
|
-
const named = (one.must ?? []).flatMap((each) => (each.unique?.length === 1 ? [each.unique[0]] : []));
|
|
283
|
-
if (named.length === 1)
|
|
284
|
-
keys.set(one.id, named[0]);
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
416
|
const permitted = resolve(ROOT, ACCESS_AT);
|
|
288
417
|
const may = new Map();
|
|
289
418
|
if (existsSync(permitted)) {
|
|
@@ -304,9 +433,6 @@ export async function record() {
|
|
|
304
433
|
refusals: [`${PERMITS_AT} exports no \`permits\`, so a trial cannot be answered by the gate this app derives`],
|
|
305
434
|
};
|
|
306
435
|
}
|
|
307
|
-
const gate = permits === undefined
|
|
308
|
-
? undefined
|
|
309
|
-
: { check: (actor, action, resource) => Promise.resolve(permits(actor, action, resource)) };
|
|
310
436
|
for (const [id, one] of [...declared].sort((a, b) => compare(a[0], b[0]))) {
|
|
311
437
|
if (one.body === undefined)
|
|
312
438
|
continue;
|
|
@@ -332,7 +458,11 @@ export async function record() {
|
|
|
332
458
|
continue;
|
|
333
459
|
}
|
|
334
460
|
for (const scenario of scenarios) {
|
|
335
|
-
|
|
461
|
+
const ran = await runScenario(operation, scenario, dispatch, stood, targets, calls);
|
|
462
|
+
if (typeof ran === 'string')
|
|
463
|
+
refusals.push(ran);
|
|
464
|
+
else
|
|
465
|
+
trials.push(ran);
|
|
336
466
|
}
|
|
337
467
|
}
|
|
338
468
|
return { trials, refusals };
|