@prestack/thread 0.1.20 → 0.1.22
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 +193 -0
- package/dist/core/attach.js +21 -0
- package/dist/core/descend.d.ts +2 -0
- package/dist/core/descend.js +18 -3
- package/dist/core/generators/commit.js +55 -3
- package/dist/core/generators/dev.d.ts +1 -0
- package/dist/core/generators/dev.js +68 -0
- package/dist/core/rows.d.ts +5 -0
- package/dist/core/rows.js +45 -0
- package/dist/core/tree.d.ts +1 -0
- package/dist/core/tree.js +7 -1
- package/dist/derivation/generators/build.d.ts +3 -1
- package/dist/derivation/generators/build.js +103 -22
- package/dist/derivation/generators/release.js +74 -26
- package/dist/derivation/release.js +3 -3
- package/dist/integrations/cli.d.ts +3 -9
- package/dist/integrations/cli.js +96 -36
- package/dist/integrations/declared.d.ts +8 -0
- package/dist/integrations/declared.js +22 -0
- package/dist/integrations/pglite.js +13 -3
- package/dist/integrations/target.d.ts +4 -1
- package/dist/integrations/target.js +20 -6
- package/dist/integrity/attach.js +1 -5
- package/dist/integrity/generators/publish.js +2 -1
- package/dist/integrity/slopnators/sl_stale_path.js +10 -0
- package/dist/integrity/slopnators/sl_unlisted_program.js +2 -2
- package/dist/operations/access.js +3 -2
- package/dist/operations/capabilities.d.ts +2 -0
- package/dist/operations/capabilities.js +2 -0
- package/dist/operations/generators/capability.js +234 -24
- package/dist/operations/generators/compile.d.ts +21 -0
- package/dist/operations/generators/compile.js +151 -51
- package/dist/operations/generators/permits.d.ts +2 -1
- package/dist/operations/generators/permits.js +52 -47
- package/dist/operations/generators/seed.d.ts +3 -0
- package/dist/operations/generators/seed.js +25 -3
- package/dist/operations/nodes.d.ts +2 -2
- package/package.json +1 -1
- package/dist/core/actor.ts +0 -21
- package/dist/core/attach.ts +0 -613
- package/dist/core/chain.ts +0 -145
- package/dist/core/condition.ts +0 -227
- package/dist/core/descend.ts +0 -1173
- package/dist/core/errors/catalogue.ts +0 -314
- package/dist/core/errors/classes.ts +0 -51
- package/dist/core/errors/error.ts +0 -152
- package/dist/core/errors/namespaces.ts +0 -19
- package/dist/core/finding.ts +0 -37
- package/dist/core/generators/commit.ts +0 -157
- package/dist/core/init.ts +0 -222
- package/dist/core/invoked.ts +0 -52
- package/dist/core/round.ts +0 -74
- package/dist/core/rows.ts +0 -459
- package/dist/core/scope.ts +0 -20
- package/dist/core/thread.ts +0 -29
- package/dist/core/tree.ts +0 -90
- package/dist/core/types.ts +0 -351
- package/dist/derivation/attach.ts +0 -57
- package/dist/derivation/generators/build.ts +0 -371
- package/dist/derivation/generators/check.ts +0 -118
- package/dist/derivation/generators/determinism.ts +0 -145
- package/dist/derivation/generators/release.ts +0 -236
- package/dist/derivation/release.ts +0 -43
- package/dist/integrations/attach.ts +0 -187
- package/dist/integrations/cli.ts +0 -615
- package/dist/integrations/disk.ts +0 -466
- package/dist/integrations/generators/read.ts +0 -127
- package/dist/integrations/generators/vault.ts +0 -210
- package/dist/integrations/https.ts +0 -216
- package/dist/integrations/pglite.ts +0 -206
- package/dist/integrations/secret.ts +0 -54
- package/dist/integrations/target.ts +0 -656
- package/dist/integrations/vault.ts +0 -148
- package/dist/integrity/attach.ts +0 -150
- package/dist/integrity/check-threads.ts +0 -166
- package/dist/integrity/constraint.ts +0 -1246
- package/dist/integrity/generators/compiler.ts +0 -138
- package/dist/integrity/generators/gate.ts +0 -227
- package/dist/integrity/generators/issues.ts +0 -160
- package/dist/integrity/generators/publish.ts +0 -232
- package/dist/integrity/generators/trial.ts +0 -359
- package/dist/integrity/lint.ts +0 -214
- package/dist/integrity/lsp/bin.ts +0 -44
- package/dist/integrity/lsp/diagnostics.ts +0 -92
- package/dist/integrity/lsp/findings.ts +0 -63
- package/dist/integrity/lsp/lint.ts +0 -124
- package/dist/integrity/lsp/protocol.ts +0 -78
- package/dist/integrity/lsp/server.ts +0 -224
- package/dist/integrity/slopnator.ts +0 -324
- package/dist/integrity/slopnators/sl_broken_lineage.ts +0 -38
- package/dist/integrity/slopnators/sl_declared_elsewhere.ts +0 -60
- package/dist/integrity/slopnators/sl_derived_drift.ts +0 -101
- package/dist/integrity/slopnators/sl_door_in_a_node.ts +0 -250
- package/dist/integrity/slopnators/sl_hardcoded_style.ts +0 -55
- package/dist/integrity/slopnators/sl_inert_island.ts +0 -84
- package/dist/integrity/slopnators/sl_inert_slopnator.ts +0 -93
- package/dist/integrity/slopnators/sl_missing_row.ts +0 -140
- package/dist/integrity/slopnators/sl_no_barrel.ts +0 -48
- package/dist/integrity/slopnators/sl_no_child_process.ts +0 -100
- package/dist/integrity/slopnators/sl_no_comment.ts +0 -43
- package/dist/integrity/slopnators/sl_no_dispatch_bypass.ts +0 -276
- package/dist/integrity/slopnators/sl_no_import_cycle.ts +0 -66
- package/dist/integrity/slopnators/sl_no_such_command.ts +0 -81
- package/dist/integrity/slopnators/sl_only_takes_a_literal.ts +0 -63
- package/dist/integrity/slopnators/sl_outside_an_operation.ts +0 -25
- package/dist/integrity/slopnators/sl_screen_holds_only_declared.ts +0 -70
- package/dist/integrity/slopnators/sl_secret_literal.ts +0 -68
- package/dist/integrity/slopnators/sl_stale_path.ts +0 -178
- package/dist/integrity/slopnators/sl_twice_meant.ts +0 -57
- package/dist/integrity/slopnators/sl_twice_parented.ts +0 -82
- package/dist/integrity/slopnators/sl_undeclared_crossing.ts +0 -481
- package/dist/integrity/slopnators/sl_undeclared_destination.ts +0 -113
- package/dist/integrity/slopnators/sl_undeclared_element.ts +0 -44
- package/dist/integrity/slopnators/sl_undeclared_list.ts +0 -358
- package/dist/integrity/slopnators/sl_undeclared_node.ts +0 -75
- package/dist/integrity/slopnators/sl_undeclared_operation.ts +0 -257
- package/dist/integrity/slopnators/sl_undeclared_variant.ts +0 -152
- package/dist/integrity/slopnators/sl_undeclared_webhook.ts +0 -108
- package/dist/integrity/slopnators/sl_underived_route.ts +0 -72
- package/dist/integrity/slopnators/sl_undescribed.ts +0 -65
- package/dist/integrity/slopnators/sl_ungoverned_island.ts +0 -60
- package/dist/integrity/slopnators/sl_unlisted_program.ts +0 -46
- package/dist/integrity/slopnators/sl_unnamed_island.ts +0 -41
- package/dist/integrity/slopnators/sl_unpaired_member.ts +0 -139
- package/dist/integrity/slopnators/sl_unpaired_slopnator.ts +0 -71
- package/dist/integrity/slopnators/sl_unprefixed_member.ts +0 -110
- package/dist/integrity/slopnators/sl_unthreaded.ts +0 -421
- package/dist/integrity/slopnators/sl_untranslated_key.ts +0 -140
- package/dist/integrity/slopnators/sl_unvaulted_secret.ts +0 -132
- package/dist/integrity/slopnators/sl_unversioned.ts +0 -52
- package/dist/integrity/sweep.ts +0 -395
- package/dist/knowledge/attach.ts +0 -327
- package/dist/knowledge/generators/docsgen.ts +0 -186
- package/dist/knowledge/generators/glossary.ts +0 -73
- package/dist/knowledge/glossary.ts +0 -6
- package/dist/operations/access.ts +0 -131
- package/dist/operations/attach.ts +0 -408
- package/dist/operations/capabilities.ts +0 -88
- package/dist/operations/dispatch.ts +0 -294
- package/dist/operations/error-results.ts +0 -31
- package/dist/operations/generators/capability.ts +0 -1054
- package/dist/operations/generators/compile.ts +0 -701
- package/dist/operations/generators/permits.ts +0 -224
- package/dist/operations/generators/seed.ts +0 -359
- package/dist/operations/generators/trial.ts +0 -666
- package/dist/operations/nodes.ts +0 -53
- package/dist/operations/operations.ts +0 -91
- package/dist/operations/result.ts +0 -28
|
@@ -1,101 +0,0 @@
|
|
|
1
|
-
import { readFileSync } from '../../integrations/disk.ts'
|
|
2
|
-
import { resolve } from 'node:path'
|
|
3
|
-
|
|
4
|
-
import type { Slopnator } from '../slopnator.ts'
|
|
5
|
-
import { atsIn } from './sl_unthreaded.ts'
|
|
6
|
-
import { reached } from '../../core/descend.ts'
|
|
7
|
-
|
|
8
|
-
export interface Derivation {
|
|
9
|
-
readonly at: string
|
|
10
|
-
readonly by?: string
|
|
11
|
-
readonly runs?: string
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export function derivations(root: string): readonly Derivation[] {
|
|
15
|
-
const found: Derivation[] = []
|
|
16
|
-
for (const at of reached()) {
|
|
17
|
-
let text: string
|
|
18
|
-
try {
|
|
19
|
-
text = readFileSync(resolve(root, at), 'utf8')
|
|
20
|
-
} catch {
|
|
21
|
-
continue
|
|
22
|
-
}
|
|
23
|
-
const threadedDerives = new Set(atsIn(text, new Set(['derives'])))
|
|
24
|
-
for (const opens of text.matchAll(/^\s*derives:\s*\[/gm)) {
|
|
25
|
-
const from = opens.index ?? 0
|
|
26
|
-
let depth = 1
|
|
27
|
-
let cursor = from + opens[0].length
|
|
28
|
-
while (cursor < text.length && depth > 0) {
|
|
29
|
-
const one = text[cursor]
|
|
30
|
-
if (one === '[') depth += 1
|
|
31
|
-
else if (one === ']') depth -= 1
|
|
32
|
-
cursor += 1
|
|
33
|
-
}
|
|
34
|
-
const own: string[] = []
|
|
35
|
-
let nesting = 0
|
|
36
|
-
for (const line of text.slice(from, cursor).split('\n')) {
|
|
37
|
-
if (nesting === 1) own.push(line)
|
|
38
|
-
for (const one of line) {
|
|
39
|
-
if (one === '[') nesting += 1
|
|
40
|
-
else if (one === ']') nesting -= 1
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
const block = own.join('\n')
|
|
44
|
-
for (const row of block.split(/^\s*\{$/m)) {
|
|
45
|
-
const at = /^\s*at:\s*'([^']+)'/m.exec(row)?.[1]
|
|
46
|
-
const by = /^\s*by:\s*'([^']+)'/m.exec(row)?.[1]
|
|
47
|
-
const runs = /^\s*runs:\s*'([^']+)'/m.exec(row)?.[1]
|
|
48
|
-
if (at !== undefined && threadedDerives.has(at)) found.push({ at, by, runs })
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
return found
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
export const slopnator: Slopnator = {
|
|
56
|
-
checks: (root) => {
|
|
57
|
-
const findings: string[] = []
|
|
58
|
-
const all = derivations(root)
|
|
59
|
-
const complete = all.filter((one) => one.by !== undefined && one.runs !== undefined)
|
|
60
|
-
const covered = new Set(complete.map((one) => one.at))
|
|
61
|
-
|
|
62
|
-
for (const at of new Set(all.map((one) => one.at))) {
|
|
63
|
-
if (covered.has(at)) continue
|
|
64
|
-
findings.push(
|
|
65
|
-
`${at} sits in a \`derives\` list and no row naming it states both \`by\` and \`runs\` — a byproduct nothing can re-run is a claim, and this check used to drop such a row in silence`,
|
|
66
|
-
)
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
return findings
|
|
70
|
-
},
|
|
71
|
-
proves: {
|
|
72
|
-
wrong: [
|
|
73
|
-
{
|
|
74
|
-
files: [
|
|
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/_drift_probe.ts',\n },\n ],\n}\n" },
|
|
77
|
-
{ at: 'threads/_drift_probe.ts', source: "import type { Threaded } from '../thread/core/thread.ts'\n\nexport const M: Threaded = {\n derives: [\n {\n at: 'scripts/_generated/gate.mjs',\n by: 'pnpm gate:generate',\n },\n ],\n}\n" },
|
|
78
|
-
],
|
|
79
|
-
at: 'threads/_drift_probe.ts',
|
|
80
|
-
},
|
|
81
|
-
{
|
|
82
|
-
files: [
|
|
83
|
-
{ 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" },
|
|
84
|
-
{ at: 'threads/thread.ts', source: "import type { Threaded } from '../thread/core/thread.ts'\n\nexport const THREAD: Threaded = {\n declares: [\n {\n at: 'threads/_drift_probe.ts',\n },\n ],\n}\n" },
|
|
85
|
-
{ at: 'threads/_drift_probe.ts', source: "import type { Threaded } from '../thread/core/thread.ts'\n\nexport const M: Threaded = {\n derives: [\n {\n at: 'scripts/finding.ts',\n },\n ],\n}\n" },
|
|
86
|
-
],
|
|
87
|
-
at: 'threads/_drift_probe.ts',
|
|
88
|
-
},
|
|
89
|
-
],
|
|
90
|
-
right: [
|
|
91
|
-
{
|
|
92
|
-
files: [
|
|
93
|
-
{ 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" },
|
|
94
|
-
{ at: 'threads/thread.ts', source: "import type { Threaded } from '../thread/core/thread.ts'\n\nexport const THREAD: Threaded = {\n declares: [\n {\n at: 'threads/_drift_probe.ts',\n },\n ],\n}\n" },
|
|
95
|
-
{ at: 'threads/_drift_probe.ts', source: "import type { Threaded } from '../thread/core/thread.ts'\n\nexport const M: Threaded = {\n derives: [\n {\n at: 'scripts/_generated/gate.mjs',\n by: 'pnpm gate:generate',\n runs: 'pnpm gate:check',\n },\n ],\n}\n" },
|
|
96
|
-
],
|
|
97
|
-
at: 'threads/_drift_probe.ts',
|
|
98
|
-
},
|
|
99
|
-
],
|
|
100
|
-
},
|
|
101
|
-
}
|
|
@@ -1,250 +0,0 @@
|
|
|
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,55 +0,0 @@
|
|
|
1
|
-
import ts from 'typescript'
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
import type { Slopnator } from '../slopnator.ts'
|
|
5
|
-
|
|
6
|
-
const ALONE = /^\s*([a-z][a-z0-9]*(?:-[a-z0-9]+)*)\s*:\s*([^;{}\n]+);\s*$/
|
|
7
|
-
|
|
8
|
-
const NAMED = /var\(\s*--/
|
|
9
|
-
|
|
10
|
-
const PIECES = new Set([
|
|
11
|
-
ts.SyntaxKind.StringLiteral,
|
|
12
|
-
ts.SyntaxKind.NoSubstitutionTemplateLiteral,
|
|
13
|
-
ts.SyntaxKind.TemplateHead,
|
|
14
|
-
ts.SyntaxKind.TemplateMiddle,
|
|
15
|
-
ts.SyntaxKind.TemplateTail,
|
|
16
|
-
])
|
|
17
|
-
|
|
18
|
-
export const slopnator: Slopnator = {
|
|
19
|
-
holds: (_node, at) => {
|
|
20
|
-
const scanner = ts.createScanner(ts.ScriptTarget.Latest, false, ts.LanguageVariant.JSX, at.text)
|
|
21
|
-
let token = scanner.scan()
|
|
22
|
-
while (token !== ts.SyntaxKind.EndOfFileToken) {
|
|
23
|
-
if (PIECES.has(token)) {
|
|
24
|
-
const said = scanner.getTokenValue()
|
|
25
|
-
const found = ALONE.exec(said)
|
|
26
|
-
if (found !== null) {
|
|
27
|
-
const property = found[1] ?? ''
|
|
28
|
-
const value = (found[2] ?? '').trim()
|
|
29
|
-
if (value !== '' && !NAMED.test(value)) {
|
|
30
|
-
return (
|
|
31
|
-
`\`${property}\` is written into the compiler with \`${value}\` typed into it, so a ` +
|
|
32
|
-
'stylesheet carries a decision nothing declared — no rebrand moves it, no mode or theme reaches it, and no sheet knows it is there'
|
|
33
|
-
)
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
token = scanner.scan()
|
|
38
|
-
}
|
|
39
|
-
return false
|
|
40
|
-
},
|
|
41
|
-
proves: {
|
|
42
|
-
wrong: [
|
|
43
|
-
"lines.push(' body {', ' background: #f8fbfa;', ' }')",
|
|
44
|
-
"classes.push(' padding: 8px 16px;')",
|
|
45
|
-
"return [' :where(*) {', ' text-box-trim: trim-both;', ' }']",
|
|
46
|
-
"held.push(' color-scheme: light dark;')",
|
|
47
|
-
],
|
|
48
|
-
right: [
|
|
49
|
-
"lines.push(' body {', ' background: var(--colour-page);', ' }')",
|
|
50
|
-
'lines.push(...block(selector, made, indent))',
|
|
51
|
-
'held.push(` ${nameOf(id)}: ${made[id] ?? String()};`)',
|
|
52
|
-
'const held: readonly string[] = [];',
|
|
53
|
-
],
|
|
54
|
-
},
|
|
55
|
-
}
|
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
import { existsSync, readFileSync } from '../../integrations/disk.ts'
|
|
2
|
-
import { resolve } from 'node:path'
|
|
3
|
-
|
|
4
|
-
import type { Slopnator } from '../slopnator.ts'
|
|
5
|
-
import { declaresIsland, here, rowsIn } from './sl_unthreaded.ts'
|
|
6
|
-
import { findTree } from '../../core/tree.ts'
|
|
7
|
-
|
|
8
|
-
const ROOT = findTree().at
|
|
9
|
-
const TREE = `${findTree().name}/`
|
|
10
|
-
|
|
11
|
-
function schemaAt(at: string): string | undefined {
|
|
12
|
-
const path = resolve(ROOT, at)
|
|
13
|
-
if (!existsSync(path)) return undefined
|
|
14
|
-
try {
|
|
15
|
-
return declaresIsland(readFileSync(path, 'utf8'))
|
|
16
|
-
} catch {
|
|
17
|
-
return undefined
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export const slopnator: Slopnator = {
|
|
22
|
-
holds: (_node, at) => {
|
|
23
|
-
const path = here(at.filename)
|
|
24
|
-
if (!path.startsWith(TREE)) return false
|
|
25
|
-
|
|
26
|
-
let rows
|
|
27
|
-
try {
|
|
28
|
-
rows = rowsIn(at.text)
|
|
29
|
-
} catch {
|
|
30
|
-
return 'this file cannot be parsed, so the islands it declares were never read and nothing about what they govern was measured'
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
for (const row of rows) {
|
|
34
|
-
if (row.at === undefined || row.id === undefined) continue
|
|
35
|
-
const schema = schemaAt(row.at)
|
|
36
|
-
if (schema === undefined) continue
|
|
37
|
-
if ((row.governs ?? []).length > 0) continue
|
|
38
|
-
return (
|
|
39
|
-
`island "${row.id}" states nothing it governs, so ${schema}IslandSchema is a shape nothing is held to — ` +
|
|
40
|
-
'no file is the place its kind is declared, which makes every file the place, and the island is inert'
|
|
41
|
-
)
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
return false
|
|
45
|
-
},
|
|
46
|
-
proves: {
|
|
47
|
-
wrong: [
|
|
48
|
-
{
|
|
49
|
-
files: [
|
|
50
|
-
{ at: 'threads/_inert_probe.ts', source: "export const M: Threaded = {\n declares: [\n { id: 'entities', at: 'src/islands/entities.ts' },\n ],\n}\n" },
|
|
51
|
-
{ at: 'src/islands/entities.ts', source: 'export const EntitiesIslandSchema = {}\n' },
|
|
52
|
-
],
|
|
53
|
-
at: 'threads/_inert_probe.ts',
|
|
54
|
-
},
|
|
55
|
-
{
|
|
56
|
-
files: [
|
|
57
|
-
{ at: 'threads/_inert_probe.ts', source: "export const M: Threaded = {\n declares: [\n { id: 'events', at: 'src/islands/events.ts', governs: [] },\n ],\n}\n" },
|
|
58
|
-
{ at: 'src/islands/events.ts', source: 'export const EventsIslandSchema = {}\n' },
|
|
59
|
-
],
|
|
60
|
-
at: 'threads/_inert_probe.ts',
|
|
61
|
-
},
|
|
62
|
-
],
|
|
63
|
-
right: [
|
|
64
|
-
{
|
|
65
|
-
files: [
|
|
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" },
|
|
69
|
-
{ at: 'src/islands/events.ts', source: 'export const EventsIslandSchema = {}\n' },
|
|
70
|
-
],
|
|
71
|
-
at: 'threads/_inert_probe.ts',
|
|
72
|
-
},
|
|
73
|
-
{
|
|
74
|
-
files: [
|
|
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' },
|
|
79
|
-
],
|
|
80
|
-
at: 'threads/_inert_probe.ts',
|
|
81
|
-
},
|
|
82
|
-
],
|
|
83
|
-
},
|
|
84
|
-
}
|
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
import type { Slopnator } from '../slopnator.ts'
|
|
2
|
-
|
|
3
|
-
interface Node {
|
|
4
|
-
readonly type?: string
|
|
5
|
-
readonly name?: string
|
|
6
|
-
readonly key?: { readonly name?: string; readonly value?: unknown }
|
|
7
|
-
readonly value?: unknown
|
|
8
|
-
readonly body?: unknown
|
|
9
|
-
readonly properties?: readonly Node[]
|
|
10
|
-
readonly elements?: readonly unknown[]
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
const keyOf = (node: Node): string | undefined =>
|
|
14
|
-
node.key?.name ?? (typeof node.key?.value === 'string' ? node.key.value : undefined)
|
|
15
|
-
|
|
16
|
-
const answersUnwritten = (held: unknown): boolean => {
|
|
17
|
-
const fn = held as { type?: string; body?: { type?: string; name?: string } } | undefined
|
|
18
|
-
if (fn?.type !== 'ArrowFunctionExpression') return false
|
|
19
|
-
return fn.body?.type === 'Identifier' && fn.body.name === 'NOT_WRITTEN'
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
const statesNothing = (held: unknown): boolean => {
|
|
23
|
-
const proves = held as Node | undefined
|
|
24
|
-
if (proves?.type !== 'ObjectExpression') return false
|
|
25
|
-
return (proves.properties ?? []).some((one) => {
|
|
26
|
-
const key = keyOf(one)
|
|
27
|
-
if (key !== 'wrong' && key !== 'right') return false
|
|
28
|
-
const list = one.value as Node | undefined
|
|
29
|
-
return list?.type === 'ArrayExpression' && (list.elements ?? []).length === 0
|
|
30
|
-
})
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
export const slopnator: Slopnator = {
|
|
34
|
-
on: ['Property'],
|
|
35
|
-
holds: (node) => {
|
|
36
|
-
const held = node as Node
|
|
37
|
-
const key = keyOf(held)
|
|
38
|
-
|
|
39
|
-
if (key === 'holds' && answersUnwritten(held.value)) {
|
|
40
|
-
return 'this is still the scaffold — it answers `NOT_WRITTEN` to everything, so the rule that names it registers, loads, reports nothing, and reads from outside exactly like a rule being obeyed'
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
if (key === 'on') {
|
|
44
|
-
const list = held.value as Node | undefined
|
|
45
|
-
if (list?.type === 'ArrayExpression' && (list.elements ?? []).length === 0) {
|
|
46
|
-
return 'it says it visits syntax and then names no node type, so it is handed nothing and governs nothing inside the files its rule governs'
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
if (key === 'proves' && statesNothing(held.value)) {
|
|
51
|
-
return 'its `proves` states nothing — with no form it refuses and none it accepts, no trial is derived from it, so nothing anywhere shows the slopnator does what its row claims'
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
return false
|
|
55
|
-
},
|
|
56
|
-
proves: {
|
|
57
|
-
wrong: [
|
|
58
|
-
{
|
|
59
|
-
files: [
|
|
60
|
-
{
|
|
61
|
-
at: 'thread/integrity/slopnators/sl_probe_inert.ts',
|
|
62
|
-
source: 'export const slopnator = {\n on: [],\n holds: () => NOT_WRITTEN,\n proves: { wrong: [], right: [] },\n}\n',
|
|
63
|
-
},
|
|
64
|
-
],
|
|
65
|
-
at: 'thread/integrity/slopnators/sl_probe_inert.ts',
|
|
66
|
-
},
|
|
67
|
-
{
|
|
68
|
-
files: [
|
|
69
|
-
{
|
|
70
|
-
at: 'thread/integrity/slopnators/sl_probe_inert.ts',
|
|
71
|
-
source: "export const slopnator = {\n holds: (_node, at) => at.text.includes('nope') && 'found',\n proves: { wrong: [], right: ['const held = 1'] },\n}\n",
|
|
72
|
-
},
|
|
73
|
-
],
|
|
74
|
-
at: 'thread/integrity/slopnators/sl_probe_inert.ts',
|
|
75
|
-
},
|
|
76
|
-
],
|
|
77
|
-
right: [
|
|
78
|
-
{
|
|
79
|
-
files: [
|
|
80
|
-
{
|
|
81
|
-
at: 'thread/integrity/slopnators/sl_probe_inert.ts',
|
|
82
|
-
source: "export const slopnator = {\n holds: (_node, at) => at.text.includes('nope') && 'found',\n proves: { wrong: ['nope'], right: ['const held = 1'] },\n}\n",
|
|
83
|
-
},
|
|
84
|
-
],
|
|
85
|
-
at: 'thread/integrity/slopnators/sl_probe_inert.ts',
|
|
86
|
-
},
|
|
87
|
-
{
|
|
88
|
-
files: [{ at: 'thread/integrity/slopnators/sl_probe_inert.ts', source: 'export const held = 1\n' }],
|
|
89
|
-
at: 'thread/integrity/slopnators/sl_probe_inert.ts',
|
|
90
|
-
},
|
|
91
|
-
],
|
|
92
|
-
},
|
|
93
|
-
}
|