@prestack/thread 0.1.21 → 0.1.23
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 +227 -0
- package/dist/core/attach.js +21 -0
- package/dist/core/descend.js +6 -3
- package/dist/core/generators/commit.js +55 -3
- package/dist/core/generators/dev.d.ts +1 -0
- package/dist/core/generators/dev.js +69 -0
- package/dist/derivation/generators/build.d.ts +3 -1
- package/dist/derivation/generators/build.js +104 -22
- package/dist/derivation/generators/release.js +74 -25
- package/dist/derivation/release.js +3 -3
- package/dist/integrations/cli.d.ts +0 -1
- package/dist/integrations/cli.js +69 -11
- package/dist/integrations/declared.d.ts +2 -0
- package/dist/integrations/declared.js +6 -1
- package/dist/integrations/pglite.js +13 -3
- package/dist/integrations/target.d.ts +3 -0
- package/dist/integrity/attach.js +1 -5
- package/dist/integrity/slopnators/sl_stale_path.js +10 -0
- 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 +237 -24
- package/dist/operations/generators/compile.js +150 -50
- 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 -1185
- 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 -506
- 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 -606
- package/dist/integrations/declared.ts +0 -34
- 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 -672
- 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 -233
- 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 -229
- 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,358 +0,0 @@
|
|
|
1
|
-
import { dirname, resolve } from 'node:path'
|
|
2
|
-
|
|
3
|
-
import ts from 'typescript'
|
|
4
|
-
|
|
5
|
-
import { readFileSync } from '../../integrations/disk.ts'
|
|
6
|
-
|
|
7
|
-
import type { Slopnator, SlopnatorContext } from '../slopnator.ts'
|
|
8
|
-
|
|
9
|
-
const LIST = 2
|
|
10
|
-
|
|
11
|
-
interface Held {
|
|
12
|
-
readonly type?: string
|
|
13
|
-
readonly value?: unknown
|
|
14
|
-
readonly name?: string
|
|
15
|
-
readonly operator?: string
|
|
16
|
-
readonly elements?: readonly (Held | null)[]
|
|
17
|
-
readonly properties?: readonly (Held | null)[]
|
|
18
|
-
readonly arguments?: readonly Held[]
|
|
19
|
-
readonly init?: Held | null
|
|
20
|
-
readonly id?: { readonly type?: string; readonly name?: string }
|
|
21
|
-
readonly callee?: Held
|
|
22
|
-
readonly object?: Held
|
|
23
|
-
readonly property?: Held
|
|
24
|
-
readonly expression?: Held
|
|
25
|
-
readonly argument?: Held | null
|
|
26
|
-
readonly test?: Held | null
|
|
27
|
-
readonly consequent?: Held | null
|
|
28
|
-
readonly alternate?: Held | null
|
|
29
|
-
readonly body?: readonly (Held | null)[]
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
const WIDENED = 'TSAsExpression'
|
|
33
|
-
|
|
34
|
-
function bare(held: Held | null | undefined): Held | undefined {
|
|
35
|
-
let one = held ?? undefined
|
|
36
|
-
while (one?.type === WIDENED) one = one.expression
|
|
37
|
-
return one
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
const spoken = (held: Held | null | undefined): boolean => {
|
|
41
|
-
const one = bare(held)
|
|
42
|
-
return one?.type === 'Literal' && typeof one.value === 'string'
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
const strings = (held: Held | null | undefined): boolean => {
|
|
46
|
-
const one = bare(held)
|
|
47
|
-
if (one?.type !== 'ArrayExpression') return false
|
|
48
|
-
const all = one.elements ?? []
|
|
49
|
-
return all.length >= LIST && all.every((each) => spoken(each) || woven(each))
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
const SPREAD = 'SpreadElement'
|
|
53
|
-
|
|
54
|
-
function woven(held: Held | null | undefined): boolean {
|
|
55
|
-
const one = bare(held)
|
|
56
|
-
if (one?.type !== SPREAD) return false
|
|
57
|
-
let words = 0
|
|
58
|
-
let other = false
|
|
59
|
-
const walk = (node: unknown, seen: Set<unknown>): void => {
|
|
60
|
-
if (node === null || typeof node !== 'object' || seen.has(node)) return
|
|
61
|
-
seen.add(node)
|
|
62
|
-
if (Array.isArray(node)) {
|
|
63
|
-
for (const each of node) walk(each, seen)
|
|
64
|
-
return
|
|
65
|
-
}
|
|
66
|
-
const held = node as Held
|
|
67
|
-
if (held.type === 'Literal') {
|
|
68
|
-
if (typeof held.value === 'string') words += 1
|
|
69
|
-
else other = true
|
|
70
|
-
return
|
|
71
|
-
}
|
|
72
|
-
for (const [key, each] of Object.entries(node as Record<string, unknown>)) {
|
|
73
|
-
if (key !== BACK) walk(each, seen)
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
walk(one.argument, new Set())
|
|
77
|
-
return words > 0 && !other
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
function valued(held: Held): readonly Held[] {
|
|
81
|
-
const found: Held[] = []
|
|
82
|
-
for (const one of held.properties ?? []) {
|
|
83
|
-
if (one?.type === 'Property') found.push(one.value as Held)
|
|
84
|
-
}
|
|
85
|
-
return found
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
function tabled(held: Held | null | undefined, seen: Set<unknown>): boolean {
|
|
89
|
-
const one = bare(held)
|
|
90
|
-
if (one === undefined || one.type !== 'ObjectExpression' || seen.has(one)) return false
|
|
91
|
-
seen.add(one)
|
|
92
|
-
const all = valued(one)
|
|
93
|
-
if (all.filter((each) => spoken(each)).length >= LIST) return true
|
|
94
|
-
return all.some((each) => strings(each) || tabled(each, seen))
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
function drawn(held: Held): string | undefined {
|
|
98
|
-
if (held.type !== 'NewExpression' || held.callee?.type !== 'Identifier' || held.callee.name !== 'Set') return undefined
|
|
99
|
-
const only = held.arguments ?? []
|
|
100
|
-
if (only.length !== 1) return undefined
|
|
101
|
-
const call = bare(only[0])
|
|
102
|
-
const callee = call?.callee
|
|
103
|
-
if (call?.type !== 'CallExpression' || callee?.type !== 'MemberExpression') return undefined
|
|
104
|
-
if (callee.object?.type !== 'Identifier' || callee.object.name !== 'Object') return undefined
|
|
105
|
-
const asked = callee.property?.name
|
|
106
|
-
if (asked !== 'keys' && asked !== 'values') return undefined
|
|
107
|
-
const from = bare((call.arguments ?? [])[0])
|
|
108
|
-
return from?.type === 'Identifier' ? from.name : undefined
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
function adapted(node: ts.Node): Held | undefined {
|
|
112
|
-
if (ts.isAsExpression(node) || ts.isParenthesizedExpression(node)) {
|
|
113
|
-
const inner = adapted(node.expression)
|
|
114
|
-
return inner === undefined ? undefined : { type: WIDENED, expression: inner }
|
|
115
|
-
}
|
|
116
|
-
if (ts.isStringLiteral(node)) return { type: 'Literal', value: node.text }
|
|
117
|
-
if (ts.isSpreadElement(node)) return { type: SPREAD, argument: adapted(node.expression) ?? null }
|
|
118
|
-
if (ts.isCallExpression(node)) {
|
|
119
|
-
return { type: 'CallExpression', arguments: node.arguments.map((one) => adapted(one)).filter((one): one is Held => one !== undefined) }
|
|
120
|
-
}
|
|
121
|
-
if (ts.isArrayLiteralExpression(node)) {
|
|
122
|
-
return { type: 'ArrayExpression', elements: node.elements.map((one) => adapted(one) ?? null) }
|
|
123
|
-
}
|
|
124
|
-
if (ts.isObjectLiteralExpression(node)) {
|
|
125
|
-
const properties: Held[] = []
|
|
126
|
-
for (const one of node.properties) {
|
|
127
|
-
if (!ts.isPropertyAssignment(one)) continue
|
|
128
|
-
const value = adapted(one.initializer)
|
|
129
|
-
if (value !== undefined) properties.push({ type: 'Property', value })
|
|
130
|
-
}
|
|
131
|
-
return { type: 'ObjectExpression', properties }
|
|
132
|
-
}
|
|
133
|
-
return undefined
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
function parsed(at: string, text: string): ts.SourceFile {
|
|
137
|
-
return ts.createSourceFile(at, text, ts.ScriptTarget.Latest, true, ts.ScriptKind.TSX)
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
function brought(source: ts.SourceFile, name: string): string | undefined {
|
|
141
|
-
for (const statement of source.statements) {
|
|
142
|
-
if (!ts.isImportDeclaration(statement) || !ts.isStringLiteral(statement.moduleSpecifier)) continue
|
|
143
|
-
const bindings = statement.importClause?.namedBindings
|
|
144
|
-
if (bindings === undefined || !ts.isNamedImports(bindings)) continue
|
|
145
|
-
if (!bindings.elements.some((one) => one.name.text === name)) continue
|
|
146
|
-
return statement.moduleSpecifier.text
|
|
147
|
-
}
|
|
148
|
-
return undefined
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
function elsewhere(at: SlopnatorContext, name: string): Held | undefined {
|
|
152
|
-
const specifier = brought(parsed(at.filename, at.text), name)
|
|
153
|
-
if (specifier === undefined || !specifier.startsWith('.')) return undefined
|
|
154
|
-
const path = resolve(dirname(at.filename), specifier)
|
|
155
|
-
let text: string
|
|
156
|
-
try {
|
|
157
|
-
text = readFileSync(path, 'utf8')
|
|
158
|
-
} catch {
|
|
159
|
-
return undefined
|
|
160
|
-
}
|
|
161
|
-
for (const statement of parsed(path, text).statements) {
|
|
162
|
-
if (!ts.isVariableStatement(statement)) continue
|
|
163
|
-
for (const one of statement.declarationList.declarations) {
|
|
164
|
-
if (!ts.isIdentifier(one.name) || one.name.text !== name || one.initializer === undefined) continue
|
|
165
|
-
return adapted(one.initializer)
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
return undefined
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
function initOf(held: unknown, name: string, seen: Set<unknown>): Held | undefined {
|
|
172
|
-
if (held === null || typeof held !== 'object' || seen.has(held)) return undefined
|
|
173
|
-
seen.add(held)
|
|
174
|
-
if (Array.isArray(held)) {
|
|
175
|
-
for (const one of held) {
|
|
176
|
-
const found = initOf(one, name, seen)
|
|
177
|
-
if (found !== undefined) return found
|
|
178
|
-
}
|
|
179
|
-
return undefined
|
|
180
|
-
}
|
|
181
|
-
const node = held as Held
|
|
182
|
-
const init = node.init ?? undefined
|
|
183
|
-
if (node.type === 'VariableDeclarator' && node.id?.type === 'Identifier' && node.id.name === name && init !== undefined) return init
|
|
184
|
-
for (const [key, one] of Object.entries(held as Record<string, unknown>)) {
|
|
185
|
-
if (key === BACK) continue
|
|
186
|
-
const found = initOf(one, name, seen)
|
|
187
|
-
if (found !== undefined) return found
|
|
188
|
-
}
|
|
189
|
-
return undefined
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
function listed(init: Held | null | undefined, program: unknown, at: SlopnatorContext, through: ReadonlySet<string>): boolean {
|
|
193
|
-
const one = bare(init)
|
|
194
|
-
if (one === undefined) return false
|
|
195
|
-
if (strings(one)) return true
|
|
196
|
-
if (tabled(one, new Set())) return true
|
|
197
|
-
if (one.type === 'NewExpression' || one.type === 'CallExpression') {
|
|
198
|
-
const only = one.arguments ?? []
|
|
199
|
-
if (only.length === 1 && strings(only[0])) return true
|
|
200
|
-
}
|
|
201
|
-
const from = drawn(one)
|
|
202
|
-
if (from === undefined || through.has(from)) return false
|
|
203
|
-
const followed = new Set(through)
|
|
204
|
-
followed.add(from)
|
|
205
|
-
const here = initOf(program, from, new Set())
|
|
206
|
-
return listed(here ?? elsewhere(at, from), program, at, followed)
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
const asks = (held: Held | undefined, name: string): boolean => {
|
|
210
|
-
const callee = held?.callee
|
|
211
|
-
if (held?.type !== 'CallExpression' || callee?.type !== 'MemberExpression') return false
|
|
212
|
-
const on = bare(callee.object)
|
|
213
|
-
if (on?.type !== 'Identifier' || on.name !== name) return false
|
|
214
|
-
const said = callee.property?.name
|
|
215
|
-
return said === 'has' || said === 'includes'
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
const BACK = 'parent'
|
|
219
|
-
|
|
220
|
-
function asking(held: unknown, name: string, seen: Set<unknown>): boolean {
|
|
221
|
-
if (held === null || typeof held !== 'object' || seen.has(held)) return false
|
|
222
|
-
seen.add(held)
|
|
223
|
-
if (Array.isArray(held)) return held.some((one) => asking(one, name, seen))
|
|
224
|
-
if (asks(held as Held, name)) return true
|
|
225
|
-
return Object.entries(held as Record<string, unknown>).some(([key, one]) => key !== BACK && asking(one, name, seen))
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
function consults(node: Held, name: string): boolean {
|
|
229
|
-
if (node.type === 'MemberExpression') {
|
|
230
|
-
const on = bare(node.object)
|
|
231
|
-
if (on?.type === 'Identifier' && on.name === name) return true
|
|
232
|
-
}
|
|
233
|
-
const callee = node.callee
|
|
234
|
-
if (node.type !== 'CallExpression' || callee?.type !== 'MemberExpression') return false
|
|
235
|
-
if (callee.object?.type !== 'Identifier' || callee.object.name !== 'Object') return false
|
|
236
|
-
const only = node.arguments ?? []
|
|
237
|
-
const from = bare(only[0])
|
|
238
|
-
return only.length === 1 && from?.type === 'Identifier' && from.name === name
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
function consulted(held: unknown, name: string, seen: Set<unknown>): boolean {
|
|
242
|
-
if (held === null || typeof held !== 'object' || seen.has(held)) return false
|
|
243
|
-
seen.add(held)
|
|
244
|
-
if (Array.isArray(held)) return held.some((one) => consulted(one, name, seen))
|
|
245
|
-
if (consults(held as Held, name)) return true
|
|
246
|
-
return Object.entries(held as Record<string, unknown>).some(([key, one]) => key !== BACK && consulted(one, name, seen))
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
function continues(held: Held | null | undefined): boolean {
|
|
250
|
-
if (held === null || held === undefined) return false
|
|
251
|
-
if (held.type === 'ContinueStatement') return true
|
|
252
|
-
if (held.type !== 'BlockStatement') return false
|
|
253
|
-
return (held.body ?? []).some((one) => one?.type === 'ContinueStatement')
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
const LEAVES = 'ReturnStatement'
|
|
257
|
-
|
|
258
|
-
const THROWN = 'ThrowStatement'
|
|
259
|
-
|
|
260
|
-
const leaving = (one: Held | null | undefined): boolean => one?.type === LEAVES || one?.type === THROWN
|
|
261
|
-
|
|
262
|
-
function exits(held: Held | null | undefined): boolean {
|
|
263
|
-
if (held === null || held === undefined) return false
|
|
264
|
-
if (leaving(held)) return true
|
|
265
|
-
if (held.type !== 'BlockStatement') return false
|
|
266
|
-
return (held.body ?? []).some((one) => leaving(one))
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
function look(held: unknown, name: string, found: { gates: boolean; skips: boolean }, seen: Set<unknown>): void {
|
|
270
|
-
if (held === null || typeof held !== 'object' || seen.has(held)) return
|
|
271
|
-
seen.add(held)
|
|
272
|
-
if (Array.isArray(held)) {
|
|
273
|
-
for (const one of held) look(one, name, found, seen)
|
|
274
|
-
return
|
|
275
|
-
}
|
|
276
|
-
const node = held as Held
|
|
277
|
-
if (node.type === 'UnaryExpression' && node.operator === '!' && asks(node.argument ?? undefined, name)) {
|
|
278
|
-
found.skips = true
|
|
279
|
-
return
|
|
280
|
-
}
|
|
281
|
-
if (node.type === 'IfStatement' && continues(node.consequent) && asking(node.test, name, new Set())) {
|
|
282
|
-
found.skips = true
|
|
283
|
-
look(node.consequent, name, found, seen)
|
|
284
|
-
look(node.alternate, name, found, seen)
|
|
285
|
-
return
|
|
286
|
-
}
|
|
287
|
-
if (node.type === 'IfStatement' && exits(node.consequent) && asking(node.test, name, new Set())) {
|
|
288
|
-
found.gates = true
|
|
289
|
-
return
|
|
290
|
-
}
|
|
291
|
-
if (asks(node, name)) {
|
|
292
|
-
found.gates = true
|
|
293
|
-
return
|
|
294
|
-
}
|
|
295
|
-
for (const [key, one] of Object.entries(node as Record<string, unknown>)) {
|
|
296
|
-
if (key !== BACK) look(one, name, found, seen)
|
|
297
|
-
}
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
export const slopnator: Slopnator = {
|
|
301
|
-
on: ['VariableDeclarator'],
|
|
302
|
-
holds: (node, at) => {
|
|
303
|
-
const held = node as Held
|
|
304
|
-
const program = at.ancestors().find((one) => (one as Held | undefined)?.type === 'Program')
|
|
305
|
-
if (!listed(held.init, program, at, new Set<string>())) return false
|
|
306
|
-
const named = held.id?.type === 'Identifier' ? held.id.name : undefined
|
|
307
|
-
const table = bare(held.init)?.type === 'ObjectExpression'
|
|
308
|
-
if (table && (named === undefined || !consulted(program, named, new Set()))) return false
|
|
309
|
-
if (named !== undefined) {
|
|
310
|
-
const found = { gates: false, skips: false }
|
|
311
|
-
look(program, named, found, new Set())
|
|
312
|
-
if (!found.gates && found.skips) return false
|
|
313
|
-
}
|
|
314
|
-
return (
|
|
315
|
-
`\`${named ?? 'this'}\` is ${table ? 'a table' : 'a list'} of words written out here and this check refuses what is IN it, so ` +
|
|
316
|
-
'whatever breaks the rule under a name nobody typed walks straight past. Wrapping them in `as const`, nesting them under a key, ' +
|
|
317
|
-
'or building a set out of the keys leaves the same list one step further out of sight. A list that decides what a check CATCHES ' +
|
|
318
|
-
'has to come from the tree: `thread inspect <word>` names the row that coins any word you need, and a check that reads those ' +
|
|
319
|
-
'rows widens when the tree does. A list a check only SKIPS is left alone, because adding a word to one of those makes it ' +
|
|
320
|
-
'stricter and it can never go blind.'
|
|
321
|
-
)
|
|
322
|
-
},
|
|
323
|
-
proves: {
|
|
324
|
-
wrong: [
|
|
325
|
-
"const CROSSINGS = ['fetch', 'setTimeout', 'setInterval']",
|
|
326
|
-
"const STARTS = new Set(['node:child_process', 'child_process'])\nexport const opens = (from: string): boolean => {\n if (!STARTS.has(from)) return false\n return true\n}",
|
|
327
|
-
"const GLOBALS = new Set(['Date', 'performance', 'Math'])",
|
|
328
|
-
"const MOVES = new Set(['push', 'replace'])\nexport const moved = (one: string): boolean => MOVES.has(one)",
|
|
329
|
-
"const SURFACES = ['cli', 'api', 'mcp'] as const\nexport const surfaced = (one: string): boolean => (SURFACES as readonly string[]).includes(one)",
|
|
330
|
-
"const MOVING = { globals: ['window', 'globalThis'], histories: ['router', 'history'] }\nexport const moves = (one: string): boolean => MOVING.globals.includes(one)",
|
|
331
|
-
"const HANDED_BY = { postgres: 'store', pglite: 'store', https: 'http' }\nexport const handedBy = (reach: string): string => HANDED_BY[reach] ?? reach",
|
|
332
|
-
"const HANDS = { postgres: 'store', pglite: 'store' }\nconst PINS = new Set(Object.keys(HANDS))\nexport const pinned = (one: string): boolean => PINS.has(one)",
|
|
333
|
-
{
|
|
334
|
-
files: [
|
|
335
|
-
{ at: 'threads/held.ts', source: "export const PINNED = { clock: ['Date.now', 'setTimeout'] }\n" },
|
|
336
|
-
{ at: 'threads/slopnators/sl_probe_followed.ts', source: "import { PINNED } from '../held.ts'\n\nconst PINS = new Set(Object.keys(PINNED))\nexport const pinned = (one: string): boolean => PINS.has(one)\n" },
|
|
337
|
-
],
|
|
338
|
-
at: 'threads/slopnators/sl_probe_followed.ts',
|
|
339
|
-
},
|
|
340
|
-
],
|
|
341
|
-
right: [
|
|
342
|
-
'const CROSSINGS = declared()',
|
|
343
|
-
"const UNDER = 'operations/'",
|
|
344
|
-
"const COMMON = new Set(['a', 'the'])\nexport const words = (all: readonly string[]): readonly string[] => all.filter((one) => !COMMON.has(one))",
|
|
345
|
-
"const SKIP = new Set(['dist', 'node_modules'])\nexport function walk(names: readonly string[]): number {\n let seen = 0\n for (const name of names) {\n if (SKIP.has(name)) continue\n seen += 1\n }\n return seen\n}",
|
|
346
|
-
"const LEFT = ['dist', 'node_modules'] as const\nexport function walked(names: readonly string[]): number {\n let seen = 0\n for (const name of names) {\n if ((LEFT as readonly string[]).includes(name)) continue\n seen += 1\n }\n return seen\n}",
|
|
347
|
-
"export const SAYS = { cli: 'runs at a terminal', api: 'answers a request' }",
|
|
348
|
-
{
|
|
349
|
-
files: [
|
|
350
|
-
{ at: 'threads/made.ts', source: "export const MADE = declared()\n" },
|
|
351
|
-
{ at: 'threads/slopnators/sl_probe_derived.ts', source: "import { MADE } from '../made.ts'\n\nconst PICKED = new Set(Object.keys(MADE))\nexport const picked = (one: string): boolean => PICKED.has(one)\n" },
|
|
352
|
-
],
|
|
353
|
-
at: 'threads/slopnators/sl_probe_derived.ts',
|
|
354
|
-
},
|
|
355
|
-
"export const slopnator = { on: ['CallExpression', 'NewExpression'], holds: () => false, proves: { wrong: ['new Date()'], right: ['const at = 1'] } }",
|
|
356
|
-
],
|
|
357
|
-
},
|
|
358
|
-
}
|
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
import { dirname, join, normalize } from 'node:path'
|
|
2
|
-
|
|
3
|
-
import type { Slopnator } from '../slopnator.ts'
|
|
4
|
-
import { here, index, parses, unreadable } from './sl_unthreaded.ts'
|
|
5
|
-
|
|
6
|
-
const SAID = /'((?:[^'\\\n]|\\.)*)'|"((?:[^"\\\n]|\\.)*)"|`((?:[^`\\$]|\\.)*)`/g
|
|
7
|
-
|
|
8
|
-
const RELATIVE = /^\.\.?\//
|
|
9
|
-
|
|
10
|
-
const UNDER = 'nodes/'
|
|
11
|
-
|
|
12
|
-
const slash = (one: string): string => one.split('\\').join('/')
|
|
13
|
-
|
|
14
|
-
function nodeAt(said: string, from: string): string | undefined {
|
|
15
|
-
if (said === '') return undefined
|
|
16
|
-
const path = slash(normalize(RELATIVE.test(said) ? join(from, said) : said))
|
|
17
|
-
return path.startsWith(UNDER) ? path : undefined
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export const slopnator: Slopnator = {
|
|
21
|
-
holds: (_node, at) => {
|
|
22
|
-
const path = here(at.filename)
|
|
23
|
-
if (unreadable(path, at.text) !== undefined) return false
|
|
24
|
-
if (!parses(path)) return false
|
|
25
|
-
|
|
26
|
-
const declared = index().reached
|
|
27
|
-
const from = dirname(path)
|
|
28
|
-
|
|
29
|
-
for (const found of String(at.text).matchAll(SAID)) {
|
|
30
|
-
const named = nodeAt(found[1] ?? found[2] ?? found[3] ?? '', from)
|
|
31
|
-
if (named === undefined || declared.has(named)) continue
|
|
32
|
-
|
|
33
|
-
return (
|
|
34
|
-
`this reaches \`${named}\`, and the descent from the tree root never arrives there — so one of ` +
|
|
35
|
-
'its steps is a node nobody declared. An operation that takes a step nothing agreed to is doing ' +
|
|
36
|
-
'something this app never said it could do, and the flow reads as agreed the whole way down'
|
|
37
|
-
)
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
return false
|
|
41
|
-
},
|
|
42
|
-
proves: {
|
|
43
|
-
wrong: [
|
|
44
|
-
"import { node as ghost } from '../nodes/nd_ghost.ts'",
|
|
45
|
-
"const held = await import('../nodes/nd_ghost.ts')",
|
|
46
|
-
"const held = require('../nodes/nd_ghost.ts')",
|
|
47
|
-
"export { node as ghost } from '../nodes/nd_ghost.ts'",
|
|
48
|
-
"const AT = '../nodes/nd_ghost.ts'",
|
|
49
|
-
"import { node as invented } from 'nodes/nd_nowhere.ts'",
|
|
50
|
-
],
|
|
51
|
-
right: [
|
|
52
|
-
{
|
|
53
|
-
files: [
|
|
54
|
-
{ 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" },
|
|
55
|
-
{ 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_nodes.ts',\n },\n ],\n}\n" },
|
|
56
|
-
{ at: 'threads/_probe_nodes.ts', source: "import type { Threaded } from '../thread/core/thread.ts'\n\nexport const NODES: Threaded = {\n id: 'nodes',\n declares: [\n {\n id: 'read',\n at: 'nodes/nd_read.ts',\n },\n {\n id: 'action',\n at: 'nodes/nd_action.ts',\n },\n ],\n}\n" },
|
|
57
|
-
{ at: 'operations/_probe_read.ts', source: "import { node as read } from '../nodes/nd_read.ts'" },
|
|
58
|
-
],
|
|
59
|
-
at: 'operations/_probe_read.ts',
|
|
60
|
-
},
|
|
61
|
-
{
|
|
62
|
-
files: [
|
|
63
|
-
{ 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" },
|
|
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/_probe_nodes.ts',\n },\n ],\n}\n" },
|
|
65
|
-
{ at: 'threads/_probe_nodes.ts', source: "import type { Threaded } from '../thread/core/thread.ts'\n\nexport const NODES: Threaded = {\n id: 'nodes',\n declares: [\n {\n id: 'read',\n at: 'nodes/nd_read.ts',\n },\n {\n id: 'action',\n at: 'nodes/nd_action.ts',\n },\n ],\n}\n" },
|
|
66
|
-
{ at: 'operations/_probe_multi.ts', source: "import { node as action } from '../nodes/nd_action.ts'\nimport { node as read } from '../nodes/nd_read.ts'" },
|
|
67
|
-
],
|
|
68
|
-
at: 'operations/_probe_multi.ts',
|
|
69
|
-
},
|
|
70
|
-
"import { z } from 'zod'",
|
|
71
|
-
"import type { Operation } from '../../operations/operations.ts'",
|
|
72
|
-
"import type { Held } from '../../operations/nodes.ts'",
|
|
73
|
-
],
|
|
74
|
-
},
|
|
75
|
-
}
|
|
@@ -1,257 +0,0 @@
|
|
|
1
|
-
import type { Slopnator } from '../slopnator.ts'
|
|
2
|
-
|
|
3
|
-
import { descend, read, reached } from '../../core/descend.ts'
|
|
4
|
-
|
|
5
|
-
import { rowsIn } from './sl_unthreaded.ts'
|
|
6
|
-
|
|
7
|
-
const UNDER = 'operations/'
|
|
8
|
-
|
|
9
|
-
const DISPATCH = 'dispatch'
|
|
10
|
-
|
|
11
|
-
function declared(): ReadonlySet<string> {
|
|
12
|
-
const found = new Set<string>()
|
|
13
|
-
|
|
14
|
-
descend()
|
|
15
|
-
|
|
16
|
-
for (const at of reached()) {
|
|
17
|
-
const text = read(at)
|
|
18
|
-
if (text === undefined) continue
|
|
19
|
-
|
|
20
|
-
for (const one of rowsIn(text)) {
|
|
21
|
-
const where = one.at?.split('\\').join('/')
|
|
22
|
-
if (where === undefined || !where.includes(UNDER)) continue
|
|
23
|
-
if (one.id !== undefined) found.add(one.id)
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
return found
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
function bodies(): ReadonlySet<string> {
|
|
31
|
-
const found = new Set<string>()
|
|
32
|
-
|
|
33
|
-
descend()
|
|
34
|
-
|
|
35
|
-
for (const at of reached()) {
|
|
36
|
-
const text = read(at)
|
|
37
|
-
if (text === undefined) continue
|
|
38
|
-
|
|
39
|
-
for (const one of rowsIn(text)) {
|
|
40
|
-
const where = one.at?.split('\\').join('/')
|
|
41
|
-
if (where === undefined || !where.includes(UNDER)) continue
|
|
42
|
-
if (one.id !== undefined) found.add(where.replace(/\.[^./]+$/, ''))
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
return found
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
function declaring(): ReadonlySet<string> {
|
|
50
|
-
const found = new Set<string>()
|
|
51
|
-
|
|
52
|
-
descend()
|
|
53
|
-
|
|
54
|
-
for (const at of reached()) {
|
|
55
|
-
const text = read(at)
|
|
56
|
-
if (text === undefined) continue
|
|
57
|
-
|
|
58
|
-
for (const one of rowsIn(text)) {
|
|
59
|
-
const where = one.at?.split('\\').join('/')
|
|
60
|
-
if (where === undefined) continue
|
|
61
|
-
found.add(where.replace(/\.[^./]+$/, ''))
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
return found
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
interface Imported {
|
|
69
|
-
readonly type?: string
|
|
70
|
-
readonly source?: { readonly value?: unknown }
|
|
71
|
-
readonly specifiers?: readonly { readonly local?: { readonly name?: unknown } }[]
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
function importedFrom(name: string, ancestors: readonly unknown[]): string | undefined {
|
|
75
|
-
const program = ancestors.find((one) => (one as { readonly type?: string } | undefined)?.type === 'Program') as
|
|
76
|
-
| { readonly body?: readonly Imported[] }
|
|
77
|
-
| undefined
|
|
78
|
-
for (const one of program?.body ?? []) {
|
|
79
|
-
if (one.type !== 'ImportDeclaration' || typeof one.source?.value !== 'string') continue
|
|
80
|
-
if (!(one.specifiers ?? []).some((each) => each.local?.name === name)) continue
|
|
81
|
-
return one.source.value
|
|
82
|
-
}
|
|
83
|
-
return undefined
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
interface Typed {
|
|
87
|
-
readonly typeName?: { readonly name?: unknown }
|
|
88
|
-
readonly typeAnnotation?: Typed
|
|
89
|
-
readonly types?: readonly Typed[]
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
interface Annotated {
|
|
93
|
-
readonly type?: string
|
|
94
|
-
readonly name?: unknown
|
|
95
|
-
readonly typeAnnotation?: Typed
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
interface Held {
|
|
99
|
-
readonly type?: string
|
|
100
|
-
readonly params?: readonly Annotated[]
|
|
101
|
-
readonly body?: unknown
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
function namedIn(said: Typed | undefined): string | undefined {
|
|
105
|
-
if (said === undefined) return undefined
|
|
106
|
-
if (typeof said.typeName?.name === 'string') return said.typeName.name
|
|
107
|
-
const inner = namedIn(said.typeAnnotation)
|
|
108
|
-
if (inner !== undefined) return inner
|
|
109
|
-
for (const one of said.types ?? []) {
|
|
110
|
-
const found = namedIn(one)
|
|
111
|
-
if (found !== undefined) return found
|
|
112
|
-
}
|
|
113
|
-
return undefined
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
const typeNamed = (held: Annotated | undefined): string | undefined => namedIn(held?.typeAnnotation?.typeAnnotation)
|
|
117
|
-
|
|
118
|
-
interface Stated {
|
|
119
|
-
readonly type?: string
|
|
120
|
-
readonly declarations?: readonly { readonly id?: Annotated }[]
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
function annotating(name: string, ancestors: readonly unknown[]): string | undefined {
|
|
124
|
-
for (const held of ancestors) {
|
|
125
|
-
for (const param of (held as Held).params ?? []) {
|
|
126
|
-
if (param.type !== 'Identifier' || param.name !== name) continue
|
|
127
|
-
const said = typeNamed(param)
|
|
128
|
-
if (said !== undefined) return said
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
const body = (held as Held).body
|
|
132
|
-
for (const one of Array.isArray(body) ? (body as readonly Stated[]) : []) {
|
|
133
|
-
if (one.type !== 'VariableDeclaration') continue
|
|
134
|
-
for (const each of one.declarations ?? []) {
|
|
135
|
-
if (each.id?.type !== 'Identifier' || each.id.name !== name) continue
|
|
136
|
-
const said = typeNamed(each.id)
|
|
137
|
-
if (said !== undefined) return said
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
return undefined
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
const settled = (from: string, source: string): string => {
|
|
145
|
-
if (!source.startsWith('.')) return source.replace(/\.[^./]+$/, '')
|
|
146
|
-
const parts = from.split('/').slice(0, -1)
|
|
147
|
-
for (const step of source.replace(/\.[^./]+$/, '').split('/')) {
|
|
148
|
-
if (step === '.' || step === '') continue
|
|
149
|
-
if (step === '..') parts.pop()
|
|
150
|
-
else parts.push(step)
|
|
151
|
-
}
|
|
152
|
-
return parts.join('/')
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
interface Called {
|
|
156
|
-
readonly callee?: { readonly type?: string; readonly name?: string; readonly property?: { readonly type?: string; readonly name?: string } }
|
|
157
|
-
readonly arguments?: readonly { readonly type?: string; readonly value?: unknown; readonly name?: string }[]
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
export const slopnator: Slopnator = {
|
|
161
|
-
on: ['CallExpression'],
|
|
162
|
-
holds: (node, at) => {
|
|
163
|
-
const call = node as Called | undefined
|
|
164
|
-
const callee = call?.callee
|
|
165
|
-
const named =
|
|
166
|
-
callee?.type === 'Identifier'
|
|
167
|
-
? callee.name
|
|
168
|
-
: callee?.property?.type === 'Identifier'
|
|
169
|
-
? callee.property.name
|
|
170
|
-
: undefined
|
|
171
|
-
if (named !== DISPATCH) return false
|
|
172
|
-
|
|
173
|
-
const first = call?.arguments?.[0]
|
|
174
|
-
if (first === undefined) {
|
|
175
|
-
return (
|
|
176
|
-
'dispatch is called with nothing to dispatch, so no operation is named and none can be found — the name ' +
|
|
177
|
-
'is the whole of what makes an operation reachable'
|
|
178
|
-
)
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
const all = declared()
|
|
182
|
-
|
|
183
|
-
if (first.type === 'Identifier' && typeof first.name === 'string') {
|
|
184
|
-
if (all.has(first.name)) return false
|
|
185
|
-
const source = importedFrom(first.name, at.ancestors())
|
|
186
|
-
if (source !== undefined && bodies().has(settled(at.filename, source))) return false
|
|
187
|
-
const named = annotating(first.name, at.ancestors())
|
|
188
|
-
const typed = named === undefined ? undefined : importedFrom(named, at.ancestors())
|
|
189
|
-
if (typed !== undefined && declaring().has(settled(at.filename, typed))) return false
|
|
190
|
-
return (
|
|
191
|
-
`dispatch is handed \`${first.name}\`, and it is not imported from a file any row declares as an operation. ` +
|
|
192
|
-
'A binding is only checkable when it is the operation — import the compiled operation and hand that over, so ' +
|
|
193
|
-
'what runs and what was declared are the same thing rather than two things that agree today'
|
|
194
|
-
)
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
if (first.type !== 'Literal' || typeof first.value !== 'string') {
|
|
198
|
-
return (
|
|
199
|
-
'dispatch is handed a name this file does not write down, so nothing can hold it against what is declared. ' +
|
|
200
|
-
'A name assembled at runtime reads from every check exactly like a name that was checked and found — the ' +
|
|
201
|
-
'operation is reachable and no declaration was ever consulted'
|
|
202
|
-
)
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
if (all.has(first.value)) return false
|
|
206
|
-
|
|
207
|
-
return (
|
|
208
|
-
`dispatch names \`${String(first.value)}\` and nothing declares an operation by that name, so this reaches for ` +
|
|
209
|
-
'something that does not exist and fails where a person is waiting rather than where it was written. ' +
|
|
210
|
-
(all.size === 0
|
|
211
|
-
? 'Nothing declares an operation at all, so no name would have been accepted — the declaration is what has to change'
|
|
212
|
-
: `Declared: ${[...all].sort().join(', ')}`)
|
|
213
|
-
)
|
|
214
|
-
},
|
|
215
|
-
proves: {
|
|
216
|
-
wrong: [
|
|
217
|
-
{
|
|
218
|
-
files: [
|
|
219
|
-
{
|
|
220
|
-
at: 'threads/_probe_operations.ts',
|
|
221
|
-
source:
|
|
222
|
-
"export const OPERATIONS: Threaded = {\n id: 'operations',\n declares: [\n {\n id: 'wf_send_invoice',\n at: 'operations/wf_send_invoice.ts',\n },\n ],\n}\n",
|
|
223
|
-
},
|
|
224
|
-
{ at: 'app/_probe_unknown.tsx', source: "export const HELD = dispatch('wf_not_declared')\n" },
|
|
225
|
-
],
|
|
226
|
-
at: 'app/_probe_unknown.tsx',
|
|
227
|
-
},
|
|
228
|
-
{
|
|
229
|
-
files: [
|
|
230
|
-
{
|
|
231
|
-
at: 'threads/_probe_operations.ts',
|
|
232
|
-
source:
|
|
233
|
-
"export const OPERATIONS: Threaded = {\n id: 'operations',\n declares: [\n {\n id: 'wf_send_invoice',\n at: 'operations/wf_send_invoice.ts',\n },\n ],\n}\n",
|
|
234
|
-
},
|
|
235
|
-
{ at: 'app/_probe_built.tsx', source: "export const HELD = dispatch(`wf_${name}`)\n" },
|
|
236
|
-
],
|
|
237
|
-
at: 'app/_probe_built.tsx',
|
|
238
|
-
},
|
|
239
|
-
],
|
|
240
|
-
right: [
|
|
241
|
-
{
|
|
242
|
-
files: [
|
|
243
|
-
{ 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" },
|
|
244
|
-
{ at: 'threads/thread.ts', source: "import type { Threaded } from '../thread/core/thread.ts'\n\nexport const THREAD: Threaded = {\n declares: [\n {\n at: 'threads/_probe_operations.ts',\n },\n ],\n}\n" },
|
|
245
|
-
{
|
|
246
|
-
at: 'threads/_probe_operations.ts',
|
|
247
|
-
source:
|
|
248
|
-
"import type { Threaded } from '../thread/core/thread.ts'\n\nexport const OPERATIONS: Threaded = {\n id: 'operations',\n declares: [\n {\n id: 'wf_send_invoice',\n at: 'operations/wf_send_invoice.ts',\n },\n ],\n}\n",
|
|
249
|
-
},
|
|
250
|
-
{ at: 'app/_probe_known.tsx', source: "export const HELD = dispatch('wf_send_invoice')\n" },
|
|
251
|
-
],
|
|
252
|
-
at: 'app/_probe_known.tsx',
|
|
253
|
-
},
|
|
254
|
-
"export const HELD = translate('order.place')",
|
|
255
|
-
],
|
|
256
|
-
},
|
|
257
|
-
}
|