@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
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
import { resolve } from 'node:path'
|
|
2
|
+
|
|
3
|
+
import ts from 'typescript'
|
|
4
|
+
|
|
5
|
+
import { ROOT, configured, hosting } from '../../core/descend.ts'
|
|
6
|
+
import { kind, readFileSync } from '../../integrations/disk.ts'
|
|
7
|
+
import { OPENS } from '../../integrations/target.ts'
|
|
8
|
+
|
|
9
|
+
import type { Slopnator } from '../slopnator.ts'
|
|
10
|
+
import { here } from './sl_unthreaded.ts'
|
|
11
|
+
|
|
12
|
+
const IMPORTED = 'import '
|
|
13
|
+
|
|
14
|
+
const MADE = 'new '
|
|
15
|
+
|
|
16
|
+
const RUNTIME = 'node:'
|
|
17
|
+
|
|
18
|
+
const ANYWHERE = 'globalThis'
|
|
19
|
+
|
|
20
|
+
const REQUIRED = 'require'
|
|
21
|
+
|
|
22
|
+
const DECLARED = /\.d\.([cm]?)ts$/
|
|
23
|
+
|
|
24
|
+
const READABLE = /\.(?:[cm]?[jt]s|[jt]sx)$/
|
|
25
|
+
|
|
26
|
+
const plain = (specifier: string): string => (specifier.startsWith(RUNTIME) ? specifier.slice(RUNTIME.length) : specifier)
|
|
27
|
+
|
|
28
|
+
interface Door {
|
|
29
|
+
readonly reach: string
|
|
30
|
+
readonly spelling: string
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
interface Doors {
|
|
34
|
+
readonly modules: ReadonlyMap<string, Door>
|
|
35
|
+
readonly globals: ReadonlyMap<string, Door>
|
|
36
|
+
readonly properties: ReadonlyMap<string, Door>
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function doors(): Doors {
|
|
40
|
+
const modules = new Map<string, Door>()
|
|
41
|
+
const globals = new Map<string, Door>()
|
|
42
|
+
const properties = new Map<string, Door>()
|
|
43
|
+
for (const [reach, spellings] of Object.entries(OPENS)) {
|
|
44
|
+
for (const spelling of spellings) {
|
|
45
|
+
const door = { reach, spelling }
|
|
46
|
+
if (spelling.startsWith(IMPORTED)) {
|
|
47
|
+
const rest = spelling.slice(IMPORTED.length)
|
|
48
|
+
const dot = rest.lastIndexOf('.')
|
|
49
|
+
modules.set(plain(dot === -1 || rest.slice(dot).includes('/') ? rest : rest.slice(0, dot)), door)
|
|
50
|
+
continue
|
|
51
|
+
}
|
|
52
|
+
if (spelling.startsWith(MADE)) {
|
|
53
|
+
globals.set(spelling.slice(MADE.length), door)
|
|
54
|
+
continue
|
|
55
|
+
}
|
|
56
|
+
if (spelling.includes('.')) properties.set(spelling, door)
|
|
57
|
+
else globals.set(spelling, door)
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return { modules, globals, properties }
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const DOORS = doors()
|
|
64
|
+
|
|
65
|
+
interface Brought {
|
|
66
|
+
readonly specifier: string
|
|
67
|
+
readonly line: number
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const files = new Map<string, ts.SourceFile>()
|
|
71
|
+
|
|
72
|
+
function parsed(at: string, text: string): ts.SourceFile {
|
|
73
|
+
const held = files.get(text)
|
|
74
|
+
if (held !== undefined) return held
|
|
75
|
+
const file = ts.createSourceFile(at, text, ts.ScriptTarget.Latest, true, at.endsWith('x') ? ts.ScriptKind.TSX : ts.ScriptKind.TS)
|
|
76
|
+
files.set(text, file)
|
|
77
|
+
return file
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function brought(file: ts.SourceFile): readonly Brought[] {
|
|
81
|
+
const found: Brought[] = []
|
|
82
|
+
const line = (node: ts.Node): number => file.getLineAndCharacterOfPosition(node.getStart(file)).line + 1
|
|
83
|
+
const visit = (node: ts.Node): void => {
|
|
84
|
+
if (ts.isImportDeclaration(node) && node.importClause?.isTypeOnly !== true && ts.isStringLiteral(node.moduleSpecifier)) {
|
|
85
|
+
found.push({ specifier: node.moduleSpecifier.text, line: line(node) })
|
|
86
|
+
}
|
|
87
|
+
if (ts.isExportDeclaration(node) && !node.isTypeOnly && node.moduleSpecifier !== undefined && ts.isStringLiteral(node.moduleSpecifier)) {
|
|
88
|
+
found.push({ specifier: node.moduleSpecifier.text, line: line(node) })
|
|
89
|
+
}
|
|
90
|
+
if (ts.isCallExpression(node)) {
|
|
91
|
+
const first = node.arguments[0]
|
|
92
|
+
const loads = node.expression.kind === ts.SyntaxKind.ImportKeyword || (ts.isIdentifier(node.expression) && node.expression.text === REQUIRED)
|
|
93
|
+
if (loads && first !== undefined && ts.isStringLiteral(first)) found.push({ specifier: first.text, line: line(node) })
|
|
94
|
+
}
|
|
95
|
+
ts.forEachChild(node, visit)
|
|
96
|
+
}
|
|
97
|
+
visit(file)
|
|
98
|
+
return found
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const binds = (node: ts.Identifier): boolean => {
|
|
102
|
+
const parent = node.parent
|
|
103
|
+
return (
|
|
104
|
+
(ts.isVariableDeclaration(parent) ||
|
|
105
|
+
ts.isParameter(parent) ||
|
|
106
|
+
ts.isBindingElement(parent) ||
|
|
107
|
+
ts.isFunctionDeclaration(parent) ||
|
|
108
|
+
ts.isClassDeclaration(parent) ||
|
|
109
|
+
ts.isImportSpecifier(parent) ||
|
|
110
|
+
ts.isImportClause(parent) ||
|
|
111
|
+
ts.isNamespaceImport(parent)) &&
|
|
112
|
+
parent.name === node
|
|
113
|
+
)
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const named = (node: ts.Node): boolean => (node.parent as { readonly name?: ts.Node } | undefined)?.name === node
|
|
117
|
+
|
|
118
|
+
const opened = new Map<string, Door | undefined>()
|
|
119
|
+
|
|
120
|
+
function opening(at: string, text: string): Door | undefined {
|
|
121
|
+
if (opened.has(text)) return opened.get(text)
|
|
122
|
+
const file = parsed(at, text)
|
|
123
|
+
let found: Door | undefined
|
|
124
|
+
for (const one of brought(file)) found ??= DOORS.modules.get(plain(one.specifier))
|
|
125
|
+
const bound = new Set<string>()
|
|
126
|
+
const collect = (node: ts.Node): void => {
|
|
127
|
+
if (ts.isIdentifier(node) && binds(node)) bound.add(node.text)
|
|
128
|
+
ts.forEachChild(node, collect)
|
|
129
|
+
}
|
|
130
|
+
collect(file)
|
|
131
|
+
const visit = (node: ts.Node): void => {
|
|
132
|
+
if (found !== undefined) return
|
|
133
|
+
if (ts.isPropertyAccessExpression(node) && ts.isIdentifier(node.expression) && !ts.isPartOfTypeNode(node)) {
|
|
134
|
+
const object = node.expression.text
|
|
135
|
+
found = DOORS.properties.get(object + '.' + node.name.text) ?? (object === ANYWHERE ? DOORS.globals.get(node.name.text) : undefined)
|
|
136
|
+
}
|
|
137
|
+
if (found === undefined && ts.isIdentifier(node) && !named(node) && !ts.isPartOfTypeNode(node) && !bound.has(node.text)) {
|
|
138
|
+
found = DOORS.globals.get(node.text)
|
|
139
|
+
}
|
|
140
|
+
ts.forEachChild(node, visit)
|
|
141
|
+
}
|
|
142
|
+
visit(file)
|
|
143
|
+
opened.set(text, found)
|
|
144
|
+
return found
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
let options: ts.CompilerOptions | undefined
|
|
148
|
+
|
|
149
|
+
let host: ts.CompilerHost | undefined
|
|
150
|
+
|
|
151
|
+
function resolvedOf(specifier: string, from: string): string | undefined {
|
|
152
|
+
options ??= configured().options
|
|
153
|
+
host ??= hosting(options)
|
|
154
|
+
const found = ts.resolveModuleName(specifier, from, options, host).resolvedModule
|
|
155
|
+
if (found === undefined) return undefined
|
|
156
|
+
const at = found.resolvedFileName
|
|
157
|
+
if (!DECLARED.test(at)) return at
|
|
158
|
+
const emitted = at.replace(DECLARED, '.$1js')
|
|
159
|
+
return kind(emitted) === 'file' ? emitted : undefined
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const readable = (at: string): string | undefined => (READABLE.test(at) && kind(at) === 'file' ? readFileSync(at, 'utf8') : undefined)
|
|
163
|
+
|
|
164
|
+
const shown = (chain: readonly string[], door: Door): string =>
|
|
165
|
+
chain.map((one) => '`' + one + '`').join(', which imports ') +
|
|
166
|
+
(door.spelling.startsWith(IMPORTED) ? ', which imports ' + door.spelling.slice(IMPORTED.length) : ', which reaches ' + door.spelling) +
|
|
167
|
+
' — the ' +
|
|
168
|
+
door.reach +
|
|
169
|
+
' reach, and no target declares it. Nothing prices that reach, replays it in a trial or refuses it. Declare the place with `thread create target`, require it on the operation, and call what the node is handed instead'
|
|
170
|
+
|
|
171
|
+
function through(at: string, chain: readonly string[], seen: Set<string>): string | undefined {
|
|
172
|
+
const text = readable(at)
|
|
173
|
+
if (text === undefined) return undefined
|
|
174
|
+
const door = opening(at, text)
|
|
175
|
+
if (door !== undefined) return shown(chain, door)
|
|
176
|
+
for (const one of brought(parsed(at, text))) {
|
|
177
|
+
const next = resolvedOf(one.specifier, at)
|
|
178
|
+
if (next === undefined || seen.has(next)) continue
|
|
179
|
+
seen.add(next)
|
|
180
|
+
const found = through(next, [...chain, one.specifier], seen)
|
|
181
|
+
if (found !== undefined) return found
|
|
182
|
+
}
|
|
183
|
+
return undefined
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
export const slopnator: Slopnator = {
|
|
187
|
+
holds: (_node, at) => {
|
|
188
|
+
const path = here(at.filename)
|
|
189
|
+
if (!READABLE.test(path)) return false
|
|
190
|
+
const from = resolve(ROOT, path)
|
|
191
|
+
const seen = new Set<string>([from])
|
|
192
|
+
for (const one of brought(parsed(from, at.text))) {
|
|
193
|
+
const next = resolvedOf(one.specifier, from)
|
|
194
|
+
if (next === undefined || seen.has(next)) continue
|
|
195
|
+
seen.add(next)
|
|
196
|
+
const found = through(next, [one.specifier], seen)
|
|
197
|
+
if (found !== undefined) return 'line ' + String(one.line) + ': imports ' + found
|
|
198
|
+
}
|
|
199
|
+
return false
|
|
200
|
+
},
|
|
201
|
+
proves: {
|
|
202
|
+
wrong: [
|
|
203
|
+
{
|
|
204
|
+
files: [
|
|
205
|
+
{ at: 'thread/integrations/cli.ts', source: "import { spawn } from 'node:child_process'\n\nexport const starts = (program: string): void => {\n spawn(program)\n}\n" },
|
|
206
|
+
{ at: 'operations/_probe_node.ts', source: "export { starts } from '../thread/integrations/cli.ts'\n" },
|
|
207
|
+
],
|
|
208
|
+
at: 'operations/_probe_node.ts',
|
|
209
|
+
},
|
|
210
|
+
{
|
|
211
|
+
files: [
|
|
212
|
+
{ at: 'thread/integrations/https.ts', source: "export const sendingHttp = async (address: string): Promise<number> => (await fetch(address)).status\n" },
|
|
213
|
+
{ at: 'src/_probe_helper.ts', source: "import { sendingHttp } from '../thread/integrations/https.ts'\n\nexport const status = (address: string): Promise<number> => sendingHttp(address)\n" },
|
|
214
|
+
{ at: 'operations/_probe_node.ts', source: "import { status } from '../src/_probe_helper.ts'\n\nexport const node = (address: string): Promise<number> => status(address)\n" },
|
|
215
|
+
],
|
|
216
|
+
at: 'operations/_probe_node.ts',
|
|
217
|
+
},
|
|
218
|
+
{
|
|
219
|
+
files: [
|
|
220
|
+
{ at: 'thread/integrations/disk.ts', source: "import * as disk from 'node:fs'\n\nexport const read = (at: string): string => disk.readFileSync(at, 'utf8')\n" },
|
|
221
|
+
{ at: 'operations/_probe_node.ts', source: "export const node = async (at: string): Promise<string> => (await import('../thread/integrations/disk.ts')).read(at)\n" },
|
|
222
|
+
],
|
|
223
|
+
at: 'operations/_probe_node.ts',
|
|
224
|
+
},
|
|
225
|
+
],
|
|
226
|
+
right: [
|
|
227
|
+
{
|
|
228
|
+
files: [
|
|
229
|
+
{ at: 'thread/operations/nodes.ts', source: "export const nd_action = <T>(run: T): T => run\n" },
|
|
230
|
+
{ at: 'operations/_probe_node.ts', source: "import { nd_action } from '../thread/operations/nodes.ts'\n\nexport const node = nd_action(async () => 1)\n" },
|
|
231
|
+
],
|
|
232
|
+
at: 'operations/_probe_node.ts',
|
|
233
|
+
},
|
|
234
|
+
{
|
|
235
|
+
files: [
|
|
236
|
+
{ at: 'thread/integrations/cli.ts', source: "import { spawn } from 'node:child_process'\n\nexport interface Ran {\n readonly out: string\n}\n\nexport const starts = (program: string): void => {\n spawn(program)\n}\n" },
|
|
237
|
+
{ at: 'operations/_probe_node.ts', source: "import type { Ran } from '../thread/integrations/cli.ts'\n\nexport const node = (ran: Ran): string => ran.out\n" },
|
|
238
|
+
],
|
|
239
|
+
at: 'operations/_probe_node.ts',
|
|
240
|
+
},
|
|
241
|
+
{
|
|
242
|
+
files: [
|
|
243
|
+
{ at: 'src/_probe_helper.ts', source: "const fetch = (said: string): string => said\n\nexport const shout = (said: string): string => fetch(said).toUpperCase()\n" },
|
|
244
|
+
{ at: 'operations/_probe_node.ts', source: "import { shout } from '../src/_probe_helper.ts'\n\nexport const node = (): string => shout('a')\n" },
|
|
245
|
+
],
|
|
246
|
+
at: 'operations/_probe_node.ts',
|
|
247
|
+
},
|
|
248
|
+
],
|
|
249
|
+
},
|
|
250
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { existsSync, readFileSync } from
|
|
1
|
+
import { existsSync, readFileSync } from '../../integrations/disk.js';
|
|
2
2
|
import { resolve } from 'node:path';
|
|
3
|
-
import { declaresIsland, here, rowsIn } from
|
|
4
|
-
import { findTree } from
|
|
3
|
+
import { declaresIsland, here, rowsIn } from './sl_unthreaded.js';
|
|
4
|
+
import { findTree } from '../../core/tree.js';
|
|
5
5
|
const ROOT = findTree().at;
|
|
6
6
|
const TREE = `${findTree().name}/`;
|
|
7
7
|
function schemaAt(at) {
|
|
@@ -60,17 +60,19 @@ export const slopnator = {
|
|
|
60
60
|
right: [
|
|
61
61
|
{
|
|
62
62
|
files: [
|
|
63
|
-
{ at: '
|
|
63
|
+
{ at: 'thread/core/thread.ts', source: "export interface Threaded {\n readonly id?: string\n readonly at?: string\n readonly describes?: string\n readonly governs?: readonly string[]\n readonly declares?: readonly Threaded[]\n readonly derives?: readonly Threaded[]\n readonly seeds?: readonly Threaded[]\n}\n" },
|
|
64
|
+
{ at: 'threads/thread.ts', source: "import type { Threaded } from '../thread/core/thread.ts'\n\nexport const THREAD: Threaded = {\n declares: [\n {\n at: 'threads/_inert_probe.ts',\n },\n ],\n}\n" },
|
|
65
|
+
{ at: 'threads/_inert_probe.ts', source: "import type { Threaded } from '../thread/core/thread.ts'\n\nexport const M: Threaded = {\n declares: [\n { id: 'events', at: 'src/islands/events.ts', governs: ['app/_declare/events.ts'] },\n ],\n}\n" },
|
|
64
66
|
{ at: 'src/islands/events.ts', source: 'export const EventsIslandSchema = {}\n' },
|
|
65
67
|
],
|
|
66
68
|
at: 'threads/_inert_probe.ts',
|
|
67
69
|
},
|
|
68
70
|
{
|
|
69
71
|
files: [
|
|
70
|
-
{
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
},
|
|
72
|
+
{ 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" },
|
|
73
|
+
{ at: 'threads/thread.ts', source: "import type { Threaded } from '../thread/core/thread.ts'\n\nexport const THREAD: Threaded = {\n declares: [\n {\n at: 'threads/_inert_probe.ts',\n },\n ],\n}\n" },
|
|
74
|
+
{ at: 'threads/_inert_probe.ts', source: "import type { Threaded } from '../thread/core/thread.ts'\n\nexport const M: Threaded = {\n declares: [\n { id: 'a-file', at: 'threads/_plain.ts' },\n ],\n}\n" },
|
|
75
|
+
{ at: 'threads/_plain.ts', source: 'export const PLAIN = 1\n' },
|
|
74
76
|
],
|
|
75
77
|
at: 'threads/_inert_probe.ts',
|
|
76
78
|
},
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { existsSync, readFileSync } from '../../
|
|
1
|
+
import { existsSync, readFileSync } from '../../integrations/disk.ts'
|
|
2
2
|
import { resolve } from 'node:path'
|
|
3
3
|
|
|
4
4
|
import type { Slopnator } from '../slopnator.ts'
|
|
@@ -63,17 +63,19 @@ export const slopnator: Slopnator = {
|
|
|
63
63
|
right: [
|
|
64
64
|
{
|
|
65
65
|
files: [
|
|
66
|
-
{ at: '
|
|
66
|
+
{ at: 'thread/core/thread.ts', source: "export interface Threaded {\n readonly id?: string\n readonly at?: string\n readonly describes?: string\n readonly governs?: readonly string[]\n readonly declares?: readonly Threaded[]\n readonly derives?: readonly Threaded[]\n readonly seeds?: readonly Threaded[]\n}\n" },
|
|
67
|
+
{ at: 'threads/thread.ts', source: "import type { Threaded } from '../thread/core/thread.ts'\n\nexport const THREAD: Threaded = {\n declares: [\n {\n at: 'threads/_inert_probe.ts',\n },\n ],\n}\n" },
|
|
68
|
+
{ at: 'threads/_inert_probe.ts', source: "import type { Threaded } from '../thread/core/thread.ts'\n\nexport const M: Threaded = {\n declares: [\n { id: 'events', at: 'src/islands/events.ts', governs: ['app/_declare/events.ts'] },\n ],\n}\n" },
|
|
67
69
|
{ at: 'src/islands/events.ts', source: 'export const EventsIslandSchema = {}\n' },
|
|
68
70
|
],
|
|
69
71
|
at: 'threads/_inert_probe.ts',
|
|
70
72
|
},
|
|
71
73
|
{
|
|
72
74
|
files: [
|
|
73
|
-
{
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
},
|
|
75
|
+
{ 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" },
|
|
76
|
+
{ at: 'threads/thread.ts', source: "import type { Threaded } from '../thread/core/thread.ts'\n\nexport const THREAD: Threaded = {\n declares: [\n {\n at: 'threads/_inert_probe.ts',\n },\n ],\n}\n" },
|
|
77
|
+
{ at: 'threads/_inert_probe.ts', source: "import type { Threaded } from '../thread/core/thread.ts'\n\nexport const M: Threaded = {\n declares: [\n { id: 'a-file', at: 'threads/_plain.ts' },\n ],\n}\n" },
|
|
78
|
+
{ at: 'threads/_plain.ts', source: 'export const PLAIN = 1\n' },
|
|
77
79
|
],
|
|
78
80
|
at: 'threads/_inert_probe.ts',
|
|
79
81
|
},
|
|
@@ -1,33 +1,11 @@
|
|
|
1
|
-
import { existsSync, readFileSync, readdirSync, statSync } from
|
|
1
|
+
import { existsSync, readFileSync, readdirSync, statSync } from '../../integrations/disk.js';
|
|
2
2
|
import { join } from 'node:path';
|
|
3
|
-
import { ORGANISATION, ours } from
|
|
4
|
-
import { findTree } from
|
|
5
|
-
/**
|
|
6
|
-
* A row the package still ships and the tree no longer holds.
|
|
7
|
-
*
|
|
8
|
-
* Read as TEXT, deliberately. Eight laws were once added whose walks imported a workspace package,
|
|
9
|
-
* plain Node could not resolve it, `load()` swallowed the failure, and all eight read from every
|
|
10
|
-
* caller exactly like laws finding nothing. Nothing here imports anything but node builtins.
|
|
11
|
-
*/
|
|
12
|
-
/** A node: a const annotated `Threaded`. */
|
|
3
|
+
import { ORGANISATION, ours } from '../../core/scope.js';
|
|
4
|
+
import { findTree } from '../../core/tree.js';
|
|
13
5
|
const NODE = /export\s+const\s+[A-Za-z_$][\w$]*\s*:\s*Threaded\b/;
|
|
14
|
-
/**
|
|
15
|
-
* Where a node's rows are written.
|
|
16
|
-
*
|
|
17
|
-
* A node may open the list inline, or name a `declares` array declared beside it and reference it by
|
|
18
|
-
* shorthand. Only the first was read, so `@thread/ui`'s tokens and signals nodes — 107 of its 178
|
|
19
|
-
* shipped rows — reported as shipping nothing, and every one of them could be deleted in silence.
|
|
20
|
-
*/
|
|
21
6
|
const SHAPES = [/declares:\s*\[/g, /export\s+const\s+declares\b[^\n]*?=\s*\[/g];
|
|
22
|
-
/**
|
|
23
|
-
* How a row states its id.
|
|
24
|
-
*
|
|
25
|
-
* A row may be an object literal, or a call to a factory declared beside the list that takes the id
|
|
26
|
-
* first. Only the literal was read, which is the other half of why tokens and signals were invisible.
|
|
27
|
-
*/
|
|
28
7
|
const ID = /\bid:\s*'([^']+)'/g;
|
|
29
8
|
const CALLED = /(?:^|[\s,[(])[a-zA-Z_$][\w$]*\(\s*'([^']+)'/g;
|
|
30
|
-
/** Strings out first, so a fixture that PRINTS a declaration is not read as being one. */
|
|
31
9
|
const QUOTED = /"(?:[^"\\\n]|\\.)*"|`(?:[^`\\]|\\.)*`/g;
|
|
32
10
|
function closes(code, from) {
|
|
33
11
|
let depth = 0;
|
|
@@ -42,13 +20,6 @@ function closes(code, from) {
|
|
|
42
20
|
}
|
|
43
21
|
return -1;
|
|
44
22
|
}
|
|
45
|
-
/**
|
|
46
|
-
* What a package SHIPS: only the ids inside a `declares` list.
|
|
47
|
-
*
|
|
48
|
-
* It was every id anywhere in a file that held one such list, which read an `Appending`'s own `id`
|
|
49
|
-
* as a shipped row — `@thread/operations` was reported as shipping `seed-generator`, the name of a
|
|
50
|
-
* row it asks an app to APPEND into another list, not one it declares.
|
|
51
|
-
*/
|
|
52
23
|
function shippedIn(text) {
|
|
53
24
|
const code = text.replace(QUOTED, '""');
|
|
54
25
|
if (!NODE.test(code))
|
|
@@ -69,13 +40,6 @@ function shippedIn(text) {
|
|
|
69
40
|
}
|
|
70
41
|
return found;
|
|
71
42
|
}
|
|
72
|
-
/**
|
|
73
|
-
* What a tree HOLDS: every id in it, whichever list it sits in.
|
|
74
|
-
*
|
|
75
|
-
* Gating this on `declares` made every row in `derivation.ts` invisible, because its node states no
|
|
76
|
-
* `declares` at all and its rows live in `GENERATORS`. A row plainly on disk read as missing, and
|
|
77
|
-
* running the command this law recommends would not have cleared the finding.
|
|
78
|
-
*/
|
|
79
43
|
function heldIn(text) {
|
|
80
44
|
const code = text.replace(QUOTED, '""');
|
|
81
45
|
const found = new Set();
|
|
@@ -93,7 +57,6 @@ function filesIn(dir) {
|
|
|
93
57
|
return [];
|
|
94
58
|
}
|
|
95
59
|
}
|
|
96
|
-
/** Every installed thread package that ships nodes of its own. */
|
|
97
60
|
function installed(root) {
|
|
98
61
|
const at = join(root, 'node_modules', ORGANISATION);
|
|
99
62
|
const found = [];
|
|
@@ -126,19 +89,10 @@ function read(path) {
|
|
|
126
89
|
export const slopnator = {
|
|
127
90
|
checks: (root) => {
|
|
128
91
|
const tree = findTree(root);
|
|
129
|
-
/* Every id the tree holds, across every node in it. */
|
|
130
92
|
const held = new Set();
|
|
131
93
|
for (const path of filesIn(tree.dir))
|
|
132
94
|
for (const one of heldIn(read(path)))
|
|
133
95
|
held.add(one);
|
|
134
|
-
/*
|
|
135
|
-
* Only packages this tree ATTACHED, not every one installed.
|
|
136
|
-
*
|
|
137
|
-
* Measured by running it: the first version named `@thread/knowledge`, which the audit app has
|
|
138
|
-
* in `node_modules` and never attached — so the law demanded rows for a package nobody had
|
|
139
|
-
* asked for. A package is attached when the tree names a file inside it; one nothing points at
|
|
140
|
-
* is a dependency, and a dependency ships nothing this tree promised to hold.
|
|
141
|
-
*/
|
|
142
96
|
const wired = new Set();
|
|
143
97
|
for (const path of filesIn(tree.dir)) {
|
|
144
98
|
for (const one of read(path).matchAll(/node_modules\/(@[\w.-]+\/[\w.-]+)\//g))
|
|
@@ -153,7 +107,6 @@ export const slopnator = {
|
|
|
153
107
|
if (shipped.size === 0)
|
|
154
108
|
continue;
|
|
155
109
|
for (const id of [...shipped].sort()) {
|
|
156
|
-
/* The node's own id names the list, not a row in it. */
|
|
157
110
|
if (held.has(id))
|
|
158
111
|
continue;
|
|
159
112
|
found.push(`${pkg.name} ships \`${id}\` and this tree does not hold it — run \`thread attach ${pkg.name}\` to put it back`);
|
|
@@ -175,7 +128,9 @@ export const slopnator = {
|
|
|
175
128
|
right: [
|
|
176
129
|
{
|
|
177
130
|
files: [
|
|
178
|
-
{ at: '
|
|
131
|
+
{ 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" },
|
|
132
|
+
{ 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_missing.ts',\n },\n ],\n}\n" },
|
|
133
|
+
{ at: '_threads/_probe_missing.ts', source: "import type { Threaded } from '../thread/core/thread.ts'\n\nexport const M: Threaded = {\n declares: [\n {\n id: 'colour',\n },\n {\n id: 'missing-thing',\n },\n {\n at: 'node_modules/@prestack/thread-probe/dist/foo.ts',\n },\n ],\n}\n" },
|
|
179
134
|
{ at: 'node_modules/@prestack/thread-probe/dist/foo.ts', source: "export const PKG: Threaded = {\n declares: [\n {\n id: 'missing-thing',\n },\n ],\n}\n" },
|
|
180
135
|
],
|
|
181
136
|
at: '_threads/_probe_missing.ts',
|
|
@@ -1,40 +1,17 @@
|
|
|
1
|
-
import { existsSync, readFileSync, readdirSync, statSync } from '../../
|
|
1
|
+
import { existsSync, readFileSync, readdirSync, statSync } from '../../integrations/disk.ts'
|
|
2
2
|
import { join } from 'node:path'
|
|
3
3
|
|
|
4
4
|
import type { Slopnator } from '../slopnator.ts'
|
|
5
5
|
import { ORGANISATION, ours } from '../../core/scope.ts'
|
|
6
6
|
import { findTree } from '../../core/tree.ts'
|
|
7
7
|
|
|
8
|
-
/**
|
|
9
|
-
* A row the package still ships and the tree no longer holds.
|
|
10
|
-
*
|
|
11
|
-
* Read as TEXT, deliberately. Eight laws were once added whose walks imported a workspace package,
|
|
12
|
-
* plain Node could not resolve it, `load()` swallowed the failure, and all eight read from every
|
|
13
|
-
* caller exactly like laws finding nothing. Nothing here imports anything but node builtins.
|
|
14
|
-
*/
|
|
15
|
-
|
|
16
|
-
/** A node: a const annotated `Threaded`. */
|
|
17
8
|
const NODE = /export\s+const\s+[A-Za-z_$][\w$]*\s*:\s*Threaded\b/
|
|
18
9
|
|
|
19
|
-
/**
|
|
20
|
-
* Where a node's rows are written.
|
|
21
|
-
*
|
|
22
|
-
* A node may open the list inline, or name a `declares` array declared beside it and reference it by
|
|
23
|
-
* shorthand. Only the first was read, so `@thread/ui`'s tokens and signals nodes — 107 of its 178
|
|
24
|
-
* shipped rows — reported as shipping nothing, and every one of them could be deleted in silence.
|
|
25
|
-
*/
|
|
26
10
|
const SHAPES = [/declares:\s*\[/g, /export\s+const\s+declares\b[^\n]*?=\s*\[/g]
|
|
27
11
|
|
|
28
|
-
/**
|
|
29
|
-
* How a row states its id.
|
|
30
|
-
*
|
|
31
|
-
* A row may be an object literal, or a call to a factory declared beside the list that takes the id
|
|
32
|
-
* first. Only the literal was read, which is the other half of why tokens and signals were invisible.
|
|
33
|
-
*/
|
|
34
12
|
const ID = /\bid:\s*'([^']+)'/g
|
|
35
13
|
const CALLED = /(?:^|[\s,[(])[a-zA-Z_$][\w$]*\(\s*'([^']+)'/g
|
|
36
14
|
|
|
37
|
-
/** Strings out first, so a fixture that PRINTS a declaration is not read as being one. */
|
|
38
15
|
const QUOTED = /"(?:[^"\\\n]|\\.)*"|`(?:[^`\\]|\\.)*`/g
|
|
39
16
|
|
|
40
17
|
function closes(code: string, from: number): number {
|
|
@@ -49,13 +26,6 @@ function closes(code: string, from: number): number {
|
|
|
49
26
|
return -1
|
|
50
27
|
}
|
|
51
28
|
|
|
52
|
-
/**
|
|
53
|
-
* What a package SHIPS: only the ids inside a `declares` list.
|
|
54
|
-
*
|
|
55
|
-
* It was every id anywhere in a file that held one such list, which read an `Appending`'s own `id`
|
|
56
|
-
* as a shipped row — `@thread/operations` was reported as shipping `seed-generator`, the name of a
|
|
57
|
-
* row it asks an app to APPEND into another list, not one it declares.
|
|
58
|
-
*/
|
|
59
29
|
function shippedIn(text: string): ReadonlySet<string> {
|
|
60
30
|
const code = text.replace(QUOTED, '""')
|
|
61
31
|
if (!NODE.test(code)) return new Set()
|
|
@@ -73,13 +43,6 @@ function shippedIn(text: string): ReadonlySet<string> {
|
|
|
73
43
|
return found
|
|
74
44
|
}
|
|
75
45
|
|
|
76
|
-
/**
|
|
77
|
-
* What a tree HOLDS: every id in it, whichever list it sits in.
|
|
78
|
-
*
|
|
79
|
-
* Gating this on `declares` made every row in `derivation.ts` invisible, because its node states no
|
|
80
|
-
* `declares` at all and its rows live in `GENERATORS`. A row plainly on disk read as missing, and
|
|
81
|
-
* running the command this law recommends would not have cleared the finding.
|
|
82
|
-
*/
|
|
83
46
|
function heldIn(text: string): ReadonlySet<string> {
|
|
84
47
|
const code = text.replace(QUOTED, '""')
|
|
85
48
|
const found = new Set<string>()
|
|
@@ -96,7 +59,6 @@ function filesIn(dir: string): readonly string[] {
|
|
|
96
59
|
}
|
|
97
60
|
}
|
|
98
61
|
|
|
99
|
-
/** Every installed thread package that ships nodes of its own. */
|
|
100
62
|
function installed(root: string): readonly { readonly name: string; readonly src: string }[] {
|
|
101
63
|
const at = join(root, 'node_modules', ORGANISATION)
|
|
102
64
|
const found: { name: string; src: string }[] = []
|
|
@@ -129,18 +91,9 @@ export const slopnator: Slopnator = {
|
|
|
129
91
|
checks: (root) => {
|
|
130
92
|
const tree = findTree(root)
|
|
131
93
|
|
|
132
|
-
/* Every id the tree holds, across every node in it. */
|
|
133
94
|
const held = new Set<string>()
|
|
134
95
|
for (const path of filesIn(tree.dir)) for (const one of heldIn(read(path))) held.add(one)
|
|
135
96
|
|
|
136
|
-
/*
|
|
137
|
-
* Only packages this tree ATTACHED, not every one installed.
|
|
138
|
-
*
|
|
139
|
-
* Measured by running it: the first version named `@thread/knowledge`, which the audit app has
|
|
140
|
-
* in `node_modules` and never attached — so the law demanded rows for a package nobody had
|
|
141
|
-
* asked for. A package is attached when the tree names a file inside it; one nothing points at
|
|
142
|
-
* is a dependency, and a dependency ships nothing this tree promised to hold.
|
|
143
|
-
*/
|
|
144
97
|
const wired = new Set<string>()
|
|
145
98
|
for (const path of filesIn(tree.dir)) {
|
|
146
99
|
for (const one of read(path).matchAll(/node_modules\/(@[\w.-]+\/[\w.-]+)\//g)) wired.add(one[1] ?? '')
|
|
@@ -153,7 +106,6 @@ export const slopnator: Slopnator = {
|
|
|
153
106
|
const shipped = shippedIn(read(path))
|
|
154
107
|
if (shipped.size === 0) continue
|
|
155
108
|
for (const id of [...shipped].sort()) {
|
|
156
|
-
/* The node's own id names the list, not a row in it. */
|
|
157
109
|
if (held.has(id)) continue
|
|
158
110
|
found.push(
|
|
159
111
|
`${pkg.name} ships \`${id}\` and this tree does not hold it — run \`thread attach ${pkg.name}\` to put it back`,
|
|
@@ -176,7 +128,9 @@ export const slopnator: Slopnator = {
|
|
|
176
128
|
right: [
|
|
177
129
|
{
|
|
178
130
|
files: [
|
|
179
|
-
{ at: '
|
|
131
|
+
{ 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" },
|
|
132
|
+
{ 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_missing.ts',\n },\n ],\n}\n" },
|
|
133
|
+
{ at: '_threads/_probe_missing.ts', source: "import type { Threaded } from '../thread/core/thread.ts'\n\nexport const M: Threaded = {\n declares: [\n {\n id: 'colour',\n },\n {\n id: 'missing-thing',\n },\n {\n at: 'node_modules/@prestack/thread-probe/dist/foo.ts',\n },\n ],\n}\n" },
|
|
180
134
|
{ at: 'node_modules/@prestack/thread-probe/dist/foo.ts', source: "export const PKG: Threaded = {\n declares: [\n {\n id: 'missing-thing',\n },\n ],\n}\n" },
|
|
181
135
|
],
|
|
182
136
|
at: '_threads/_probe_missing.ts',
|
|
@@ -1,38 +1,56 @@
|
|
|
1
|
-
|
|
1
|
+
import { dirname, join, normalize } from 'node:path';
|
|
2
|
+
import { ROOT, descend, read, reached } from '../../core/descend.js';
|
|
3
|
+
import { rowsIn } from './sl_unthreaded.js';
|
|
4
|
+
const DOOR = 'programs';
|
|
5
|
+
const ROOTED = ROOT + '/';
|
|
6
|
+
const slash = (one) => one.split('\\').join('/');
|
|
7
|
+
const bare = (one) => one.replace(/\.[^./]+$/, '');
|
|
8
|
+
function doorway() {
|
|
9
|
+
descend();
|
|
10
|
+
for (const at of reached()) {
|
|
11
|
+
const text = read(at);
|
|
12
|
+
if (text === undefined)
|
|
13
|
+
continue;
|
|
14
|
+
for (const one of rowsIn(text)) {
|
|
15
|
+
if (one.id === DOOR && one.at !== undefined)
|
|
16
|
+
return one.at;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
return undefined;
|
|
20
|
+
}
|
|
2
21
|
const named = (held) => {
|
|
3
22
|
const one = held;
|
|
4
23
|
return one?.type === 'Literal' && typeof one.value === 'string' ? one.value : undefined;
|
|
5
24
|
};
|
|
6
25
|
export const slopnator = {
|
|
7
26
|
on: ['ImportDeclaration', 'ImportExpression', 'CallExpression'],
|
|
8
|
-
holds: (node) => {
|
|
27
|
+
holds: (node, at) => {
|
|
9
28
|
const held = node;
|
|
10
29
|
const from = held.type === 'ImportDeclaration' || held.type === 'ImportExpression'
|
|
11
30
|
? (named(held.source) ?? named(held.arguments?.[0]))
|
|
12
31
|
: held.callee?.name === 'require'
|
|
13
32
|
? named(held.arguments?.[0])
|
|
14
33
|
: undefined;
|
|
15
|
-
if (from === undefined
|
|
34
|
+
if (from === undefined)
|
|
16
35
|
return false;
|
|
17
|
-
|
|
36
|
+
const door = doorway();
|
|
37
|
+
if (door === undefined)
|
|
38
|
+
return false;
|
|
39
|
+
const here = at.filename.startsWith(ROOTED) ? at.filename.slice(ROOTED.length) : at.filename;
|
|
40
|
+
const aimed = from.startsWith('.') ? slash(normalize(join(dirname(here), from))) : from;
|
|
41
|
+
if (bare(aimed) !== bare(door))
|
|
42
|
+
return false;
|
|
43
|
+
return ('it imports ' + door + ', which is where every process this framework starts comes from. A check that can reach it can ' +
|
|
44
|
+
'start something, and whatever it starts can reach back into the enforcement that called it');
|
|
18
45
|
},
|
|
19
46
|
proves: {
|
|
20
47
|
wrong: [
|
|
21
48
|
{
|
|
22
49
|
files: [
|
|
23
|
-
{
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
},
|
|
27
|
-
],
|
|
28
|
-
at: 'thread/integrity/slopnators/sl_probe_spawn.ts',
|
|
29
|
-
},
|
|
30
|
-
{
|
|
31
|
-
files: [
|
|
32
|
-
{
|
|
33
|
-
at: 'thread/integrity/slopnators/sl_probe_spawn.ts',
|
|
34
|
-
source: "const held = await import('child_process')\nexport const slopnator = { holds: () => false, proves: { wrong: ['a'], right: ['b'] } }\n",
|
|
35
|
-
},
|
|
50
|
+
{ 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" },
|
|
51
|
+
{ 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" },
|
|
52
|
+
{ 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" },
|
|
53
|
+
{ 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" },
|
|
36
54
|
],
|
|
37
55
|
at: 'thread/integrity/slopnators/sl_probe_spawn.ts',
|
|
38
56
|
},
|
|
@@ -42,7 +60,7 @@ export const slopnator = {
|
|
|
42
60
|
files: [
|
|
43
61
|
{
|
|
44
62
|
at: 'thread/integrity/slopnators/sl_probe_spawn.ts',
|
|
45
|
-
source: "import {
|
|
63
|
+
source: "import { dirname } from 'node:path'\nexport const slopnator = { holds: () => false, proves: { wrong: ['a'], right: ['b'] } }\n",
|
|
46
64
|
},
|
|
47
65
|
],
|
|
48
66
|
at: 'thread/integrity/slopnators/sl_probe_spawn.ts',
|