@prestack/thread 0.1.21 → 0.1.22

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