@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,6 +1,31 @@
|
|
|
1
|
+
import { dirname, join, normalize } from 'node:path'
|
|
2
|
+
|
|
1
3
|
import type { Slopnator } from '../slopnator.ts'
|
|
2
4
|
|
|
3
|
-
|
|
5
|
+
import { ROOT, descend, read, reached } from '../../core/descend.ts'
|
|
6
|
+
import { rowsIn } from './sl_unthreaded.ts'
|
|
7
|
+
|
|
8
|
+
const DOOR = 'programs'
|
|
9
|
+
|
|
10
|
+
const ROOTED = ROOT + '/'
|
|
11
|
+
|
|
12
|
+
const slash = (one: string): string => one.split('\\').join('/')
|
|
13
|
+
|
|
14
|
+
const bare = (one: string): string => one.replace(/\.[^./]+$/, '')
|
|
15
|
+
|
|
16
|
+
function doorway(): string | undefined {
|
|
17
|
+
descend()
|
|
18
|
+
|
|
19
|
+
for (const at of reached()) {
|
|
20
|
+
const text = read(at)
|
|
21
|
+
if (text === undefined) continue
|
|
22
|
+
for (const one of rowsIn(text)) {
|
|
23
|
+
if (one.id === DOOR && one.at !== undefined) return one.at
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return undefined
|
|
28
|
+
}
|
|
4
29
|
|
|
5
30
|
interface Node {
|
|
6
31
|
readonly type?: string
|
|
@@ -16,7 +41,7 @@ const named = (held: unknown): string | undefined => {
|
|
|
16
41
|
|
|
17
42
|
export const slopnator: Slopnator = {
|
|
18
43
|
on: ['ImportDeclaration', 'ImportExpression', 'CallExpression'],
|
|
19
|
-
holds: (node) => {
|
|
44
|
+
holds: (node, at) => {
|
|
20
45
|
const held = node as Node
|
|
21
46
|
const from =
|
|
22
47
|
held.type === 'ImportDeclaration' || held.type === 'ImportExpression'
|
|
@@ -25,27 +50,28 @@ export const slopnator: Slopnator = {
|
|
|
25
50
|
? named(held.arguments?.[0])
|
|
26
51
|
: undefined
|
|
27
52
|
|
|
28
|
-
if (from === undefined
|
|
53
|
+
if (from === undefined) return false
|
|
54
|
+
|
|
55
|
+
const door = doorway()
|
|
56
|
+
if (door === undefined) return false
|
|
57
|
+
|
|
58
|
+
const here = at.filename.startsWith(ROOTED) ? at.filename.slice(ROOTED.length) : at.filename
|
|
59
|
+
const aimed = from.startsWith('.') ? slash(normalize(join(dirname(here), from))) : from
|
|
60
|
+
if (bare(aimed) !== bare(door)) return false
|
|
29
61
|
|
|
30
|
-
return
|
|
62
|
+
return (
|
|
63
|
+
'it imports ' + door + ', which is where every process this framework starts comes from. A check that can reach it can ' +
|
|
64
|
+
'start something, and whatever it starts can reach back into the enforcement that called it'
|
|
65
|
+
)
|
|
31
66
|
},
|
|
32
67
|
proves: {
|
|
33
68
|
wrong: [
|
|
34
69
|
{
|
|
35
70
|
files: [
|
|
36
|
-
{
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
},
|
|
40
|
-
],
|
|
41
|
-
at: 'thread/integrity/slopnators/sl_probe_spawn.ts',
|
|
42
|
-
},
|
|
43
|
-
{
|
|
44
|
-
files: [
|
|
45
|
-
{
|
|
46
|
-
at: 'thread/integrity/slopnators/sl_probe_spawn.ts',
|
|
47
|
-
source: "const held = await import('child_process')\nexport const slopnator = { holds: () => false, proves: { wrong: ['a'], right: ['b'] } }\n",
|
|
48
|
-
},
|
|
71
|
+
{ at: 'thread/core/thread.ts', source: "export interface Threaded {\n readonly id?: string\n readonly at?: string\n readonly describes?: string\n readonly declares?: readonly Threaded[]\n readonly derives?: readonly Threaded[]\n readonly seeds?: readonly Threaded[]\n}\n" },
|
|
72
|
+
{ at: 'threads/thread.ts', source: "import type { Threaded } from '../thread/core/thread.ts'\n\nexport const THREAD: Threaded = {\n declares: [\n {\n at: 'threads/_probe_programs.ts',\n },\n ],\n}\n" },
|
|
73
|
+
{ at: 'threads/_probe_programs.ts', source: "import type { Threaded } from '../thread/core/thread.ts'\n\nexport const INTEGRATIONS: Threaded = {\n id: 'integrations',\n declares: [\n {\n id: 'programs',\n at: 'thread/integrations/cli.ts',\n },\n ],\n}\n" },
|
|
74
|
+
{ at: 'thread/integrity/slopnators/sl_probe_spawn.ts', source: "import { starts } from '../../integrations/cli.ts'\nexport const slopnator = { holds: () => false, proves: { wrong: ['a'], right: ['b'] } }\n" },
|
|
49
75
|
],
|
|
50
76
|
at: 'thread/integrity/slopnators/sl_probe_spawn.ts',
|
|
51
77
|
},
|
|
@@ -55,7 +81,7 @@ export const slopnator: Slopnator = {
|
|
|
55
81
|
files: [
|
|
56
82
|
{
|
|
57
83
|
at: 'thread/integrity/slopnators/sl_probe_spawn.ts',
|
|
58
|
-
source: "import {
|
|
84
|
+
source: "import { dirname } from 'node:path'\nexport const slopnator = { holds: () => false, proves: { wrong: ['a'], right: ['b'] } }\n",
|
|
59
85
|
},
|
|
60
86
|
],
|
|
61
87
|
at: 'thread/integrity/slopnators/sl_probe_spawn.ts',
|
|
@@ -1,62 +1,29 @@
|
|
|
1
1
|
import { dirname, join, normalize } from 'node:path';
|
|
2
2
|
import { parser } from 'typescript-eslint';
|
|
3
|
-
import { descend } from
|
|
4
|
-
import {
|
|
3
|
+
import { descend } from '../../core/descend.js';
|
|
4
|
+
import { PREFIXES } from '../../core/thread.js';
|
|
5
|
+
import { here, index, parses, unreadable } from './sl_unthreaded.js';
|
|
5
6
|
const RELATIVE = /^\.\.?\//;
|
|
6
7
|
const UNDER = 'operations/';
|
|
7
|
-
const
|
|
8
|
-
/*
|
|
9
|
-
* The one thing an operation may be handed to.
|
|
10
|
-
*
|
|
11
|
-
* It is still `dispatch` and it is still the only one. What moved 2026-08-28 is where it lives:
|
|
12
|
-
* it was imported from this framework, and it is now the app's own `operations/_dispatch.ts`,
|
|
13
|
-
* written by `thread compile`. The name a caller writes did not change, so neither did this.
|
|
14
|
-
*/
|
|
8
|
+
const COMPILES = 'thread compile';
|
|
15
9
|
const INVOKER = 'dispatch';
|
|
16
10
|
const slash = (one) => one.split('\\').join('/');
|
|
17
|
-
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
-
* Where a name is a shape, not a thing.
|
|
21
|
-
*
|
|
22
|
-
* `const one: typeof operation` runs nothing and reaches nothing — it borrows the type and is erased
|
|
23
|
-
* before anything executes. Refusing it was a false positive found by reading, not by running: the
|
|
24
|
-
* probe written to test the reassignment form used exactly this line to type its own variable, and
|
|
25
|
-
* the law would have refused the probe for its type annotation rather than for the bypass under it.
|
|
26
|
-
*/
|
|
27
|
-
const TYPED = new Set(['TSTypeQuery', 'TSTypeReference', 'TSQualifiedName', 'TSImportType']);
|
|
11
|
+
const IMPORTED = 'ImportBinding';
|
|
12
|
+
const TYPE_ONLY = 'type';
|
|
13
|
+
const QUERIED = 'TSTypeQuery';
|
|
28
14
|
let bodies;
|
|
29
|
-
/*
|
|
30
|
-
* Which files are operation bodies, asked of the tree rather than guessed from a directory.
|
|
31
|
-
*
|
|
32
|
-
* An operation body is a file a row beneath `operations` DERIVES and that is not a node's body, and
|
|
33
|
-
* the descent already knows every one of them. Reading the directory instead was close enough while
|
|
34
|
-
* an operation was the only thing living there — and stopped being close enough the moment an
|
|
35
|
-
* operation was folded into a body plus the steps it is woven from: the woven body imports its own
|
|
36
|
-
* steps, the steps sat under `operations/`, and this law refused the one file it exists to protect
|
|
37
|
-
* for importing the one file it is made of. A rule that refuses the sanctioned form is deciding
|
|
38
|
-
* nothing, which is the same failure this law was rewritten for on 2026-08-25.
|
|
39
|
-
*/
|
|
40
15
|
function operations() {
|
|
41
16
|
if (bodies !== undefined)
|
|
42
17
|
return bodies;
|
|
43
|
-
const
|
|
44
|
-
const under = (id, root, seen) => {
|
|
45
|
-
if (id === root)
|
|
46
|
-
return true;
|
|
47
|
-
if (seen.has(id))
|
|
48
|
-
return false;
|
|
49
|
-
seen.add(id);
|
|
50
|
-
return (pass.coined.get(id)?.from ?? []).some((one) => under(one, root, seen));
|
|
51
|
-
};
|
|
18
|
+
const prefixed = Object.values(PREFIXES);
|
|
52
19
|
const found = new Set();
|
|
53
|
-
for (const [at, threadings] of
|
|
20
|
+
for (const [at, threadings] of descend().threaded) {
|
|
54
21
|
for (const one of threadings) {
|
|
55
22
|
if (one.list !== 'derives')
|
|
56
23
|
continue;
|
|
57
|
-
if (
|
|
24
|
+
if (one.row.by !== COMPILES)
|
|
58
25
|
continue;
|
|
59
|
-
if (
|
|
26
|
+
if (!prefixed.some((each) => one.by.startsWith(each)))
|
|
60
27
|
continue;
|
|
61
28
|
found.add(at);
|
|
62
29
|
}
|
|
@@ -64,31 +31,37 @@ function operations() {
|
|
|
64
31
|
bodies = found;
|
|
65
32
|
return found;
|
|
66
33
|
}
|
|
67
|
-
/*
|
|
68
|
-
* Whether a path is an operation body.
|
|
69
|
-
*
|
|
70
|
-
* The tree decides for every file the tree reaches. For a file it does not reach — a probe, a
|
|
71
|
-
* scratch file, anything not yet declared — the shape decides: an operation is seeded as one file
|
|
72
|
-
* directly inside `operations/`, so a single segment is a body and a deeper path is something an
|
|
73
|
-
* operation is built from. Nothing the tree does not reach can be part of an app anyway; that
|
|
74
|
-
* fallback is what makes this law's own proofs mean the same thing in every repository.
|
|
75
|
-
*/
|
|
76
34
|
function isBody(path) {
|
|
77
35
|
const known = index().reached;
|
|
78
36
|
if (known.has(path))
|
|
79
37
|
return operations().has(path);
|
|
80
38
|
if (!path.startsWith(UNDER))
|
|
81
39
|
return false;
|
|
82
|
-
|
|
40
|
+
const file = path.slice(UNDER.length);
|
|
41
|
+
return !file.includes('/') && Object.values(PREFIXES).some((one) => file.startsWith(one));
|
|
42
|
+
}
|
|
43
|
+
let endings;
|
|
44
|
+
function spellings() {
|
|
45
|
+
if (endings !== undefined)
|
|
46
|
+
return endings;
|
|
47
|
+
const found = new Map();
|
|
48
|
+
for (const one of index().reached) {
|
|
49
|
+
const dot = one.lastIndexOf('.');
|
|
50
|
+
if (dot > one.lastIndexOf('/'))
|
|
51
|
+
found.set(one.slice(0, dot), one);
|
|
52
|
+
}
|
|
53
|
+
endings = found;
|
|
54
|
+
return found;
|
|
83
55
|
}
|
|
84
|
-
const TRIED = ['', '.ts', '.tsx'];
|
|
85
56
|
function reaches(said, from) {
|
|
86
57
|
if (said === '')
|
|
87
58
|
return false;
|
|
88
59
|
const at = slash(normalize(RELATIVE.test(said) ? join(from, said) : said));
|
|
89
|
-
|
|
60
|
+
if (isBody(at))
|
|
61
|
+
return true;
|
|
62
|
+
const whole = spellings().get(at);
|
|
63
|
+
return whole !== undefined && isBody(whole);
|
|
90
64
|
}
|
|
91
|
-
/* Whatever a run-time reach spells out, whole or in pieces. */
|
|
92
65
|
function spelled(node, said) {
|
|
93
66
|
if (node === undefined || node === null || typeof node !== 'object')
|
|
94
67
|
return;
|
|
@@ -107,58 +80,26 @@ function spelled(node, said) {
|
|
|
107
80
|
spelled(value, said);
|
|
108
81
|
}
|
|
109
82
|
}
|
|
110
|
-
|
|
111
|
-
const LOCAL = /(?:^|[,{]|\s)(?:[A-Za-z_$][\w$]*\s+as\s+)?([A-Za-z_$][\w$]*)/g;
|
|
112
|
-
/*
|
|
113
|
-
* `dispatch` IS NOT AN OPERATION, AND IT LIVES WHERE THE OPERATIONS LIVE.
|
|
114
|
-
*
|
|
115
|
-
* `bound` below calls every name imported from under the operations directory an operation, which is
|
|
116
|
-
* right for every file there but one: an app's own dispatch is written to `operations/_dispatch.ts`,
|
|
117
|
-
* so a file importing `dispatch` from beside its operations had `dispatch` itself added to the set —
|
|
118
|
-
* and then this law refused that file for doing something with an operation other than handing it to
|
|
119
|
-
* dispatch. The thing it was accused of mishandling was the handler.
|
|
120
|
-
*
|
|
121
|
-
* It had never surfaced because nothing imported dispatch from inside that directory. Measured
|
|
122
|
-
* 2026-08-30, when the generated client gained a binding that calls the app's own dispatch for an
|
|
123
|
-
* operation needing no server, and the app refused a file its own generator had just written.
|
|
124
|
-
*
|
|
125
|
-
* The other three below are not names at all — they are the words of an import clause that the
|
|
126
|
-
* regex cannot tell from an identifier. This one is a real binding that is really imported and
|
|
127
|
-
* really is not an operation, which is the same answer for a different reason.
|
|
128
|
-
*/
|
|
129
|
-
const NOT_A_NAME = new Set(['type', 'as', 'from', 'import', 'dispatch']);
|
|
130
|
-
/*
|
|
131
|
-
* The names this file bound by importing an operation body.
|
|
132
|
-
*
|
|
133
|
-
* A type-only import binds nothing that can be run and is skipped: `import type { Operation }` names
|
|
134
|
-
* a shape, and a shape cannot be dispatched or reached past.
|
|
135
|
-
*/
|
|
136
|
-
function bound(text, from) {
|
|
83
|
+
function handled(scopes, from) {
|
|
137
84
|
const found = new Set();
|
|
138
|
-
for (const
|
|
139
|
-
const
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
const
|
|
146
|
-
if (
|
|
147
|
-
|
|
85
|
+
for (const scope of scopes?.scopes ?? []) {
|
|
86
|
+
for (const one of scope.variables ?? []) {
|
|
87
|
+
const made = (one.defs ?? [])[0];
|
|
88
|
+
if (made?.type !== IMPORTED)
|
|
89
|
+
continue;
|
|
90
|
+
if (made.node?.importKind === TYPE_ONLY || made.parent?.importKind === TYPE_ONLY)
|
|
91
|
+
continue;
|
|
92
|
+
const said = made.parent?.source?.value;
|
|
93
|
+
if (typeof said !== 'string' || !reaches(said, from))
|
|
94
|
+
continue;
|
|
95
|
+
for (const seen of one.references ?? []) {
|
|
96
|
+
if (seen.isValueReference !== false && seen.identifier !== undefined)
|
|
97
|
+
found.add(seen.identifier);
|
|
98
|
+
}
|
|
148
99
|
}
|
|
149
100
|
}
|
|
150
101
|
return found;
|
|
151
102
|
}
|
|
152
|
-
/*
|
|
153
|
-
* Parsed here rather than taken from the visitor.
|
|
154
|
-
*
|
|
155
|
-
* `holds` is handed a node when a law runs as an eslint rule and nothing at all when the sweep runs
|
|
156
|
-
* it over a file — measured 2026-08-25, the sweep calls it once per file with `node` undefined, so a
|
|
157
|
-
* reading that only inspected what it was given threw on the first file it saw. What a file DOES
|
|
158
|
-
* with a binding is a question about the whole file anyway, not about one node in it.
|
|
159
|
-
*
|
|
160
|
-
* The parent comes down with the node because the whole rule is about where a name appears.
|
|
161
|
-
*/
|
|
162
103
|
function walked(node, up, seen) {
|
|
163
104
|
if (node === null || typeof node !== 'object')
|
|
164
105
|
return undefined;
|
|
@@ -195,40 +136,20 @@ export const slopnator = {
|
|
|
195
136
|
if (!text.includes(UNDER))
|
|
196
137
|
return false;
|
|
197
138
|
const from = dirname(path);
|
|
198
|
-
|
|
199
|
-
let ast;
|
|
139
|
+
let parsed;
|
|
200
140
|
try {
|
|
201
|
-
|
|
141
|
+
parsed = parser.parseForESLint(text, {
|
|
202
142
|
range: true,
|
|
203
143
|
ecmaVersion: 'latest',
|
|
204
144
|
sourceType: 'module',
|
|
205
145
|
ecmaFeatures: { jsx: true },
|
|
206
|
-
})
|
|
146
|
+
});
|
|
207
147
|
}
|
|
208
148
|
catch {
|
|
209
149
|
return false;
|
|
210
150
|
}
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
*
|
|
214
|
-
* The first reading of this enumerated the SHAPES of a bypass — a member access, a call, an
|
|
215
|
-
* argument — and every shape it did not think of walked through: an alias, a destructure, an
|
|
216
|
-
* array holding the name, a comma operator. Enumerating forms is how a refusal ends up with a
|
|
217
|
-
* list of everything somebody happened to try. The rule is not a list. An operation goes to
|
|
218
|
-
* `dispatch` and nowhere else, so the name may appear where it is bound and as an argument to
|
|
219
|
-
* `dispatch`, and every other appearance is the finding — whatever it happens to look like.
|
|
220
|
-
*/
|
|
221
|
-
return (walked(ast, undefined, (held, parent) => {
|
|
222
|
-
/*
|
|
223
|
-
* A body reached at run time never binds a name at all, so the rule above cannot see it.
|
|
224
|
-
*
|
|
225
|
-
* What is read is everything the reach spells out, whole or in pieces: a plain literal, a
|
|
226
|
-
* concatenation, a template. Measured 2026-08-26 against the reading this replaces, which
|
|
227
|
-
* only ever inspected a plain `Literal` — `await import('../operations/' + name + '.ts')`
|
|
228
|
-
* passed it without a word, which is the exact form its own comment said it was written to
|
|
229
|
-
* catch. Nothing legitimate needs any of it: dispatch is handed the operation, and the one
|
|
230
|
-
* file that hands it over imports it statically.
|
|
231
|
-
*/
|
|
151
|
+
const mine = handled(parsed.scopeManager, from);
|
|
152
|
+
return (walked(parsed.ast, undefined, (held, parent) => {
|
|
232
153
|
const dynamic = held.type === 'ImportExpression'
|
|
233
154
|
? held.source
|
|
234
155
|
: held.type === 'CallExpression' && held.callee?.type === 'Identifier' && held.callee.name === 'require'
|
|
@@ -243,10 +164,9 @@ export const slopnator = {
|
|
|
243
164
|
`to \`${INVOKER}\`, and a file that goes looking for one is looking for a second way in`);
|
|
244
165
|
}
|
|
245
166
|
}
|
|
246
|
-
if (held.type !== 'Identifier' || typeof held.name !== 'string' || !mine.has(held
|
|
167
|
+
if (held.type !== 'Identifier' || typeof held.name !== 'string' || !mine.has(held))
|
|
247
168
|
return undefined;
|
|
248
|
-
|
|
249
|
-
if (BINDS.has(up) || TYPED.has(up))
|
|
169
|
+
if (parent?.type === QUERIED)
|
|
250
170
|
return undefined;
|
|
251
171
|
if (parent?.type === 'CallExpression' &&
|
|
252
172
|
parent.callee?.type === 'Identifier' &&
|
|
@@ -259,23 +179,10 @@ export const slopnator = {
|
|
|
259
179
|
'aliasing it, taking it apart or passing it on all reach past every one of them');
|
|
260
180
|
}) ?? false);
|
|
261
181
|
},
|
|
262
|
-
/*
|
|
263
|
-
* These were driven at `holds` directly, because the trial harness cannot run them.
|
|
264
|
-
*
|
|
265
|
-
* A shipped law is meant to be proved by `thread trials`, and today nothing proves one anywhere:
|
|
266
|
-
* the harness cannot run in this repository at all — the framework has no tree, and the command
|
|
267
|
-
* that once tried was removed in 6e98a6d after it was measured exiting 1 — and in an app
|
|
268
|
-
* `thread trials` answers "0 trials from the 0 rule(s) this tree declares",
|
|
269
|
-
* because a shipped constraint is proved in the package that ships it. So every proof below was
|
|
270
|
-
* written to a real probe file and handed to `slopnator.holds` by hand, 2026-08-26: fourteen
|
|
271
|
-
* refused, six accepted, no failures. That is a stand-in for the harness and it is written down
|
|
272
|
-
* here rather than only in a report, because a proof nobody can run is indistinguishable from a
|
|
273
|
-
* proof that passes, and the next person to read this list deserves to know which one it is.
|
|
274
|
-
*/
|
|
275
182
|
proves: {
|
|
276
183
|
wrong: [
|
|
277
184
|
"import { operation } from './operations/wf_send_invoice.ts'\n\nawait operation.run(input)",
|
|
278
|
-
"import { operation } from './operations/wf_send_invoice.ts'\n\nconst { run } = operation\n\nawait run(
|
|
185
|
+
"import { operation } from './operations/wf_send_invoice.ts'\n\nconst { run } = operation\n\nawait run(opened, held)",
|
|
279
186
|
"import { operation } from './operations/wf_send_invoice.ts'\n\nawait operation(input)",
|
|
280
187
|
"import { operation } from './operations/wf_send_invoice.ts'\n\nawait somewhere(operation, input)",
|
|
281
188
|
"import { operation } from './operations/wf_send_invoice.ts'\n\nconst one = operation\n\nawait one.run(input)",
|
|
@@ -290,10 +197,34 @@ export const slopnator = {
|
|
|
290
197
|
'const held = await import(`../operations/${name}.ts`)',
|
|
291
198
|
],
|
|
292
199
|
right: [
|
|
293
|
-
|
|
200
|
+
{
|
|
201
|
+
files: [
|
|
202
|
+
{ at: 'thread/core/thread.ts', source: "export interface Threaded {\n readonly id?: string\n readonly at?: string\n readonly describes?: string\n readonly declares?: readonly Threaded[]\n readonly derives?: readonly Threaded[]\n readonly seeds?: readonly Threaded[]\n}\n" },
|
|
203
|
+
{ at: 'threads/thread.ts', source: "import type { Threaded } from '../thread/core/thread.ts'\n\nexport const THREAD: Threaded = {\n declares: [\n {\n at: 'threads/_probe_operations.ts',\n },\n ],\n}\n" },
|
|
204
|
+
{ at: 'threads/_probe_operations.ts', source: "import type { Threaded } from '../thread/core/thread.ts'\n\nexport const OPERATIONS: Threaded = {\n id: 'operations',\n declares: [\n {\n id: 'wf_send_invoice',\n describes: 'sends an invoice',\n derives: [\n {\n id: 'wf_send_invoice_body',\n at: 'operations/wf_send_invoice.ts',\n by: 'thread compile',\n },\n ],\n },\n ],\n}\n" },
|
|
205
|
+
{ at: 'probe.tsx', source: "import { dispatch } from './operations/_dispatch.ts'\n\nimport { operation } from './operations/wf_send_invoice.ts'\n\nawait dispatch(operation, input, actor, opened)\n" },
|
|
206
|
+
],
|
|
207
|
+
at: 'probe.tsx',
|
|
208
|
+
},
|
|
294
209
|
"import type { Operation } from '../../operations/operations.ts'\n\nexport const held: Operation = one",
|
|
295
|
-
|
|
296
|
-
|
|
210
|
+
{
|
|
211
|
+
files: [
|
|
212
|
+
{ at: 'thread/core/thread.ts', source: "export interface Threaded {\n readonly id?: string\n readonly at?: string\n readonly describes?: string\n readonly declares?: readonly Threaded[]\n readonly derives?: readonly Threaded[]\n readonly seeds?: readonly Threaded[]\n}\n" },
|
|
213
|
+
{ at: 'threads/thread.ts', source: "import type { Threaded } from '../thread/core/thread.ts'\n\nexport const THREAD: Threaded = {\n declares: [\n {\n at: 'threads/_probe_operations.ts',\n },\n ],\n}\n" },
|
|
214
|
+
{ at: 'threads/_probe_operations.ts', source: "import type { Threaded } from '../thread/core/thread.ts'\n\nexport const OPERATIONS: Threaded = {\n id: 'operations',\n declares: [\n {\n id: 'wf_send_invoice',\n describes: 'sends an invoice',\n derives: [\n {\n id: 'wf_send_invoice_body',\n at: 'operations/wf_send_invoice.ts',\n by: 'thread compile',\n },\n ],\n },\n ],\n}\n" },
|
|
215
|
+
{ at: 'probe.tsx', source: "import { dispatch } from './operations/_dispatch.ts'\n\nawait dispatch(wf_send_invoice_body, input, actor, opened)\n" },
|
|
216
|
+
],
|
|
217
|
+
at: 'probe.tsx',
|
|
218
|
+
},
|
|
219
|
+
{
|
|
220
|
+
files: [
|
|
221
|
+
{ at: 'thread/core/thread.ts', source: "export interface Threaded {\n readonly id?: string\n readonly at?: string\n readonly describes?: string\n readonly declares?: readonly Threaded[]\n readonly derives?: readonly Threaded[]\n readonly seeds?: readonly Threaded[]\n}\n" },
|
|
222
|
+
{ at: 'threads/thread.ts', source: "import type { Threaded } from '../thread/core/thread.ts'\n\nexport const THREAD: Threaded = {\n declares: [\n {\n at: 'threads/_probe_operations.ts',\n },\n ],\n}\n" },
|
|
223
|
+
{ at: 'threads/_probe_operations.ts', source: "import type { Threaded } from '../thread/core/thread.ts'\n\nexport const OPERATIONS: Threaded = {\n id: 'operations',\n declares: [\n {\n id: 'wf_send_invoice',\n describes: 'sends an invoice',\n derives: [\n {\n id: 'wf_send_invoice_body',\n at: 'operations/wf_send_invoice.ts',\n by: 'thread compile',\n },\n ],\n },\n ],\n}\n" },
|
|
224
|
+
{ at: 'probe.tsx', source: "import { dispatch } from './operations/_dispatch.ts'\n\nimport { operation } from './operations/wf_send_invoice.ts'\n\nlet one: typeof operation\n\nawait dispatch(operation, input, actor, opened)\n" },
|
|
225
|
+
],
|
|
226
|
+
at: 'probe.tsx',
|
|
227
|
+
},
|
|
297
228
|
"import * as steps from './operations/wf_send_invoice/steps.ts'\n\nexport const held = steps.reads_the_invoice",
|
|
298
229
|
"import * as steps from './steps/wf_send_invoice.ts'\n\nexport const held = steps.reads_the_invoice",
|
|
299
230
|
],
|