@prestack/thread 0.1.21 → 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.
Files changed (137) hide show
  1. package/CHANGELOG.md +109 -0
  2. package/dist/core/attach.js +21 -0
  3. package/dist/core/descend.js +6 -3
  4. package/dist/core/generators/commit.js +55 -3
  5. package/dist/core/generators/dev.d.ts +1 -0
  6. package/dist/core/generators/dev.js +68 -0
  7. package/dist/core/tree.d.ts +1 -0
  8. package/dist/core/tree.js +7 -1
  9. package/dist/derivation/generators/build.d.ts +3 -1
  10. package/dist/derivation/generators/build.js +103 -22
  11. package/dist/derivation/generators/release.js +74 -26
  12. package/dist/derivation/release.js +3 -3
  13. package/dist/integrations/cli.js +69 -10
  14. package/dist/integrations/pglite.js +13 -3
  15. package/dist/integrations/target.d.ts +3 -0
  16. package/dist/integrity/attach.js +1 -5
  17. package/dist/integrity/slopnators/sl_stale_path.js +10 -0
  18. package/dist/operations/access.js +3 -2
  19. package/dist/operations/capabilities.d.ts +2 -0
  20. package/dist/operations/capabilities.js +2 -0
  21. package/dist/operations/generators/capability.js +234 -24
  22. package/dist/operations/generators/compile.js +150 -50
  23. package/dist/operations/generators/seed.d.ts +3 -0
  24. package/dist/operations/generators/seed.js +25 -3
  25. package/dist/operations/nodes.d.ts +2 -2
  26. package/package.json +1 -1
  27. package/dist/core/actor.ts +0 -21
  28. package/dist/core/attach.ts +0 -613
  29. package/dist/core/chain.ts +0 -145
  30. package/dist/core/condition.ts +0 -227
  31. package/dist/core/descend.ts +0 -1185
  32. package/dist/core/errors/catalogue.ts +0 -314
  33. package/dist/core/errors/classes.ts +0 -51
  34. package/dist/core/errors/error.ts +0 -152
  35. package/dist/core/errors/namespaces.ts +0 -19
  36. package/dist/core/finding.ts +0 -37
  37. package/dist/core/generators/commit.ts +0 -157
  38. package/dist/core/init.ts +0 -222
  39. package/dist/core/invoked.ts +0 -52
  40. package/dist/core/round.ts +0 -74
  41. package/dist/core/rows.ts +0 -506
  42. package/dist/core/scope.ts +0 -20
  43. package/dist/core/thread.ts +0 -29
  44. package/dist/core/tree.ts +0 -90
  45. package/dist/core/types.ts +0 -351
  46. package/dist/derivation/attach.ts +0 -57
  47. package/dist/derivation/generators/build.ts +0 -371
  48. package/dist/derivation/generators/check.ts +0 -118
  49. package/dist/derivation/generators/determinism.ts +0 -145
  50. package/dist/derivation/generators/release.ts +0 -236
  51. package/dist/derivation/release.ts +0 -43
  52. package/dist/integrations/attach.ts +0 -187
  53. package/dist/integrations/cli.ts +0 -606
  54. package/dist/integrations/declared.ts +0 -34
  55. package/dist/integrations/disk.ts +0 -466
  56. package/dist/integrations/generators/read.ts +0 -127
  57. package/dist/integrations/generators/vault.ts +0 -210
  58. package/dist/integrations/https.ts +0 -216
  59. package/dist/integrations/pglite.ts +0 -206
  60. package/dist/integrations/secret.ts +0 -54
  61. package/dist/integrations/target.ts +0 -672
  62. package/dist/integrations/vault.ts +0 -148
  63. package/dist/integrity/attach.ts +0 -150
  64. package/dist/integrity/check-threads.ts +0 -166
  65. package/dist/integrity/constraint.ts +0 -1246
  66. package/dist/integrity/generators/compiler.ts +0 -138
  67. package/dist/integrity/generators/gate.ts +0 -227
  68. package/dist/integrity/generators/issues.ts +0 -160
  69. package/dist/integrity/generators/publish.ts +0 -233
  70. package/dist/integrity/generators/trial.ts +0 -359
  71. package/dist/integrity/lint.ts +0 -214
  72. package/dist/integrity/lsp/bin.ts +0 -44
  73. package/dist/integrity/lsp/diagnostics.ts +0 -92
  74. package/dist/integrity/lsp/findings.ts +0 -63
  75. package/dist/integrity/lsp/lint.ts +0 -124
  76. package/dist/integrity/lsp/protocol.ts +0 -78
  77. package/dist/integrity/lsp/server.ts +0 -224
  78. package/dist/integrity/slopnator.ts +0 -324
  79. package/dist/integrity/slopnators/sl_broken_lineage.ts +0 -38
  80. package/dist/integrity/slopnators/sl_declared_elsewhere.ts +0 -60
  81. package/dist/integrity/slopnators/sl_derived_drift.ts +0 -101
  82. package/dist/integrity/slopnators/sl_door_in_a_node.ts +0 -250
  83. package/dist/integrity/slopnators/sl_hardcoded_style.ts +0 -55
  84. package/dist/integrity/slopnators/sl_inert_island.ts +0 -84
  85. package/dist/integrity/slopnators/sl_inert_slopnator.ts +0 -93
  86. package/dist/integrity/slopnators/sl_missing_row.ts +0 -140
  87. package/dist/integrity/slopnators/sl_no_barrel.ts +0 -48
  88. package/dist/integrity/slopnators/sl_no_child_process.ts +0 -100
  89. package/dist/integrity/slopnators/sl_no_comment.ts +0 -43
  90. package/dist/integrity/slopnators/sl_no_dispatch_bypass.ts +0 -276
  91. package/dist/integrity/slopnators/sl_no_import_cycle.ts +0 -66
  92. package/dist/integrity/slopnators/sl_no_such_command.ts +0 -81
  93. package/dist/integrity/slopnators/sl_only_takes_a_literal.ts +0 -63
  94. package/dist/integrity/slopnators/sl_outside_an_operation.ts +0 -25
  95. package/dist/integrity/slopnators/sl_screen_holds_only_declared.ts +0 -70
  96. package/dist/integrity/slopnators/sl_secret_literal.ts +0 -68
  97. package/dist/integrity/slopnators/sl_stale_path.ts +0 -178
  98. package/dist/integrity/slopnators/sl_twice_meant.ts +0 -57
  99. package/dist/integrity/slopnators/sl_twice_parented.ts +0 -82
  100. package/dist/integrity/slopnators/sl_undeclared_crossing.ts +0 -481
  101. package/dist/integrity/slopnators/sl_undeclared_destination.ts +0 -113
  102. package/dist/integrity/slopnators/sl_undeclared_element.ts +0 -44
  103. package/dist/integrity/slopnators/sl_undeclared_list.ts +0 -358
  104. package/dist/integrity/slopnators/sl_undeclared_node.ts +0 -75
  105. package/dist/integrity/slopnators/sl_undeclared_operation.ts +0 -257
  106. package/dist/integrity/slopnators/sl_undeclared_variant.ts +0 -152
  107. package/dist/integrity/slopnators/sl_undeclared_webhook.ts +0 -108
  108. package/dist/integrity/slopnators/sl_underived_route.ts +0 -72
  109. package/dist/integrity/slopnators/sl_undescribed.ts +0 -65
  110. package/dist/integrity/slopnators/sl_ungoverned_island.ts +0 -60
  111. package/dist/integrity/slopnators/sl_unlisted_program.ts +0 -46
  112. package/dist/integrity/slopnators/sl_unnamed_island.ts +0 -41
  113. package/dist/integrity/slopnators/sl_unpaired_member.ts +0 -139
  114. package/dist/integrity/slopnators/sl_unpaired_slopnator.ts +0 -71
  115. package/dist/integrity/slopnators/sl_unprefixed_member.ts +0 -110
  116. package/dist/integrity/slopnators/sl_unthreaded.ts +0 -421
  117. package/dist/integrity/slopnators/sl_untranslated_key.ts +0 -140
  118. package/dist/integrity/slopnators/sl_unvaulted_secret.ts +0 -132
  119. package/dist/integrity/slopnators/sl_unversioned.ts +0 -52
  120. package/dist/integrity/sweep.ts +0 -395
  121. package/dist/knowledge/attach.ts +0 -327
  122. package/dist/knowledge/generators/docsgen.ts +0 -186
  123. package/dist/knowledge/generators/glossary.ts +0 -73
  124. package/dist/knowledge/glossary.ts +0 -6
  125. package/dist/operations/access.ts +0 -131
  126. package/dist/operations/attach.ts +0 -408
  127. package/dist/operations/capabilities.ts +0 -88
  128. package/dist/operations/dispatch.ts +0 -294
  129. package/dist/operations/error-results.ts +0 -31
  130. package/dist/operations/generators/capability.ts +0 -1054
  131. package/dist/operations/generators/compile.ts +0 -701
  132. package/dist/operations/generators/permits.ts +0 -229
  133. package/dist/operations/generators/seed.ts +0 -359
  134. package/dist/operations/generators/trial.ts +0 -666
  135. package/dist/operations/nodes.ts +0 -53
  136. package/dist/operations/operations.ts +0 -91
  137. 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
- }