@prestack/thread 0.0.2 → 0.1.0

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 (183) hide show
  1. package/CHANGELOG.md +98 -0
  2. package/dist/core/{asking.d.ts → answers.d.ts} +6 -14
  3. package/dist/core/{asking.js → answers.js} +13 -332
  4. package/dist/core/{asking.ts → answers.ts} +14 -357
  5. package/dist/core/attach.js +2 -2
  6. package/dist/core/attach.ts +2 -3
  7. package/dist/core/chain.js +4 -11
  8. package/dist/core/chain.ts +4 -3
  9. package/dist/core/client.d.ts +57 -0
  10. package/dist/core/client.js +186 -0
  11. package/dist/core/client.ts +227 -0
  12. package/dist/core/descend.d.ts +2 -0
  13. package/dist/core/descend.js +109 -16
  14. package/dist/core/descend.ts +120 -16
  15. package/dist/core/files.d.ts +45 -0
  16. package/dist/core/files.js +95 -0
  17. package/dist/core/files.ts +137 -0
  18. package/dist/core/init.d.ts +1 -0
  19. package/dist/core/init.js +10 -2
  20. package/dist/core/init.ts +8 -2
  21. package/dist/core/invoked.js +1 -1
  22. package/dist/core/invoked.ts +1 -1
  23. package/dist/core/rows.d.ts +2 -0
  24. package/dist/core/rows.js +65 -0
  25. package/dist/core/rows.ts +58 -0
  26. package/dist/core/tree.js +1 -1
  27. package/dist/core/tree.ts +1 -1
  28. package/dist/derivation/attach.js +0 -12
  29. package/dist/derivation/attach.ts +0 -12
  30. package/dist/derivation/generators/build.js +8 -7
  31. package/dist/derivation/generators/build.ts +8 -7
  32. package/dist/derivation/generators/release.d.ts +1 -1
  33. package/dist/derivation/generators/release.js +15 -23
  34. package/dist/derivation/generators/release.ts +15 -15
  35. package/dist/integrations/attach.js +1 -19
  36. package/dist/integrations/attach.ts +1 -19
  37. package/dist/integrations/generators/read.js +2 -2
  38. package/dist/integrations/generators/read.ts +4 -4
  39. package/dist/integrations/generators/vault.js +4 -12
  40. package/dist/integrations/generators/vault.ts +4 -4
  41. package/dist/integrations/module.js +5 -3
  42. package/dist/integrations/module.ts +5 -3
  43. package/dist/integrations/vault.js +5 -5
  44. package/dist/integrations/vault.ts +5 -5
  45. package/dist/integrity/attach.js +0 -12
  46. package/dist/integrity/attach.ts +0 -12
  47. package/dist/integrity/check-threads.js +1 -1
  48. package/dist/integrity/check-threads.ts +1 -1
  49. package/dist/integrity/constraint.d.ts +11 -2
  50. package/dist/integrity/constraint.js +163 -20
  51. package/dist/integrity/constraint.ts +174 -22
  52. package/dist/integrity/generators/compiler.js +1 -1
  53. package/dist/integrity/generators/compiler.ts +1 -1
  54. package/dist/integrity/generators/gate.js +8 -16
  55. package/dist/integrity/generators/gate.ts +8 -7
  56. package/dist/integrity/generators/publish.js +14 -26
  57. package/dist/integrity/generators/publish.ts +14 -17
  58. package/dist/integrity/generators/trial.d.ts +2 -2
  59. package/dist/integrity/generators/trial.js +108 -50
  60. package/dist/integrity/generators/trial.ts +107 -48
  61. package/dist/integrity/lint.js +3 -3
  62. package/dist/integrity/lint.ts +3 -3
  63. package/dist/integrity/lsp/diagnostics.js +1 -1
  64. package/dist/integrity/lsp/diagnostics.ts +1 -1
  65. package/dist/integrity/lsp/lint.js +1 -1
  66. package/dist/integrity/lsp/lint.ts +1 -1
  67. package/dist/integrity/slopnator.d.ts +1 -14
  68. package/dist/integrity/slopnator.js +8 -40
  69. package/dist/integrity/slopnator.ts +9 -51
  70. package/dist/integrity/slopnators/sl_broken_lineage.js +4 -2
  71. package/dist/integrity/slopnators/sl_broken_lineage.ts +4 -2
  72. package/dist/integrity/slopnators/sl_declared_elsewhere.js +24 -3
  73. package/dist/integrity/slopnators/sl_declared_elsewhere.ts +24 -3
  74. package/dist/integrity/slopnators/sl_derived_drift.js +10 -4
  75. package/dist/integrity/slopnators/sl_derived_drift.ts +10 -4
  76. package/dist/integrity/slopnators/sl_inert_island.js +7 -13
  77. package/dist/integrity/slopnators/sl_inert_island.ts +7 -16
  78. package/dist/integrity/slopnators/sl_inert_slopnator.js +8 -8
  79. package/dist/integrity/slopnators/sl_inert_slopnator.ts +8 -8
  80. package/dist/integrity/slopnators/sl_missing_row.js +14 -9
  81. package/dist/integrity/slopnators/sl_missing_row.ts +14 -10
  82. package/dist/integrity/slopnators/sl_no_child_process.js +8 -8
  83. package/dist/integrity/slopnators/sl_no_child_process.ts +8 -8
  84. package/dist/integrity/slopnators/sl_no_import_cycle.js +1 -1
  85. package/dist/integrity/slopnators/sl_no_import_cycle.ts +1 -1
  86. package/dist/integrity/slopnators/sl_no_such_command.js +20 -5
  87. package/dist/integrity/slopnators/sl_no_such_command.ts +22 -5
  88. package/dist/integrity/slopnators/sl_only_takes_a_literal.js +8 -8
  89. package/dist/integrity/slopnators/sl_only_takes_a_literal.ts +8 -8
  90. package/dist/integrity/slopnators/sl_stale_path.js +91 -18
  91. package/dist/integrity/slopnators/sl_stale_path.ts +97 -18
  92. package/dist/integrity/slopnators/sl_twice_meant.js +25 -2
  93. package/dist/integrity/slopnators/sl_twice_meant.ts +28 -2
  94. package/dist/integrity/slopnators/sl_twice_parented.js +26 -5
  95. package/dist/integrity/slopnators/sl_twice_parented.ts +26 -5
  96. package/dist/integrity/slopnators/sl_undeclared_node.js +18 -2
  97. package/dist/integrity/slopnators/sl_undeclared_node.ts +18 -2
  98. package/dist/integrity/slopnators/sl_undeclared_operation.js +3 -1
  99. package/dist/integrity/slopnators/sl_undeclared_operation.ts +3 -1
  100. package/dist/integrity/slopnators/sl_undeclared_variant.js +6 -2
  101. package/dist/integrity/slopnators/sl_undeclared_variant.ts +6 -2
  102. package/dist/integrity/slopnators/sl_ungoverned_island.js +3 -4
  103. package/dist/integrity/slopnators/sl_ungoverned_island.ts +3 -4
  104. package/dist/integrity/slopnators/sl_unkept_rows.js +1 -1
  105. package/dist/integrity/slopnators/sl_unkept_rows.ts +1 -1
  106. package/dist/integrity/slopnators/sl_unpaired_member.js +13 -8
  107. package/dist/integrity/slopnators/sl_unpaired_member.ts +16 -8
  108. package/dist/integrity/slopnators/sl_unpaired_slopnator.js +16 -3
  109. package/dist/integrity/slopnators/sl_unpaired_slopnator.ts +16 -3
  110. package/dist/integrity/slopnators/sl_unthreaded.d.ts +1 -0
  111. package/dist/integrity/slopnators/sl_unthreaded.js +18 -10
  112. package/dist/integrity/slopnators/sl_unthreaded.ts +19 -11
  113. package/dist/integrity/slopnators/sl_untranslated_key.js +1 -1
  114. package/dist/integrity/slopnators/sl_untranslated_key.ts +1 -1
  115. package/dist/integrity/slopnators/sl_unversioned.js +16 -2
  116. package/dist/integrity/slopnators/sl_unversioned.ts +16 -2
  117. package/dist/integrity/sweep.js +17 -3
  118. package/dist/integrity/sweep.ts +20 -3
  119. package/dist/knowledge/attach.js +0 -6
  120. package/dist/knowledge/attach.ts +0 -6
  121. package/dist/knowledge/generators/docsgen.js +12 -3
  122. package/dist/knowledge/generators/docsgen.ts +12 -2
  123. package/dist/knowledge/generators/glossary.js +1 -1
  124. package/dist/knowledge/generators/glossary.ts +1 -1
  125. package/dist/operations/attach.js +2 -26
  126. package/dist/operations/attach.ts +2 -27
  127. package/dist/operations/dispatch.js +2 -3
  128. package/dist/operations/dispatch.ts +2 -3
  129. package/dist/operations/generators/capability.js +4 -7
  130. package/dist/operations/generators/capability.ts +3 -6
  131. package/dist/operations/generators/compile.js +8 -13
  132. package/dist/operations/generators/compile.ts +8 -4
  133. package/dist/operations/generators/permits.js +4 -13
  134. package/dist/operations/generators/permits.ts +3 -3
  135. package/dist/operations/generators/seed.js +5 -2
  136. package/dist/operations/generators/seed.ts +6 -2
  137. package/dist/operations/generators/trial.js +10 -10
  138. package/dist/operations/generators/trial.ts +10 -10
  139. package/dist/operations/http.js +4 -1
  140. package/dist/operations/http.ts +6 -1
  141. package/package.json +2 -2
  142. package/dist/core/cli.d.ts +0 -2
  143. package/dist/core/cli.js +0 -1761
  144. package/dist/core/cli.ts +0 -2004
  145. package/dist/core/generators/meta.d.ts +0 -2
  146. package/dist/core/generators/meta.js +0 -829
  147. package/dist/core/generators/meta.ts +0 -1006
  148. package/dist/derivation/generators/derivation.d.ts +0 -3
  149. package/dist/derivation/generators/derivation.js +0 -273
  150. package/dist/derivation/generators/derivation.ts +0 -347
  151. package/dist/derivation/makers/release.d.ts +0 -1
  152. package/dist/derivation/makers/release.js +0 -107
  153. package/dist/derivation/makers/release.ts +0 -135
  154. package/dist/integrations/generators/secret.d.ts +0 -1
  155. package/dist/integrations/generators/secret.js +0 -233
  156. package/dist/integrations/generators/secret.ts +0 -289
  157. package/dist/integrations/generators/target.d.ts +0 -1
  158. package/dist/integrations/generators/target.js +0 -775
  159. package/dist/integrations/generators/target.ts +0 -944
  160. package/dist/integrity/generators/rule.d.ts +0 -2
  161. package/dist/integrity/generators/rule.js +0 -213
  162. package/dist/integrity/generators/rule.ts +0 -271
  163. package/dist/integrity/generators/seal.d.ts +0 -4
  164. package/dist/integrity/generators/seal.js +0 -101
  165. package/dist/integrity/generators/seal.ts +0 -117
  166. package/dist/integrity/sealed.d.ts +0 -1
  167. package/dist/integrity/sealed.js +0 -56
  168. package/dist/integrity/sealed.ts +0 -56
  169. package/dist/knowledge/generators/skills.d.ts +0 -1
  170. package/dist/knowledge/generators/skills.js +0 -222
  171. package/dist/knowledge/generators/skills.ts +0 -263
  172. package/dist/operations/generators/grant.d.ts +0 -1
  173. package/dist/operations/generators/grant.js +0 -267
  174. package/dist/operations/generators/grant.ts +0 -336
  175. package/dist/operations/generators/operation.d.ts +0 -1
  176. package/dist/operations/generators/operation.js +0 -811
  177. package/dist/operations/generators/operation.ts +0 -972
  178. package/dist/operations/makers/capability.d.ts +0 -1
  179. package/dist/operations/makers/capability.js +0 -117
  180. package/dist/operations/makers/capability.ts +0 -166
  181. package/dist/operations/makers/trial.d.ts +0 -1
  182. package/dist/operations/makers/trial.js +0 -413
  183. package/dist/operations/makers/trial.ts +0 -520
@@ -1,5 +1,5 @@
1
1
  import { createHash, randomBytes } from 'node:crypto'
2
- import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs'
2
+ import { existsSync, readFileSync, rmSync, writeFileSync } from './files.ts'
3
3
  import { tmpdir } from 'node:os'
4
4
  import { dirname, join, relative } from 'node:path'
5
5
  import { argv, env, exit, stderr, stdin, stdout } from 'node:process'
@@ -8,11 +8,9 @@ import { pathToFileURL } from 'node:url'
8
8
  import { CONSTRAINTS } from '../integrity/constraint.ts'
9
9
  import type { Row } from './descend.ts'
10
10
  import { LISTS, descend, listFields, listShape, shapeFields, shapesNamed, type Field } from './descend.ts'
11
- import { quoted } from './rows.ts'
11
+ import { quoted, valuesIn } from './rows.ts'
12
12
  import type { Tree } from './tree.ts'
13
13
 
14
- const RECEIPTS = join(tmpdir(), 'thread-create')
15
-
16
14
  export const RECEIPT = '--receipt'
17
15
 
18
16
  export const ENDS = '.'
@@ -135,77 +133,6 @@ export interface Said {
135
133
  readonly optional?: boolean
136
134
  }
137
135
 
138
- let meanings: ReadonlyMap<string, string> | undefined
139
-
140
- function coined(word: string): string | undefined {
141
- if (meanings === undefined) {
142
- const found = new Map<string, string>()
143
- try {
144
- for (const [id, held] of descend().coined) {
145
- const first = held.said[0]
146
- if (first !== undefined && first !== '') found.set(id, first)
147
- }
148
- } catch {
149
- found.clear()
150
- }
151
- meanings = found
152
- }
153
- return meanings.get(word)
154
- }
155
-
156
- export function asksOf(
157
- noun: string,
158
- shape: string | readonly Field[],
159
- said: Readonly<Record<string, Said>>,
160
- computed: readonly string[],
161
- ): readonly Asked[] {
162
- const fields: readonly Field[] = typeof shape === 'string' ? shapeFields(shape).map((one) => ({ ...one, boolean: false })) : shape
163
- if (fields.length === 0) {
164
- refuse(
165
- `nothing installed here states what a ${noun} is made of, so \`thread create ${noun}\` has no questions to ` +
166
- `ask. It reads that from the shape a ${noun} is declared with rather than from a list kept beside it. ` +
167
- 'Run `npx thread attach` to see whether the package that states it is taken on.',
168
- )
169
- }
170
-
171
- const spoken: Record<string, Said> = { ...said }
172
- for (const one of fields) {
173
- if (spoken[one.name] !== undefined || computed.includes(one.name)) continue
174
- const meaning = coined(one.name) ?? (one.guide === '' ? undefined : one.guide)
175
- if (meaning === undefined) continue
176
- spoken[one.name] = { guide: meaning }
177
- }
178
-
179
- const mute = fields
180
- .filter((one) => one.required && !computed.includes(one.name) && spoken[one.name] === undefined)
181
- .map((one) => one.name)
182
- if (mute.length > 0) {
183
- const one = mute.length === 1
184
- refuse(
185
- `every ${noun} states ${mute.join(', ')}, and \`thread create ${noun}\` never asks for ${one ? 'it' : 'them'}. ` +
186
- `It would write a ${noun} with no ${mute.join(' and no ')}, and tell you it worked. ` +
187
- `Give ${one ? 'it' : 'each'} a sentence where this command lists what it asks, or name ${one ? 'it' : 'them'} ` +
188
- 'as something the command fills in.',
189
- )
190
- }
191
-
192
- return Object.keys(spoken).map((one) => {
193
- const told = spoken[one] as Said
194
- const known = fields.find((each) => each.name === one)
195
- const spare = told.optional ?? (known !== undefined && !known.required)
196
- return {
197
- at: one,
198
- guide: known?.boolean === true ? `${told.guide} — yes or no` : told.guide,
199
- ...(told.asks === undefined ? {} : { asks: told.asks }),
200
- ...(spare ? { optional: true } : {}),
201
- ...(known?.list === true ? { list: true } : {}),
202
- ...(known?.map === true ? { map: true } : {}),
203
- ...(known?.boolean === true ? { boolean: true } : {}),
204
- ...(known?.worded === true ? { worded: true } : {}),
205
- }
206
- })
207
- }
208
-
209
136
  export const named = (all: readonly Asked[]): readonly string[] => all.map((one) => one.at)
210
137
 
211
138
  export const prompted = (one: Asked): string =>
@@ -468,8 +395,7 @@ export function seeding(kids: ReadonlyMap<string, readonly Child[]>, root: strin
468
395
  continue
469
396
  }
470
397
 
471
- mkdirSync(dirname(into), { recursive: true })
472
- writeFileSync(into, one.body.endsWith('\n') ? one.body : `${one.body}\n`, 'utf8')
398
+ writeFileSync(into, one.body.endsWith('\n') ? one.body : `${one.body}\n`, 'utf8')
473
399
  said.push(`wrote ${one.at} with the body you gave it.`)
474
400
  }
475
401
  }
@@ -580,21 +506,20 @@ export async function bodied(
580
506
  return written === '' ? undefined : written
581
507
  }
582
508
 
583
- export function guidance(all: readonly Asked[]): readonly string[] {
584
- const width = Math.max(...all.map((one) => one.at.length)) + 3
585
- const under = ' '.repeat(width + 2)
586
- return all.flatMap((one) => {
587
- const lines = wrapped(one.optional === true ? `${one.guide.replace(/\.$/, '')}. Leave it blank and it is not stated` : one.guide, '')
588
- return [` ${`${one.at}:`.padEnd(width)}${lines[0] ?? ''}`, ...lines.slice(1).map((each) => `${under}${each}`)]
589
- })
509
+ export function only(
510
+ given: readonly { readonly at: number; readonly key: string; readonly value: string }[],
511
+ allowed: readonly string[],
512
+ where: string,
513
+ ): void {
514
+ unasked(ownAnswers(given), allowed, where)
590
515
  }
591
516
 
592
- export function only(
517
+ export function unasked(
593
518
  given: readonly { readonly at: number; readonly key: string; readonly value: string }[],
594
519
  allowed: readonly string[],
595
520
  where: string,
596
521
  ): void {
597
- for (const one of ownAnswers(given)) {
522
+ for (const one of given) {
598
523
  if (CARRIES.includes(one.key) || one.key === WRITES) continue
599
524
  if (allowed.includes(one.key)) continue
600
525
  refuse(
@@ -680,8 +605,6 @@ const citesOf = (held: unknown): readonly Told[] =>
680
605
  )
681
606
  : []
682
607
 
683
- const HELD = ['holds', 'declares'] as const
684
-
685
608
  const NAMED_SHAPE = /^[A-Za-z_][A-Za-z0-9_]*$/
686
609
 
687
610
  const rowLike = (one: unknown): one is Row =>
@@ -736,7 +659,7 @@ export async function declared(tree: Tree): Promise<ReadonlyMap<string, Declared
736
659
 
737
660
  let read: Record<string, unknown>
738
661
  try {
739
- read = (await import(pathToFileURL(join(tree.at, at)).href)) as Record<string, unknown>
662
+ read = valuesIn(readFileSync(join(tree.at, at))) as Record<string, unknown>
740
663
  } catch {
741
664
  return
742
665
  }
@@ -747,7 +670,7 @@ export async function declared(tree: Tree): Promise<ReadonlyMap<string, Declared
747
670
  | undefined
748
671
  const lists: readonly { readonly list: string; readonly rows: readonly unknown[] }[] = Array.isArray(value)
749
672
  ? [{ list: name, rows: value }]
750
- : HELD.flatMap((list) => {
673
+ : (['holds', 'declares'] as const).flatMap((list) => {
751
674
  const rows = node?.[list]
752
675
  return Array.isArray(rows) ? [{ list, rows }] : []
753
676
  })
@@ -845,80 +768,6 @@ export function nouns(all: readonly Row[], verb = 'create'): ReadonlyMap<string,
845
768
  return found
846
769
  }
847
770
 
848
- const spends = (root: string, nonce: string): string =>
849
- join(RECEIPTS, `${createHash('sha256').update(root).digest('hex').slice(0, 16)}-${nonce}.txt`)
850
-
851
- const stamp = (listing: string): string => createHash('sha256').update(listing).digest('hex').slice(0, 12)
852
-
853
- function beingMade(): string {
854
- const words = argv.slice(2).filter((one) => !one.startsWith('-'))
855
- if (words[0] === 'create' && typeof words[1] === 'string' && words[1] !== '') return words[1]
856
- const file = (argv[1] ?? '').split('\\').join('/').split('/').at(-1) ?? ''
857
- return file.replace(/\.[^.]+$/, '')
858
- }
859
-
860
- export const SKILLS_AT = 'thread/knowledge'
861
-
862
- export function skillOf(noun: string): string {
863
- return `${SKILLS_AT}/${noun}.md`
864
- }
865
-
866
- function readTheSkill(): readonly string[] {
867
- const noun = env.THREAD_NOUN ?? beingMade()
868
- if (noun === '') return []
869
- return [
870
- '',
871
- `READ ${skillOf(noun)} BEFORE YOU WRITE ANYTHING. It is this command's own output, kept as a`,
872
- 'file, and it lists everything that already exists. Building a second copy of something the tree',
873
- 'already has is the most expensive mistake available here and it has already been made: thirteen',
874
- 'elements were declared in one app that every one of them already shipped.',
875
- '',
876
- 'If that file is not there, run `npx thread skills` and it will be.',
877
- ]
878
- }
879
-
880
- export function issue(root: string, listing: string): string {
881
- const nonce = randomBytes(6).toString('hex')
882
- mkdirSync(RECEIPTS, { recursive: true })
883
- writeFileSync(spends(root, nonce), `${stamp(listing)}\n`, 'utf8')
884
- const said = readTheSkill()
885
- if (said.length > 0) stdout.write(`${said.join('\n')}\n`)
886
- return nonce
887
- }
888
-
889
- export function spend(root: string, listing: string, offered: string | undefined, sheet: string): void {
890
- if (offered === undefined || offered.startsWith('-')) {
891
- refuse(
892
- `\`${RECEIPT}\` was handed nothing to check. Run \`thread create\` with no arguments — it lists what is ` +
893
- 'here and issues a receipt to hand back.',
894
- )
895
- }
896
- const path = spends(root, offered)
897
- if (!existsSync(path)) {
898
- refuse(
899
- `\`${offered}\` is not a receipt this tree issued, or it has been spent already — one run per receipt. ` +
900
- 'Run `thread create` again and use the one it prints.',
901
- )
902
- }
903
- let when = ''
904
- try {
905
- when = readFileSync(path, 'utf8').trim()
906
- } catch {
907
- when = ''
908
- }
909
-
910
- process.on('exit', (code) => {
911
- if (code === 0) rmSync(path, { force: true })
912
- })
913
-
914
- if (when !== stamp(listing)) {
915
- refuse(
916
- `${sheet} has changed since that receipt was issued, so what you read is not what is here. ` +
917
- 'Run `thread create` again.',
918
- )
919
- }
920
- }
921
-
922
771
  export function asking(
923
772
  held: readonly { readonly at: number; readonly key: string; readonly value: string }[],
924
773
  ): {
@@ -1054,126 +903,7 @@ export function handed(): string | undefined {
1054
903
  return stood
1055
904
  }
1056
905
 
1057
- export const TYPED = 'npx thread'
1058
-
1059
- const folded = (said: string, pad: string, room = 92): readonly string[] => {
1060
- const out: string[] = []
1061
- let line = ''
1062
- for (const word of said.split(/\s+/)) {
1063
- if (line === '') line = word
1064
- else if (`${line} ${word}`.length <= room) line = `${line} ${word}`
1065
- else {
1066
- out.push(line)
1067
- line = word
1068
- }
1069
- }
1070
- if (line !== '') out.push(line)
1071
- return out.map((one) => `${pad}${one}`)
1072
- }
1073
-
1074
- const parented = ((): readonly string[] => {
1075
- const one = CONSTRAINTS.find((each) => each.id === 'twice-parented')
1076
- if (one === undefined) {
1077
- return [
1078
- '',
1079
- 'THIS PAGE CANNOT STATE THE RULE. It reads what may declare a file out of the `twice-parented`',
1080
- 'rule, and no rule by that name ships any more. Whatever governs this now, it is not said here —',
1081
- `\`${TYPED} create rule\` lists every rule that does exist.`,
1082
- ]
1083
- }
1084
- return [
1085
- '',
1086
- 'which one you pick is a rule rather than a preference, and a check refuses the tree over it:',
1087
- '',
1088
- ` ${one.id}`,
1089
- ...folded(`bans ${one.bans}.`, ' '),
1090
- ...folded(one.instead.replace(/^./, (each) => each.toUpperCase()), ' '),
1091
- ]
1092
- })()
1093
-
1094
- const NEXT: Readonly<Record<string, string>> = {
1095
- declares: 'It writes the file from that, and nothing else runs. A file already on disk is left alone and the row simply names it.',
1096
- derives: `It writes NO file at \`at:\`, because the command in \`by:\` writes that one — which is why there is no ${asFlag(BODY)} here to give. If this tree has no command answering \`by:\`, that generator is declared first and ${asFlag(WRITES)} is what goes in it.`,
1097
- seeds: `TWO files come out of this and a different hand writes each, which is the whole reason there are two bodies. If this tree has no command answering \`by:\`, that generator is declared here too, so one run leaves the row, the generator and the file.`,
1098
- }
1099
-
1100
- export const VERBS: readonly string[] = [
1101
- 'nothing is in this tree until a row names it, and which of three lists the row sits in says who',
1102
- 'wrote the file. Pick the one that is true and the rest of this framework follows from it:',
1103
- '',
1104
- ' declares a person wrote it. Exactly one row may ever declare an address, and a second is',
1105
- ' refused: a file with two owners has two answers for where it came from and the tree',
1106
- ' can give neither.',
1107
- ' derives a command wrote it. Delete the file, run the command again, and the same bytes come',
1108
- ' back. Many rows may derive one address, because a derivation owns nothing — it states',
1109
- ' `by:`, the command that writes it, and `runs:`, what catches it drifting.',
1110
- ' seeds a command wrote the shape and a person fills in the part no machine can supply. Many',
1111
- ' rows may seed one address as well, and it is proved by shape rather than by bytes,',
1112
- ' because half of what is on disk is your own writing.',
1113
- '',
1114
- `a file in none of them is not in this app at all — \`${TYPED} descent\` names every one it reached`,
1115
- 'and every one it did not, and nothing undeclared is compiled in or shipped.',
1116
- '',
1117
- 'and the list decides what this command does next, not just what the row says. What it does next is',
1118
- 'ASK FOR BODIES, and there are two of them, which are never the same text and never the same file:',
1119
- '',
1120
- ` ${asFlag(BODY).padEnd(10)} the body of the file the row names. A person wrote it.`,
1121
- ` ${asFlag(WRITES).padEnd(10)} the body of the GENERATOR — the code that writes a template into that file.`,
1122
- '',
1123
- 'give each as text on the command. Neither may be taken out of a file, because a file is not a',
1124
- 'receipt: nothing here checked it and no descent reached it. Single-quote a body that holds quotes.',
1125
- '',
1126
- 'so each list takes a different set of them, and giving one where the other belongs is the mistake',
1127
- 'this section exists to stop:',
1128
- '',
1129
- ...LISTS.flatMap((list) => {
1130
- const takes = [
1131
- ...(WRITTEN.includes(list) ? [asFlag(WRITES)] : []),
1132
- ...(BODIED.includes(list) ? [asFlag(BODY)] : []),
1133
- ]
1134
- const said = folded(
1135
- `takes ${takes.length === 0 ? 'no body at all' : takes.join(' and ')}. ${NEXT[list] ?? ''}`,
1136
- ' ',
1137
- )
1138
- return said.map((line, at) => (at === 0 ? ` ${list.padEnd(11)}${line.trim()}` : line))
1139
- }),
1140
- ...parented,
1141
- ]
1142
-
1143
- export const grows = (sheetAt: string, noun: string): readonly string[] => {
1144
- const shaped = noun.split('-').map((one) => `${one.slice(0, 1).toUpperCase()}${one.slice(1)}`).join('')
1145
- return [
1146
- `this is also how the CLI grows. A list of rows in ${sheetAt} becomes a command: declare`,
1147
- `\`export const THINGS: readonly Thing[] = []\` beside what is already there and \`${TYPED} create thing\``,
1148
- 'answers from that moment, asking for whatever a `Thing` states and writing the row into that list.',
1149
- '',
1150
- `The same is true of what you have now: \`${TYPED} create ${noun}\` exists because ${sheetAt} declares`,
1151
- `\`${shaped}\`. Nothing was added to a list of commands anywhere — the CLI reads what your tree says.`,
1152
- ]
1153
- }
1154
-
1155
- export const VALUED = 'THREAD_VALUED'
1156
-
1157
- export function answering(noun: string): readonly string[] {
1158
- return [
1159
- ...(env[VALUED] === '1' ? [] : [...VERBS, '']),
1160
- 'what `new` asks. Give each answer as a flag on one command and close with the receipt:',
1161
- '',
1162
- ` ${TYPED} create ${noun} new --<answer> <value> ... ${RECEIPT} <receipt>`,
1163
- '',
1164
- 'Flags need no terminal, no pipe and no file, so that form works everywhere. The two others',
1165
- 'each need something you may not have: one answer to a line on stdin needs a shell that can',
1166
- 'redirect, and `--answers <path>` needs a way to write the file first. Use them when you have',
1167
- 'them; the line above is the one that never depends on it.',
1168
- '',
1169
- 'Where a question repeats — a list of things, each with answers of its own — there are no',
1170
- 'separate flag names for the inner ones. Give the group\'s own flag again to begin the next,',
1171
- 'then its answers after it, reusing the same words. They are read in the order you write them,',
1172
- 'and everything before the first group flag belongs to the thing itself.',
1173
- '',
1174
- 'the answers, in order:',
1175
- ]
1176
- }
906
+ export const TYPED = 'thread'
1177
907
 
1178
908
  export const CARRIED: readonly { readonly verb: string; readonly typed: string; readonly does: string }[] = [
1179
909
  { verb: 'read', typed: `${TYPED} read <row>`, does: 'prints every field a row states' },
@@ -1183,79 +913,6 @@ export const CARRIED: readonly { readonly verb: string; readonly typed: string;
1183
913
 
1184
914
  const elsewhere = CARRIED.map((one) => `\`${one.typed}\` ${one.does}`)
1185
915
 
1186
- export const ELSEWHERE: readonly string[] = [
1187
- 'reading a row, changing one of its answers and taking it out are done in one place, whatever',
1188
- 'declared it \u2014 name the row by its id or by the mark printed beside it above:',
1189
- '',
1190
- ...CARRIED.map((one) => ` ${one.typed.padEnd(40)}${one.does}`),
1191
- ]
1192
-
1193
- const opening = (said: string): string => said.split(/(?<=\.)\s/)[0] ?? said
1194
-
1195
- const article = (word: string): string => (/^[aeiou]/.test(word) ? 'an' : 'a')
1196
-
1197
- export function taught(noun: string, told: ReadonlyMap<string, Declared>, commands: ReadonlySet<string>): readonly string[] {
1198
- const one = told.get(noun)
1199
- const opens: readonly (readonly [string, string])[] = [
1200
- [`${TYPED} read <id>`, 'the row, and the file it names'],
1201
- [`${TYPED} create ${noun}`, 'this list again, with a fresh receipt'],
1202
- ...(commands.has('thread inspect')
1203
- ? [[`${TYPED} inspect <word>`, 'every row whose name or sentence carries the word'] as const]
1204
- : []),
1205
- ]
1206
- const top = one?.top
1207
- const siblings =
1208
- top === undefined
1209
- ? []
1210
- : [...told]
1211
- .filter(([, each]) => each.top?.id === top.id && each.describes !== '')
1212
- .sort(([left], [right]) => (left < right ? -1 : 1))
1213
- const pad = Math.max(0, ...siblings.map(([word]) => word.length)) + 3
1214
- return [
1215
- 'DO NOT CREATE WHAT ALREADY EXISTS. Everything listed below is declared and yours to use. A thing',
1216
- 'you cannot find is usually one of these under a word you have not met \u2014 read the sentences, not',
1217
- 'the names.',
1218
- '',
1219
- 'IF NOTHING FITS, SAY SO AND STOP. Do not coin a name: a name is a meaning, and a meaning is',
1220
- 'declared rather than invented. Being told the list is short a thing is a useful answer.',
1221
- '',
1222
- 'NOW GO AND OPEN ONE. A sentence is a summary, and every expensive mistake here was made from a',
1223
- 'summary. These print the real thing, and none of them writes anything:',
1224
- '',
1225
- ...opens.map(([said, does]) => ` ${said.padEnd(30)}${does}`),
1226
- '',
1227
- ...(one === undefined || one.describes === ''
1228
- ? []
1229
- : [
1230
- `what ${article(noun)} ${noun} is:`,
1231
- '',
1232
- ...wrapped(one.describes, ' '),
1233
- ...one.cites.flatMap((said) => ['', ...wrapped(`said on ${said.on}: "${said.said}"`, ' ')]),
1234
- '',
1235
- ]),
1236
- ...(top === undefined || siblings.length < 2
1237
- ? []
1238
- : [
1239
- `${top.id} \u2014 ${opening(top.describes)}`,
1240
- 'what it makes, and what each one is. Pick the one that is true before declaring anything:',
1241
- '',
1242
- ...siblings.flatMap(([word, each]) => {
1243
- const lines = wrapped(opening(each.describes), '')
1244
- return [` ${word.padEnd(pad)}${lines[0] ?? ''}`, ...lines.slice(1).map((line) => `${' '.repeat(pad + 2)}${line}`)]
1245
- }),
1246
- '',
1247
- ]),
1248
- ]
1249
- }
1250
-
1251
- export function reminded(noun: string | undefined): string {
1252
- const lists =
1253
- noun === undefined || noun === 'meta'
1254
- ? `\`${TYPED} create\` lists what this tree makes, and any noun run bare lists what exists, each with its sentence`
1255
- : `\`${TYPED} create ${noun}\` lists what exists, each with its sentence`
1256
- return `thread: before the next thing, ${lists}. Do not create what already exists, and if nothing fits, say so and stop.`
1257
- }
1258
-
1259
916
  export function wordsIn(offered: readonly string[]): readonly string[] {
1260
917
  const values = new Set([argv[argv.indexOf(RECEIPT) + 1], argv[argv.indexOf(ANSWERS) + 1], ...flagged().map((one) => one.value)])
1261
918
  const words = argv.slice(2).filter((one) => !one.startsWith('-') && !values.has(one))
@@ -6,14 +6,14 @@ var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExte
6
6
  }
7
7
  return path;
8
8
  };
9
- import { existsSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
9
+ import { existsSync, readFileSync, rmSync, writeFileSync } from "./files.js";
10
10
  import { createRequire } from 'node:module';
11
11
  import { join, relative, resolve } from 'node:path';
12
12
  import { stdout } from 'node:process';
13
+ import { LISTS } from "./descend.js";
13
14
  import { ROOT, TREE, root as rooted } from "./init.js";
14
15
  import { STATED, holdsRow, listBounds, quoted, reconciled, rowsIn, withRow, withoutRow } from "./rows.js";
15
16
  import { ours as isOurs } from "./scope.js";
16
- const LISTS = ['declares', 'derives', 'seeds'];
17
17
  const SHAPE = /export interface [A-Za-z0-9_]+ extends Threaded \{[\s\S]*?\n\}\n/;
18
18
  const MEMBERS = 'MEMBERS';
19
19
  const DOOR = '/attach';
@@ -1,14 +1,13 @@
1
- import { existsSync, readFileSync, rmSync, writeFileSync } from 'node:fs'
1
+ import { existsSync, readFileSync, rmSync, writeFileSync } from './files.ts'
2
2
  import { createRequire } from 'node:module'
3
3
  import { join, relative, resolve } from 'node:path'
4
4
  import { stdout } from 'node:process'
5
5
 
6
+ import { LISTS } from './descend.ts'
6
7
  import { ROOT, TREE, root as rooted } from './init.ts'
7
8
  import { STATED, holdsRow, listBounds, quoted, reconciled, rowsIn, withRow, withoutRow } from './rows.ts'
8
9
  import { ours as isOurs } from './scope.ts'
9
10
 
10
- const LISTS = ['declares', 'derives', 'seeds']
11
-
12
11
  const SHAPE = /export interface [A-Za-z0-9_]+ extends Threaded \{[\s\S]*?\n\}\n/
13
12
 
14
13
  const MEMBERS = 'MEMBERS'
@@ -1,16 +1,9 @@
1
- var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExtension) || function (path, preserveJsx) {
2
- if (typeof path === "string" && /^\.\.?\//.test(path)) {
3
- return path.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function (m, tsx, d, ext, cm) {
4
- return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : (d + ext + "." + cm.toLowerCase() + "js");
5
- });
6
- }
7
- return path;
8
- };
9
1
  import { spawnSync } from 'node:child_process';
10
- import { existsSync, readdirSync } from 'node:fs';
11
2
  import { join } from 'node:path';
12
3
  import { execPath } from 'node:process';
13
- import { fileURLToPath, pathToFileURL } from 'node:url';
4
+ import { fileURLToPath } from 'node:url';
5
+ import { existsSync, readFileSync, readdirSync } from "./files.js";
6
+ import { valuesIn } from "./rows.js";
14
7
  import { findTree } from "./tree.js";
15
8
  const STATING = 'derivation.ts';
16
9
  export async function generators(tree = findTree()) {
@@ -23,7 +16,7 @@ export async function generators(tree = findTree()) {
23
16
  for (const name of sheets) {
24
17
  const sheet = join(tree.dir, name);
25
18
  try {
26
- const held = (await import(__rewriteRelativeImportExtension(pathToFileURL(sheet).href)));
19
+ const held = valuesIn(readFileSync(sheet));
27
20
  found.push(...(held.GENERATORS ?? []));
28
21
  }
29
22
  catch (thrown) {
@@ -1,10 +1,11 @@
1
1
  import { spawnSync } from 'node:child_process'
2
- import { existsSync, readdirSync } from 'node:fs'
3
2
  import { join } from 'node:path'
4
3
  import { execPath } from 'node:process'
5
- import { fileURLToPath, pathToFileURL } from 'node:url'
4
+ import { fileURLToPath } from 'node:url'
6
5
 
7
6
  import type { Row } from './descend.ts'
7
+ import { existsSync, readFileSync, readdirSync } from './files.ts'
8
+ import { valuesIn } from './rows.ts'
8
9
  import { findTree, type Tree } from './tree.ts'
9
10
 
10
11
  const STATING = 'derivation.ts'
@@ -20,7 +21,7 @@ export async function generators(tree: Tree = findTree()): Promise<readonly Row[
20
21
  for (const name of sheets) {
21
22
  const sheet = join(tree.dir, name)
22
23
  try {
23
- const held = (await import(pathToFileURL(sheet).href)) as { GENERATORS?: readonly Row[] }
24
+ const held = valuesIn(readFileSync(sheet)) as { GENERATORS?: readonly Row[] }
24
25
  found.push(...(held.GENERATORS ?? []))
25
26
  } catch (thrown) {
26
27
  throw new Error(
@@ -0,0 +1,57 @@
1
+ #!/usr/bin/env node
2
+ import { type Kind, type Listed, type Measured } from './files.ts';
3
+ export type Order = {
4
+ readonly order: 'kind';
5
+ readonly at: string;
6
+ } | {
7
+ readonly order: 'read';
8
+ readonly at: string;
9
+ } | {
10
+ readonly order: 'list';
11
+ readonly at: string;
12
+ } | {
13
+ readonly order: 'stat';
14
+ readonly at: string;
15
+ } | {
16
+ readonly order: 'remove';
17
+ readonly at: string;
18
+ } | {
19
+ readonly order: 'real';
20
+ readonly at: string;
21
+ } | {
22
+ readonly order: 'write';
23
+ readonly at: string;
24
+ readonly said: string;
25
+ };
26
+ export type Answered = {
27
+ readonly answered: 'kind';
28
+ readonly held: Kind;
29
+ } | {
30
+ readonly answered: 'read';
31
+ readonly said: string;
32
+ } | {
33
+ readonly answered: 'list';
34
+ readonly held: readonly Listed[];
35
+ } | {
36
+ readonly answered: 'stat';
37
+ readonly held: Measured;
38
+ } | {
39
+ readonly answered: 'written';
40
+ } | {
41
+ readonly answered: 'removed';
42
+ } | {
43
+ readonly answered: 'real';
44
+ readonly at: string;
45
+ } | {
46
+ readonly answered: 'refused';
47
+ readonly because: string;
48
+ };
49
+ export declare function carry(one: Order): Answered;
50
+ export interface Turn {
51
+ readonly of: string;
52
+ readonly orders?: readonly Order[];
53
+ readonly say?: string;
54
+ readonly done?: boolean;
55
+ }
56
+ export declare function talking(at: string, key: string, message: string): Promise<number>;
57
+ export declare function run(said: readonly string[]): Promise<number>;