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