@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.
- package/CHANGELOG.md +109 -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 +68 -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.js +69 -10
- 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 +234 -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
package/dist/core/rows.ts
DELETED
|
@@ -1,506 +0,0 @@
|
|
|
1
|
-
import { createHash } from 'node:crypto'
|
|
2
|
-
|
|
3
|
-
import ts from 'typescript'
|
|
4
|
-
|
|
5
|
-
export interface Bounds {
|
|
6
|
-
readonly open: number
|
|
7
|
-
readonly close: number
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
const PARSED = new Map<string, ts.SourceFile>()
|
|
11
|
-
|
|
12
|
-
function tree(text: string): ts.SourceFile {
|
|
13
|
-
const held = PARSED.get(text)
|
|
14
|
-
if (held !== undefined) return held
|
|
15
|
-
const made = ts.createSourceFile('rows.ts', text, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS)
|
|
16
|
-
if (PARSED.size > 8) PARSED.clear()
|
|
17
|
-
PARSED.set(text, made)
|
|
18
|
-
return made
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
function walk(node: ts.Node, seen: (one: ts.Node) => void): void {
|
|
22
|
-
seen(node)
|
|
23
|
-
ts.forEachChild(node, (one) => {
|
|
24
|
-
walk(one, seen)
|
|
25
|
-
})
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
const keyed = (one: ts.PropertyAssignment): string | undefined =>
|
|
29
|
-
ts.isIdentifier(one.name) || ts.isStringLiteral(one.name) ? one.name.text : undefined
|
|
30
|
-
|
|
31
|
-
function stringy(node: ts.ObjectLiteralExpression, name: string): string | undefined {
|
|
32
|
-
for (const one of node.properties) {
|
|
33
|
-
if (!ts.isPropertyAssignment(one) || keyed(one) !== name) continue
|
|
34
|
-
return ts.isStringLiteral(one.initializer) ? one.initializer.text : undefined
|
|
35
|
-
}
|
|
36
|
-
return undefined
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
function listy(node: ts.ObjectLiteralExpression, name: string): ts.ArrayLiteralExpression | undefined {
|
|
40
|
-
for (const one of node.properties) {
|
|
41
|
-
if (!ts.isPropertyAssignment(one) || keyed(one) !== name) continue
|
|
42
|
-
return ts.isArrayLiteralExpression(one.initializer) ? one.initializer : undefined
|
|
43
|
-
}
|
|
44
|
-
return undefined
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
const spanned = (node: ts.Node): { readonly open: number; readonly close: number } => ({
|
|
48
|
-
open: node.getStart(),
|
|
49
|
-
close: node.getEnd() - 1,
|
|
50
|
-
})
|
|
51
|
-
|
|
52
|
-
export function inCode(text: string): readonly boolean[] {
|
|
53
|
-
const held = new Array<boolean>(text.length).fill(true)
|
|
54
|
-
let cursor = 0
|
|
55
|
-
while (cursor < text.length) {
|
|
56
|
-
const one = text[cursor]
|
|
57
|
-
const next = text[cursor + 1]
|
|
58
|
-
if (one === '/' && next === '/') {
|
|
59
|
-
while (cursor < text.length && text[cursor] !== '\n') held[cursor++] = false
|
|
60
|
-
continue
|
|
61
|
-
}
|
|
62
|
-
if (one === '/' && next === '*') {
|
|
63
|
-
held[cursor++] = false
|
|
64
|
-
held[cursor++] = false
|
|
65
|
-
while (cursor < text.length && !(text[cursor] === '*' && text[cursor + 1] === '/')) held[cursor++] = false
|
|
66
|
-
held[cursor++] = false
|
|
67
|
-
held[cursor++] = false
|
|
68
|
-
continue
|
|
69
|
-
}
|
|
70
|
-
if (one === "'" || one === '"' || one === '`') {
|
|
71
|
-
held[cursor++] = false
|
|
72
|
-
while (cursor < text.length && text[cursor] !== one) {
|
|
73
|
-
if (text[cursor] === '\\') held[cursor++] = false
|
|
74
|
-
held[cursor++] = false
|
|
75
|
-
}
|
|
76
|
-
held[cursor++] = false
|
|
77
|
-
continue
|
|
78
|
-
}
|
|
79
|
-
cursor += 1
|
|
80
|
-
}
|
|
81
|
-
return held
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
export function matched(text: string, code: readonly boolean[], from: number, open: string, close: string): number {
|
|
85
|
-
let depth = 0
|
|
86
|
-
for (let cursor = from; cursor < text.length; cursor += 1) {
|
|
87
|
-
if (code[cursor] !== true) continue
|
|
88
|
-
if (text[cursor] === open) depth += 1
|
|
89
|
-
if (text[cursor] === close) {
|
|
90
|
-
depth -= 1
|
|
91
|
-
if (depth === 0) return cursor
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
return -1
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
export function listBounds(text: string, list: string): Bounds | undefined {
|
|
98
|
-
const file = tree(text)
|
|
99
|
-
|
|
100
|
-
let found: ts.ArrayLiteralExpression | undefined
|
|
101
|
-
let held: ts.ObjectLiteralExpression | undefined
|
|
102
|
-
|
|
103
|
-
walk(file, (node) => {
|
|
104
|
-
if (found !== undefined || !ts.isVariableDeclaration(node) || node.initializer === undefined) return
|
|
105
|
-
|
|
106
|
-
const unwrapped = (node2: ts.Expression): ts.Expression =>
|
|
107
|
-
ts.isAsExpression(node2) || ts.isSatisfiesExpression(node2) ? unwrapped(node2.expression) : node2
|
|
108
|
-
const one = unwrapped(node.initializer)
|
|
109
|
-
if (ts.isIdentifier(node.name) && node.name.text === list && ts.isArrayLiteralExpression(one)) {
|
|
110
|
-
found = one
|
|
111
|
-
return
|
|
112
|
-
}
|
|
113
|
-
if (held === undefined && ts.isObjectLiteralExpression(one)) held = one
|
|
114
|
-
})
|
|
115
|
-
|
|
116
|
-
if (found !== undefined) return spanned(found)
|
|
117
|
-
if (held === undefined) return undefined
|
|
118
|
-
const inside = listy(held, list)
|
|
119
|
-
return inside === undefined ? undefined : spanned(inside)
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
export function nestedBounds(text: string, where: Bounds, parent: string, list: string): Bounds | undefined {
|
|
123
|
-
const row = rowAt(text, where, parent)
|
|
124
|
-
if (row === undefined) return undefined
|
|
125
|
-
const inside = listy(row, list)
|
|
126
|
-
return inside === undefined ? undefined : spanned(inside)
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
function elements(text: string, where: Bounds): readonly ts.ObjectLiteralExpression[] {
|
|
130
|
-
let found: ts.ArrayLiteralExpression | undefined
|
|
131
|
-
walk(tree(text), (node) => {
|
|
132
|
-
if (found !== undefined) return
|
|
133
|
-
if (ts.isArrayLiteralExpression(node) && node.getStart() === where.open && node.getEnd() - 1 === where.close) {
|
|
134
|
-
found = node
|
|
135
|
-
}
|
|
136
|
-
})
|
|
137
|
-
if (found === undefined) return []
|
|
138
|
-
return found.elements.filter((one): one is ts.ObjectLiteralExpression => ts.isObjectLiteralExpression(one))
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
function rowAt(text: string, where: Bounds, id: string): ts.ObjectLiteralExpression | undefined {
|
|
142
|
-
return elements(text, where).find((one) => stringy(one, 'id') === id)
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
export function holdsRow(text: string, where: Bounds, id: string): boolean {
|
|
146
|
-
return rowAt(text, where, id) !== undefined
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
export function seededAt(text: string, where: Bounds, id: string): string | undefined {
|
|
150
|
-
const held = rowAt(text, where, id)
|
|
151
|
-
if (held === undefined) return undefined
|
|
152
|
-
const seeds = listy(held, 'seeds')
|
|
153
|
-
const derives = listy(held, 'derives')
|
|
154
|
-
if (seeds !== undefined && derives !== undefined) return undefined
|
|
155
|
-
const first = (seeds ?? derives)?.elements[0]
|
|
156
|
-
if (first === undefined || !ts.isObjectLiteralExpression(first)) return undefined
|
|
157
|
-
return stringy(first, 'at')
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
export function rowsIn(
|
|
161
|
-
text: string,
|
|
162
|
-
where: Bounds,
|
|
163
|
-
): readonly { readonly id: string; readonly at: string | undefined; readonly source: string }[] {
|
|
164
|
-
const found: { id: string; at: string | undefined; source: string }[] = []
|
|
165
|
-
for (const one of elements(text, where)) {
|
|
166
|
-
const id = stringy(one, 'id')
|
|
167
|
-
if (id === undefined) continue
|
|
168
|
-
|
|
169
|
-
const at = stringy(one, 'at')
|
|
170
|
-
const from = one.getStart()
|
|
171
|
-
const line = text.lastIndexOf('\n', from) + 1
|
|
172
|
-
const pad = text.slice(line, from)
|
|
173
|
-
found.push({ id, at, source: `${/^[ \t]*$/.test(pad) ? pad : ''}${text.slice(from, one.getEnd())},` })
|
|
174
|
-
}
|
|
175
|
-
return found
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
export function withRow(text: string, where: Bounds, row: string): string {
|
|
179
|
-
const held = /[ \t]*$/.exec(text.slice(0, where.close))?.[0] ?? ''
|
|
180
|
-
const before = text.slice(0, where.close - held.length)
|
|
181
|
-
const empty = before.trimEnd().endsWith('[')
|
|
182
|
-
const line = text.lastIndexOf('\n', where.open)
|
|
183
|
-
const pad = empty ? (/^[ \t]*/.exec(text.slice(line + 1))?.[0] ?? '') : held
|
|
184
|
-
return `${before}${empty ? '\n' : ''}${row}\n${pad}${text.slice(where.close)}`
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
export function withSeed(text: string, where: Bounds, id: string, seed: string): string {
|
|
188
|
-
const held = rowAt(text, where, id)
|
|
189
|
-
if (held === undefined) return text
|
|
190
|
-
if (listy(held, 'seeds') !== undefined) return text
|
|
191
|
-
|
|
192
|
-
const to = held.getEnd() - 1
|
|
193
|
-
const line = text.lastIndexOf('\n', held.getStart())
|
|
194
|
-
const pad = /^[ \t]*/.exec(text.slice(line + 1))?.[0] ?? ' '
|
|
195
|
-
const before = text.slice(0, to).trimEnd()
|
|
196
|
-
const comma = before.endsWith(',') ? '' : ','
|
|
197
|
-
return `${before}${comma}\n${pad} seeds: [\n${seed}\n${pad} ],\n${pad}${text.slice(to)}`
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
export function withRequires(text: string, where: Bounds, id: string, requires: readonly string[]): string | undefined {
|
|
201
|
-
const held = rowAt(text, where, id)
|
|
202
|
-
if (held === undefined) return undefined
|
|
203
|
-
|
|
204
|
-
const line = text.lastIndexOf('\n', held.getStart()) + 1
|
|
205
|
-
const pad = `${/^[ \t]*/.exec(text.slice(line))?.[0] ?? ' '} `
|
|
206
|
-
const written = requires.length === 0 ? '' : `${pad}requires: [${requires.map((one) => `'${one}'`).join(', ')}],\n`
|
|
207
|
-
|
|
208
|
-
const propertyNamed = (name: string): ts.PropertyAssignment | undefined => {
|
|
209
|
-
for (const one of held.properties) {
|
|
210
|
-
if (ts.isPropertyAssignment(one) && keyed(one) === name) return one
|
|
211
|
-
}
|
|
212
|
-
return undefined
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
const already = propertyNamed('requires')
|
|
216
|
-
if (already !== undefined) {
|
|
217
|
-
const opens = text.lastIndexOf('\n', already.getStart()) + 1
|
|
218
|
-
const shut = text.indexOf('\n', already.getEnd())
|
|
219
|
-
return `${text.slice(0, opens)}${written}${text.slice(shut === -1 ? text.length : shut + 1)}`
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
const version = propertyNamed('version')
|
|
223
|
-
if (version === undefined) return undefined
|
|
224
|
-
const after = text.indexOf('\n', version.getEnd())
|
|
225
|
-
const at = after === -1 ? text.length : after + 1
|
|
226
|
-
return `${text.slice(0, at)}${written}${text.slice(at)}`
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
export function seedsIn(text: string, where: Bounds, id: string): Bounds | undefined {
|
|
230
|
-
const held = rowAt(text, where, id)
|
|
231
|
-
if (held === undefined) return undefined
|
|
232
|
-
const list = listy(held, 'seeds')
|
|
233
|
-
return list === undefined ? undefined : spanned(list)
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
export function afterRow(text: string, where: Bounds, follows: string, row: string): string | undefined {
|
|
237
|
-
const held = rowAt(text, where, follows)
|
|
238
|
-
if (held === undefined) return undefined
|
|
239
|
-
let to = held.getEnd()
|
|
240
|
-
if (text[to] === ',') to += 1
|
|
241
|
-
return `${text.slice(0, to)}\n${row}${text.slice(to)}`
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
function headed(text: string): ts.ObjectLiteralExpression | undefined {
|
|
245
|
-
let found: ts.ObjectLiteralExpression | undefined
|
|
246
|
-
walk(tree(text), (node) => {
|
|
247
|
-
if (found !== undefined || !ts.isVariableDeclaration(node) || node.initializer === undefined) return
|
|
248
|
-
const one = ts.isAsExpression(node.initializer) ? node.initializer.expression : node.initializer
|
|
249
|
-
if (ts.isObjectLiteralExpression(one)) found = one
|
|
250
|
-
})
|
|
251
|
-
return found
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
function stated(node: ts.ObjectLiteralExpression, field: string): Bounds | undefined {
|
|
255
|
-
for (const one of node.properties) {
|
|
256
|
-
if (!ts.isPropertyAssignment(one) || keyed(one) !== field) continue
|
|
257
|
-
return { open: one.initializer.getStart(), close: one.initializer.getEnd() }
|
|
258
|
-
}
|
|
259
|
-
return undefined
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
export const quoted = (one: string): string => `'${one.replace(/\\/g, '\\\\').replace(/'/g, "\\'").replace(/\n/g, '\\n')}'`
|
|
263
|
-
|
|
264
|
-
const UNREAD = Symbol.for('thread.unread')
|
|
265
|
-
|
|
266
|
-
function valued(node: ts.Expression): unknown {
|
|
267
|
-
if (ts.isStringLiteral(node) || ts.isNoSubstitutionTemplateLiteral(node)) return node.text
|
|
268
|
-
if (ts.isNumericLiteral(node)) return Number(node.text)
|
|
269
|
-
if (node.kind === ts.SyntaxKind.TrueKeyword) return true
|
|
270
|
-
if (node.kind === ts.SyntaxKind.FalseKeyword) return false
|
|
271
|
-
if (node.kind === ts.SyntaxKind.NullKeyword) return null
|
|
272
|
-
if (ts.isAsExpression(node) || ts.isSatisfiesExpression(node) || ts.isParenthesizedExpression(node)) return valued(node.expression)
|
|
273
|
-
if (ts.isPrefixUnaryExpression(node) && node.operator === ts.SyntaxKind.MinusToken) {
|
|
274
|
-
const held = valued(node.operand)
|
|
275
|
-
return typeof held === 'number' ? -held : UNREAD
|
|
276
|
-
}
|
|
277
|
-
if (ts.isArrayLiteralExpression(node)) {
|
|
278
|
-
const held: unknown[] = []
|
|
279
|
-
for (const one of node.elements) {
|
|
280
|
-
const each = valued(one)
|
|
281
|
-
if (each === UNREAD) return UNREAD
|
|
282
|
-
held.push(each)
|
|
283
|
-
}
|
|
284
|
-
return held
|
|
285
|
-
}
|
|
286
|
-
if (ts.isObjectLiteralExpression(node)) {
|
|
287
|
-
const held: Record<string, unknown> = {}
|
|
288
|
-
for (const one of node.properties) {
|
|
289
|
-
if (!ts.isPropertyAssignment(one)) return UNREAD
|
|
290
|
-
const key = keyed(one)
|
|
291
|
-
if (key === undefined) return UNREAD
|
|
292
|
-
const each = valued(one.initializer)
|
|
293
|
-
if (each === UNREAD) return UNREAD
|
|
294
|
-
held[key] = each
|
|
295
|
-
}
|
|
296
|
-
return held
|
|
297
|
-
}
|
|
298
|
-
return UNREAD
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
function taken(text: string): readonly (readonly [string, unknown])[] {
|
|
302
|
-
const found: (readonly [string, unknown])[] = []
|
|
303
|
-
walk(tree(text), (node) => {
|
|
304
|
-
if (!ts.isVariableDeclaration(node) || node.initializer === undefined || !ts.isIdentifier(node.name)) return
|
|
305
|
-
found.push([node.name.text, valued(node.initializer)])
|
|
306
|
-
})
|
|
307
|
-
return found
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
export function valuesIn(text: string): Readonly<Record<string, unknown>> {
|
|
311
|
-
const found: Record<string, unknown> = {}
|
|
312
|
-
for (const [name, held] of taken(text)) {
|
|
313
|
-
if (held !== UNREAD) found[name] = held
|
|
314
|
-
}
|
|
315
|
-
return found
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
export function unread(text: string): readonly string[] {
|
|
319
|
-
return taken(text).filter(([, held]) => held === UNREAD).map(([name]) => name)
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
export const STATED = ['at', 'describes', 'version']
|
|
323
|
-
|
|
324
|
-
export function reconciled(held: string, source: string, fields: readonly string[]): string {
|
|
325
|
-
const ships = headed(source)
|
|
326
|
-
if (ships === undefined) return held
|
|
327
|
-
const named = stringy(ships, 'id')
|
|
328
|
-
|
|
329
|
-
let next = held
|
|
330
|
-
for (const field of fields) {
|
|
331
|
-
const want = stated(ships, field)
|
|
332
|
-
if (want === undefined) continue
|
|
333
|
-
|
|
334
|
-
const mine = headed(next)
|
|
335
|
-
if (mine === undefined || stringy(mine, 'id') !== named) return held
|
|
336
|
-
const have = stated(mine, field)
|
|
337
|
-
if (have === undefined) continue
|
|
338
|
-
const said = source.slice(want.open, want.close)
|
|
339
|
-
if (next.slice(have.open, have.close) === said) continue
|
|
340
|
-
next = next.slice(0, have.open) + said + next.slice(have.close)
|
|
341
|
-
}
|
|
342
|
-
return next
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
export function fieldIn(text: string, where: Bounds, id: string, field: string): string | undefined {
|
|
346
|
-
const held = rowAt(text, where, id)
|
|
347
|
-
if (held === undefined) return undefined
|
|
348
|
-
for (const one of held.properties) {
|
|
349
|
-
if (!ts.isPropertyAssignment(one) || keyed(one) !== field) continue
|
|
350
|
-
return text.slice(one.initializer.getStart(), one.initializer.getEnd())
|
|
351
|
-
}
|
|
352
|
-
return undefined
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
export function withField(
|
|
356
|
-
text: string,
|
|
357
|
-
where: Bounds,
|
|
358
|
-
id: string,
|
|
359
|
-
field: string,
|
|
360
|
-
value: string,
|
|
361
|
-
): string | undefined {
|
|
362
|
-
const held = rowAt(text, where, id)
|
|
363
|
-
return held === undefined ? undefined : fieldOn(text, held, field, value)
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
export function withTopField(text: string, field: string, value: string): string | undefined {
|
|
367
|
-
const held = headed(text)
|
|
368
|
-
return held === undefined ? undefined : fieldOn(text, held, field, value)
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
export function withoutField(text: string, where: Bounds, id: string, field: string): string {
|
|
372
|
-
const held = rowAt(text, where, id)
|
|
373
|
-
const one = held?.properties.find((each) => ts.isPropertyAssignment(each) && keyed(each) === field)
|
|
374
|
-
if (held === undefined || one === undefined) return text
|
|
375
|
-
let ends = one.getEnd()
|
|
376
|
-
if (text[ends] === ',') ends += 1
|
|
377
|
-
const line = text.lastIndexOf('\n', one.getStart())
|
|
378
|
-
if (line > held.getStart()) return text.slice(0, line) + text.slice(ends)
|
|
379
|
-
return text.slice(0, one.getStart()) + text.slice(text[ends] === ' ' ? ends + 1 : ends)
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
function fieldOn(text: string, held: ts.ObjectLiteralExpression, field: string, value: string): string | undefined {
|
|
383
|
-
for (const one of held.properties) {
|
|
384
|
-
if (!ts.isPropertyAssignment(one) || keyed(one) !== field) continue
|
|
385
|
-
return `${text.slice(0, one.initializer.getStart())}${value}${text.slice(one.initializer.getEnd())}`
|
|
386
|
-
}
|
|
387
|
-
|
|
388
|
-
const last = held.properties.at(-1)
|
|
389
|
-
if (last === undefined) return undefined
|
|
390
|
-
|
|
391
|
-
let ends = last.getEnd()
|
|
392
|
-
if (text[ends] === ',') ends += 1
|
|
393
|
-
const already = text[ends - 1] === ',' ? '' : ','
|
|
394
|
-
|
|
395
|
-
if (!text.slice(held.getStart(), held.getEnd()).includes('\n')) {
|
|
396
|
-
return `${text.slice(0, ends)}${already} ${field}: ${value}${text.slice(ends)}`
|
|
397
|
-
}
|
|
398
|
-
|
|
399
|
-
const line = text.lastIndexOf('\n', last.getStart()) + 1
|
|
400
|
-
const pad = /^[ \t]*/.exec(text.slice(line))?.[0] ?? ' '
|
|
401
|
-
return `${text.slice(0, ends)}${already}\n${pad}${field}: ${value},${text.slice(ends)}`
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
export function withoutRow(text: string, where: Bounds, id: string): string {
|
|
405
|
-
const held = rowAt(text, where, id)
|
|
406
|
-
if (held === undefined) return text
|
|
407
|
-
let ends = held.getEnd()
|
|
408
|
-
if (text[ends] === ',') ends += 1
|
|
409
|
-
const pad = /[ \t]*$/.exec(text.slice(0, held.getStart()))?.[0] ?? ''
|
|
410
|
-
const from = held.getStart() - pad.length
|
|
411
|
-
if (text[ends] === '\n') ends += 1
|
|
412
|
-
const left = text.slice(0, from)
|
|
413
|
-
const right = text.slice(ends)
|
|
414
|
-
return left.trimEnd().endsWith('[') && right.trimStart().startsWith(']')
|
|
415
|
-
? `${left.replace(/\s*$/, '')}${right.replace(/^\s*/, '')}`
|
|
416
|
-
: left + right
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
const lines = (held: unknown, pad: string): readonly string[] => {
|
|
420
|
-
if (held === null || held === undefined) return [String(held)]
|
|
421
|
-
if (Array.isArray(held)) {
|
|
422
|
-
if (held.length === 0) return ['[]']
|
|
423
|
-
if (held.every((one) => typeof one !== 'object' || one === null)) return [held.map((one) => String(one)).join(', ')]
|
|
424
|
-
return held.flatMap((one) => {
|
|
425
|
-
const [first = '', ...rest] = lines(one, `${pad} `)
|
|
426
|
-
return [`${pad}- ${first.trimStart()}`, ...rest]
|
|
427
|
-
})
|
|
428
|
-
}
|
|
429
|
-
if (typeof held === 'object') {
|
|
430
|
-
const parts = Object.entries(held as Record<string, unknown>)
|
|
431
|
-
if (parts.length === 0) return ['{}']
|
|
432
|
-
return parts.flatMap(([key, one]) => {
|
|
433
|
-
const [first = '', ...rest] = lines(one, `${pad} `)
|
|
434
|
-
return rest.length === 0 && !first.includes('\n') ? [`${pad}${key}: ${first}`] : [`${pad}${key}:`, first, ...rest]
|
|
435
|
-
})
|
|
436
|
-
}
|
|
437
|
-
return [String(held)]
|
|
438
|
-
}
|
|
439
|
-
|
|
440
|
-
export function marked(parts: readonly string[]): string {
|
|
441
|
-
return createHash('sha256').update(parts.join('\n')).digest('hex').slice(0, 6)
|
|
442
|
-
}
|
|
443
|
-
|
|
444
|
-
export const markOf = (row: object): string => {
|
|
445
|
-
const held = row as { readonly id?: unknown; readonly at?: unknown; readonly describes?: unknown; readonly version?: unknown }
|
|
446
|
-
return marked([
|
|
447
|
-
typeof held.id === 'string' ? held.id : '',
|
|
448
|
-
typeof held.at === 'string' ? held.at : '',
|
|
449
|
-
typeof held.describes === 'string' ? held.describes : '',
|
|
450
|
-
held.version === undefined ? '' : String(held.version),
|
|
451
|
-
])
|
|
452
|
-
}
|
|
453
|
-
|
|
454
|
-
const WHOLE = /^-?[0-9][0-9_]*(?:\.[0-9]+)?$/
|
|
455
|
-
|
|
456
|
-
export function literalIn(text: string): unknown {
|
|
457
|
-
const said = text.trim()
|
|
458
|
-
if (WHOLE.test(said)) return Number(said.split('_').join(''))
|
|
459
|
-
const held = taken(`const held = ${said}`)
|
|
460
|
-
return held.length === 1 && held[0]?.[1] !== UNREAD ? held[0]?.[1] : UNREAD
|
|
461
|
-
}
|
|
462
|
-
|
|
463
|
-
export const unreadable = (held: unknown): boolean => held === UNREAD
|
|
464
|
-
|
|
465
|
-
const BARE = /^[A-Za-z_$][A-Za-z0-9_$]*$/
|
|
466
|
-
|
|
467
|
-
function inline(held: unknown): string {
|
|
468
|
-
if (typeof held === 'string') return quoted(held)
|
|
469
|
-
if (Array.isArray(held)) return `[${held.map(inline).join(', ')}]`
|
|
470
|
-
if (held !== null && typeof held === 'object') {
|
|
471
|
-
const pairs = Object.entries(held).map(([key, one]) => `${BARE.test(key) ? key : quoted(key)}: ${inline(one)}`)
|
|
472
|
-
return pairs.length === 0 ? '{}' : `{ ${pairs.join(', ')} }`
|
|
473
|
-
}
|
|
474
|
-
return String(held)
|
|
475
|
-
}
|
|
476
|
-
|
|
477
|
-
export function written(held: unknown, pad: string): string {
|
|
478
|
-
if (!Array.isArray(held) || held.every((one) => typeof one !== 'object' || one === null)) return inline(held)
|
|
479
|
-
return ['[', ...held.map((one) => `${pad} ${inline(one)},`), `${pad}]`].join('\n')
|
|
480
|
-
}
|
|
481
|
-
|
|
482
|
-
export function withWritten(text: string, where: Bounds, id: string, field: string, held: unknown): string | undefined {
|
|
483
|
-
const row = rowAt(text, where, id)
|
|
484
|
-
if (row === undefined) return undefined
|
|
485
|
-
const named = row.properties.find((one) => ts.isPropertyAssignment(one) && keyed(one) === field) ?? row.properties.at(-1)
|
|
486
|
-
if (named === undefined) return undefined
|
|
487
|
-
const line = text.lastIndexOf('\n', named.getStart()) + 1
|
|
488
|
-
return fieldOn(text, row, field, written(held, /^[ \t]*/.exec(text.slice(line))?.[0] ?? ''))
|
|
489
|
-
}
|
|
490
|
-
|
|
491
|
-
export function showing(row: object, only: readonly string[] = []): string {
|
|
492
|
-
const held = row as Record<string, unknown>
|
|
493
|
-
const keys = Object.keys(held)
|
|
494
|
-
|
|
495
|
-
const wanted = only.filter((one) => one !== '')
|
|
496
|
-
const stray = wanted.filter((one) => !keys.includes(one))
|
|
497
|
-
if (stray.length > 0) {
|
|
498
|
-
return `${stray.map((one) => `\`${one}\``).join(', ')} ${stray.length === 1 ? 'is not a field' : 'are not fields'} of this row. It states ${keys.join(', ')}\n`
|
|
499
|
-
}
|
|
500
|
-
|
|
501
|
-
const showing = wanted.length === 0 ? keys : wanted
|
|
502
|
-
return `${showing.flatMap((key) => {
|
|
503
|
-
const [first = '', ...rest] = lines(held[key], ' ')
|
|
504
|
-
return rest.length === 0 ? [`${key}: ${first.trimStart()}`] : [`${key}:`, first, ...rest]
|
|
505
|
-
}).join('\n')}\n`
|
|
506
|
-
}
|
package/dist/core/scope.ts
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
export const ORGANISATION = '@prestack'
|
|
2
|
-
|
|
3
|
-
export const THING = 'thread'
|
|
4
|
-
|
|
5
|
-
export const NAMED = `${ORGANISATION}/${THING}`
|
|
6
|
-
|
|
7
|
-
const CORE = 'core'
|
|
8
|
-
|
|
9
|
-
const HEAD = `${NAMED}-`
|
|
10
|
-
|
|
11
|
-
export const named = (id: string): string => (id === CORE ? NAMED : `${HEAD}${id}`)
|
|
12
|
-
|
|
13
|
-
export const idOf = (name: string): string | undefined => {
|
|
14
|
-
if (name === NAMED) return CORE
|
|
15
|
-
return name.startsWith(HEAD) ? name.slice(HEAD.length) : undefined
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export const ours = (name: string): boolean => idOf(name) !== undefined
|
|
19
|
-
|
|
20
|
-
export const installed = (at: string): boolean => at.startsWith('node_modules/') && ours(at.split('/').slice(1, 3).join('/'))
|
package/dist/core/thread.ts
DELETED
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
export interface Quote {
|
|
2
|
-
readonly on: string
|
|
3
|
-
readonly said: string
|
|
4
|
-
}
|
|
5
|
-
|
|
6
|
-
export interface Threaded {
|
|
7
|
-
readonly id: string
|
|
8
|
-
readonly at: string
|
|
9
|
-
readonly describes: string
|
|
10
|
-
readonly version?: number
|
|
11
|
-
readonly cites?: readonly Quote[]
|
|
12
|
-
readonly requires?: readonly string[]
|
|
13
|
-
readonly takes?: readonly string[]
|
|
14
|
-
readonly by?: string
|
|
15
|
-
readonly runs?: string
|
|
16
|
-
readonly declares?: readonly Threaded[]
|
|
17
|
-
readonly derives?: readonly Threaded[]
|
|
18
|
-
readonly seeds?: readonly Threaded[]
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export interface Member extends Threaded {
|
|
22
|
-
readonly version: number
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export const PREFIXES = {
|
|
26
|
-
workflow: 'wf_',
|
|
27
|
-
view: 'vw_',
|
|
28
|
-
agent: 'agt_',
|
|
29
|
-
} as const satisfies Readonly<Record<string, string>>
|
package/dist/core/tree.ts
DELETED
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
import { FOLDER, attached, existsSync, readdirSync, statSync } from '../integrations/disk.ts'
|
|
2
|
-
import { dirname, join, relative, resolve } from 'node:path'
|
|
3
|
-
import { cwd, env } from 'node:process'
|
|
4
|
-
|
|
5
|
-
export { FOLDER }
|
|
6
|
-
|
|
7
|
-
const NAMES = ['_threads', 'threads'] as const
|
|
8
|
-
const ROOT = 'thread.ts'
|
|
9
|
-
|
|
10
|
-
export interface Tree {
|
|
11
|
-
readonly at: string
|
|
12
|
-
readonly dir: string
|
|
13
|
-
readonly name: string
|
|
14
|
-
readonly root: string
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export class UnthreadedRepositoryError extends Error {
|
|
18
|
-
constructor(from: string, floor?: string) {
|
|
19
|
-
super(
|
|
20
|
-
'no tree above ' + from + ' — looked for a ' + NAMES.map((one) => one + '/').join(' or ') + ' directory in every directory up to ' + (floor === undefined ? 'the filesystem root' : floor + ', the folder this was pointed at') + '. ' +
|
|
21
|
-
'Run `thread init` to write one.',
|
|
22
|
-
)
|
|
23
|
-
this.name = 'UnthreadedRepositoryError'
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
function given(): string {
|
|
28
|
-
const named = env[FOLDER]
|
|
29
|
-
return named === undefined ? '' : named
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export function folder(): string {
|
|
33
|
-
const named = given()
|
|
34
|
-
if (named !== '') return resolve(named)
|
|
35
|
-
if (attached())
|
|
36
|
-
throw new Error(
|
|
37
|
-
'this is answering for a tree on another machine and nothing said which folder that is. ' +
|
|
38
|
-
FOLDER +
|
|
39
|
-
' carries it and is not set here. Falling back to the folder this process was started in would read ' +
|
|
40
|
-
'the wrong tree and report it as yours, so it stopped instead.',
|
|
41
|
-
)
|
|
42
|
-
return cwd()
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
export function findTree(from: string = folder()): Tree {
|
|
46
|
-
const named = given()
|
|
47
|
-
const floor = attached() && named !== '' ? resolve(named) : undefined
|
|
48
|
-
let at = resolve(from)
|
|
49
|
-
for (;;) {
|
|
50
|
-
for (const name of NAMES) {
|
|
51
|
-
const dir = join(at, name)
|
|
52
|
-
if (existsSync(dir) && statSync(dir).isDirectory()) return { at, dir, name, root: join(dir, ROOT) }
|
|
53
|
-
}
|
|
54
|
-
const up = dirname(at)
|
|
55
|
-
if (up === at) throw new UnthreadedRepositoryError(from, floor)
|
|
56
|
-
if (at === floor) throw new UnthreadedRepositoryError(from, floor)
|
|
57
|
-
at = up
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
export function strayFiles(root: string, prefix: string, ends: string): readonly string[] {
|
|
62
|
-
const skip = new Set(['node_modules', '.git', '.next', 'dist', '_generated', 'coverage'])
|
|
63
|
-
const found: string[] = []
|
|
64
|
-
const walk = (dir: string): void => {
|
|
65
|
-
|
|
66
|
-
let held
|
|
67
|
-
try {
|
|
68
|
-
held = readdirSync(dir, { withFileTypes: true })
|
|
69
|
-
} catch {
|
|
70
|
-
return
|
|
71
|
-
}
|
|
72
|
-
for (const one of held) {
|
|
73
|
-
if (one.name.startsWith('.') || skip.has(one.name)) continue
|
|
74
|
-
const path = join(dir, one.name)
|
|
75
|
-
if (one.isDirectory()) {
|
|
76
|
-
walk(path)
|
|
77
|
-
continue
|
|
78
|
-
}
|
|
79
|
-
if (one.name.startsWith(prefix) && one.name.endsWith(ends)) {
|
|
80
|
-
found.push(relative(root, path).split('\\').join('/'))
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
walk(root)
|
|
85
|
-
return found.sort()
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
export function inTree(tree: Tree, file: string): string {
|
|
89
|
-
return join(tree.dir, file)
|
|
90
|
-
}
|