@prestack/thread 0.1.3 → 0.1.5

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 (270) hide show
  1. package/CHANGELOG.md +563 -0
  2. package/dist/core/actor.d.ts +1 -1
  3. package/dist/core/actor.js +5 -4
  4. package/dist/core/actor.ts +5 -4
  5. package/dist/core/attach.d.ts +0 -7
  6. package/dist/core/attach.js +38 -67
  7. package/dist/core/attach.ts +34 -80
  8. package/dist/core/chain.js +13 -17
  9. package/dist/core/chain.ts +11 -15
  10. package/dist/core/condition.d.ts +4 -4
  11. package/dist/core/condition.js +2 -2
  12. package/dist/core/descend.d.ts +4 -2
  13. package/dist/core/descend.js +91 -23
  14. package/dist/core/descend.ts +90 -21
  15. package/dist/core/errors/catalogue.d.ts +52 -0
  16. package/dist/{operations/errors/errors.js → core/errors/catalogue.js} +1 -1
  17. package/dist/{operations/errors/errors.ts → core/errors/catalogue.ts} +1 -1
  18. package/dist/{operations/errors/catalog.d.ts → core/errors/error.d.ts} +2 -3
  19. package/dist/{operations/errors/catalog.js → core/errors/error.js} +1 -16
  20. package/dist/{operations/errors/catalog.ts → core/errors/error.ts} +3 -19
  21. package/dist/core/errors/namespaces.d.ts +3 -0
  22. package/dist/core/errors/namespaces.js +15 -0
  23. package/dist/core/errors/namespaces.ts +19 -0
  24. package/dist/core/finding.js +2 -2
  25. package/dist/core/finding.ts +1 -2
  26. package/dist/core/init.d.ts +1 -1
  27. package/dist/core/init.js +51 -5
  28. package/dist/core/init.ts +58 -5
  29. package/dist/core/invoked.d.ts +1 -1
  30. package/dist/core/invoked.js +3 -1
  31. package/dist/core/invoked.ts +5 -2
  32. package/dist/core/round.js +36 -22
  33. package/dist/core/round.ts +26 -9
  34. package/dist/core/rows.d.ts +4 -0
  35. package/dist/core/rows.js +31 -2
  36. package/dist/core/rows.ts +33 -1
  37. package/dist/core/scope.d.ts +1 -0
  38. package/dist/core/scope.js +1 -0
  39. package/dist/core/scope.ts +2 -0
  40. package/dist/core/thread.d.ts +3 -3
  41. package/dist/core/tree.d.ts +4 -1
  42. package/dist/core/tree.js +26 -6
  43. package/dist/core/tree.ts +29 -6
  44. package/dist/core/types.d.ts +3 -3
  45. package/dist/core/types.js +3 -1
  46. package/dist/core/types.ts +2 -1
  47. package/dist/derivation/attach.js +3 -2
  48. package/dist/derivation/attach.ts +2 -1
  49. package/dist/derivation/generators/build.js +6 -6
  50. package/dist/derivation/generators/build.ts +1 -1
  51. package/dist/derivation/generators/check.js +1 -1
  52. package/dist/derivation/generators/determinism.js +1 -1
  53. package/dist/derivation/generators/release.js +60 -47
  54. package/dist/derivation/generators/release.ts +66 -43
  55. package/dist/derivation/release.d.ts +7 -3
  56. package/dist/derivation/release.js +14 -1
  57. package/dist/derivation/release.ts +22 -2
  58. package/dist/integrations/attach.d.ts +4 -0
  59. package/dist/integrations/attach.js +37 -56
  60. package/dist/integrations/attach.ts +54 -60
  61. package/dist/integrations/cli.d.ts +37 -0
  62. package/dist/integrations/cli.js +440 -0
  63. package/dist/integrations/cli.ts +543 -0
  64. package/dist/{core/files.d.ts → integrations/disk.d.ts} +16 -24
  65. package/dist/integrations/disk.js +435 -0
  66. package/dist/integrations/disk.ts +465 -0
  67. package/dist/integrations/generators/read.js +6 -6
  68. package/dist/integrations/generators/read.ts +4 -4
  69. package/dist/integrations/generators/vault.js +10 -11
  70. package/dist/integrations/generators/vault.ts +4 -5
  71. package/dist/{operations/http.d.ts → integrations/https.d.ts} +10 -6
  72. package/dist/integrations/https.js +170 -0
  73. package/dist/integrations/https.ts +216 -0
  74. package/dist/integrations/pglite.d.ts +26 -0
  75. package/dist/integrations/pglite.js +143 -0
  76. package/dist/integrations/pglite.ts +206 -0
  77. package/dist/integrations/secret.d.ts +8 -2
  78. package/dist/integrations/secret.js +3 -5
  79. package/dist/integrations/secret.ts +10 -6
  80. package/dist/integrations/target.d.ts +155 -14
  81. package/dist/integrations/target.js +171 -50
  82. package/dist/integrations/target.ts +377 -61
  83. package/dist/integrations/vault.d.ts +1 -0
  84. package/dist/integrations/vault.js +20 -5
  85. package/dist/integrations/vault.ts +17 -5
  86. package/dist/integrity/attach.js +8 -77
  87. package/dist/integrity/attach.ts +7 -76
  88. package/dist/integrity/check-threads.js +5 -5
  89. package/dist/integrity/check-threads.ts +2 -2
  90. package/dist/integrity/constraint.d.ts +0 -1
  91. package/dist/integrity/constraint.js +267 -366
  92. package/dist/integrity/constraint.ts +267 -375
  93. package/dist/integrity/generators/compiler.js +14 -17
  94. package/dist/integrity/generators/compiler.ts +10 -15
  95. package/dist/integrity/generators/gate.js +15 -15
  96. package/dist/integrity/generators/gate.ts +10 -10
  97. package/dist/integrity/generators/issues.js +7 -7
  98. package/dist/integrity/generators/publish.js +39 -57
  99. package/dist/integrity/generators/publish.ts +44 -55
  100. package/dist/integrity/generators/trial.d.ts +3 -3
  101. package/dist/integrity/generators/trial.js +133 -165
  102. package/dist/integrity/generators/trial.ts +145 -169
  103. package/dist/integrity/lint.js +9 -13
  104. package/dist/integrity/lint.ts +7 -10
  105. package/dist/integrity/lsp/bin.js +5 -4
  106. package/dist/integrity/lsp/bin.ts +3 -1
  107. package/dist/integrity/lsp/diagnostics.js +3 -3
  108. package/dist/integrity/lsp/diagnostics.ts +1 -1
  109. package/dist/integrity/lsp/lint.js +19 -30
  110. package/dist/integrity/lsp/lint.ts +16 -32
  111. package/dist/integrity/lsp/server.js +3 -3
  112. package/dist/integrity/slopnator.d.ts +3 -13
  113. package/dist/integrity/slopnator.js +59 -38
  114. package/dist/integrity/slopnator.ts +54 -38
  115. package/dist/integrity/slopnators/sl_broken_lineage.js +2 -2
  116. package/dist/integrity/slopnators/sl_declared_elsewhere.js +1 -1
  117. package/dist/integrity/slopnators/sl_derived_drift.js +4 -4
  118. package/dist/integrity/slopnators/sl_derived_drift.ts +1 -1
  119. package/dist/integrity/slopnators/sl_door_in_a_node.js +220 -0
  120. package/dist/integrity/slopnators/sl_door_in_a_node.ts +250 -0
  121. package/dist/integrity/slopnators/sl_inert_island.js +10 -8
  122. package/dist/integrity/slopnators/sl_inert_island.ts +8 -6
  123. package/dist/integrity/slopnators/sl_missing_row.js +6 -51
  124. package/dist/integrity/slopnators/sl_missing_row.ts +4 -50
  125. package/dist/integrity/slopnators/sl_no_barrel.js +1 -1
  126. package/dist/integrity/slopnators/sl_no_child_process.js +36 -18
  127. package/dist/integrity/slopnators/sl_no_child_process.ts +44 -18
  128. package/dist/integrity/slopnators/sl_no_dispatch_bypass.js +80 -149
  129. package/dist/integrity/slopnators/sl_no_dispatch_bypass.ts +95 -147
  130. package/dist/integrity/slopnators/sl_no_import_cycle.js +15 -4
  131. package/dist/integrity/slopnators/sl_no_import_cycle.ts +20 -4
  132. package/dist/integrity/slopnators/sl_no_such_command.js +2 -2
  133. package/dist/integrity/slopnators/sl_no_such_command.ts +1 -1
  134. package/dist/integrity/slopnators/sl_outside_an_operation.js +20 -0
  135. package/dist/integrity/slopnators/sl_outside_an_operation.ts +25 -0
  136. package/dist/integrity/slopnators/sl_screen_holds_only_declared.d.ts +2 -0
  137. package/dist/integrity/slopnators/sl_screen_holds_only_declared.js +61 -0
  138. package/dist/integrity/slopnators/sl_screen_holds_only_declared.ts +70 -0
  139. package/dist/integrity/slopnators/sl_secret_literal.js +1 -33
  140. package/dist/integrity/slopnators/sl_secret_literal.ts +1 -33
  141. package/dist/integrity/slopnators/sl_stale_path.js +5 -22
  142. package/dist/integrity/slopnators/sl_stale_path.ts +4 -20
  143. package/dist/integrity/slopnators/sl_twice_meant.js +1 -1
  144. package/dist/integrity/slopnators/sl_twice_parented.js +29 -5
  145. package/dist/integrity/slopnators/sl_twice_parented.ts +28 -4
  146. package/dist/integrity/slopnators/sl_undeclared_crossing.d.ts +4 -1
  147. package/dist/integrity/slopnators/sl_undeclared_crossing.js +426 -62
  148. package/dist/integrity/slopnators/sl_undeclared_crossing.ts +454 -70
  149. package/dist/integrity/slopnators/sl_undeclared_destination.js +59 -25
  150. package/dist/integrity/slopnators/sl_undeclared_destination.ts +69 -21
  151. package/dist/integrity/slopnators/sl_undeclared_list.d.ts +2 -0
  152. package/dist/integrity/slopnators/sl_undeclared_list.js +363 -0
  153. package/dist/integrity/slopnators/sl_undeclared_list.ts +358 -0
  154. package/dist/integrity/slopnators/sl_undeclared_node.js +1 -11
  155. package/dist/integrity/slopnators/sl_undeclared_node.ts +0 -10
  156. package/dist/integrity/slopnators/sl_undeclared_operation.js +111 -29
  157. package/dist/integrity/slopnators/sl_undeclared_operation.ts +134 -26
  158. package/dist/integrity/slopnators/sl_undeclared_variant.js +2 -2
  159. package/dist/integrity/slopnators/sl_undeclared_webhook.d.ts +2 -0
  160. package/dist/integrity/slopnators/sl_undeclared_webhook.js +90 -0
  161. package/dist/integrity/slopnators/sl_undeclared_webhook.ts +108 -0
  162. package/dist/integrity/slopnators/sl_underived_route.d.ts +2 -0
  163. package/dist/integrity/slopnators/sl_underived_route.js +61 -0
  164. package/dist/integrity/slopnators/sl_underived_route.ts +72 -0
  165. package/dist/integrity/slopnators/sl_ungoverned_island.js +3 -3
  166. package/dist/integrity/slopnators/sl_ungoverned_island.ts +1 -1
  167. package/dist/integrity/slopnators/sl_unlisted_program.d.ts +2 -0
  168. package/dist/integrity/slopnators/sl_unlisted_program.js +39 -0
  169. package/dist/integrity/slopnators/sl_unlisted_program.ts +40 -0
  170. package/dist/integrity/slopnators/sl_unnamed_island.js +1 -1
  171. package/dist/integrity/slopnators/sl_unpaired_member.js +3 -3
  172. package/dist/integrity/slopnators/sl_unpaired_member.ts +1 -1
  173. package/dist/integrity/slopnators/sl_unpaired_slopnator.js +4 -4
  174. package/dist/integrity/slopnators/sl_unpaired_slopnator.ts +1 -1
  175. package/dist/integrity/slopnators/sl_unprefixed_member.js +5 -31
  176. package/dist/integrity/slopnators/sl_unprefixed_member.ts +2 -28
  177. package/dist/integrity/slopnators/sl_unthreaded.js +11 -7
  178. package/dist/integrity/slopnators/sl_unthreaded.ts +6 -7
  179. package/dist/integrity/slopnators/sl_untranslated_key.js +2 -25
  180. package/dist/integrity/slopnators/sl_untranslated_key.ts +1 -25
  181. package/dist/integrity/slopnators/sl_unvaulted_secret.d.ts +2 -0
  182. package/dist/integrity/slopnators/sl_unvaulted_secret.js +107 -0
  183. package/dist/integrity/slopnators/sl_unvaulted_secret.ts +132 -0
  184. package/dist/integrity/slopnators/sl_unversioned.js +6 -4
  185. package/dist/integrity/slopnators/sl_unversioned.ts +5 -3
  186. package/dist/integrity/sweep.d.ts +1 -0
  187. package/dist/integrity/sweep.js +105 -42
  188. package/dist/integrity/sweep.ts +115 -42
  189. package/dist/knowledge/attach.js +4 -3
  190. package/dist/knowledge/attach.ts +3 -2
  191. package/dist/knowledge/generators/docsgen.js +8 -6
  192. package/dist/knowledge/generators/docsgen.ts +4 -2
  193. package/dist/knowledge/generators/glossary.js +4 -4
  194. package/dist/knowledge/generators/glossary.ts +1 -1
  195. package/dist/operations/access.d.ts +23 -0
  196. package/dist/operations/access.js +28 -1
  197. package/dist/operations/access.ts +43 -0
  198. package/dist/operations/attach.js +33 -113
  199. package/dist/operations/attach.ts +51 -117
  200. package/dist/operations/capabilities.d.ts +14 -5
  201. package/dist/operations/capabilities.js +13 -4
  202. package/dist/operations/capabilities.ts +27 -8
  203. package/dist/operations/dispatch.d.ts +8 -2
  204. package/dist/operations/dispatch.js +128 -185
  205. package/dist/operations/dispatch.ts +138 -191
  206. package/dist/operations/error-results.d.ts +1 -1
  207. package/dist/operations/error-results.js +2 -2
  208. package/dist/operations/error-results.ts +2 -2
  209. package/dist/operations/generators/capability.d.ts +11 -1
  210. package/dist/operations/generators/capability.js +481 -224
  211. package/dist/operations/generators/capability.ts +562 -233
  212. package/dist/operations/generators/compile.js +66 -39
  213. package/dist/operations/generators/compile.ts +78 -39
  214. package/dist/operations/generators/permits.js +11 -12
  215. package/dist/operations/generators/permits.ts +3 -4
  216. package/dist/operations/generators/seed.d.ts +1 -0
  217. package/dist/operations/generators/seed.js +19 -17
  218. package/dist/operations/generators/seed.ts +22 -14
  219. package/dist/operations/generators/trial.d.ts +6 -8
  220. package/dist/operations/generators/trial.js +200 -70
  221. package/dist/operations/generators/trial.ts +230 -93
  222. package/dist/operations/nodes.d.ts +15 -16
  223. package/dist/operations/nodes.ts +16 -17
  224. package/dist/operations/operations.d.ts +40 -4
  225. package/dist/operations/operations.js +1 -0
  226. package/dist/operations/operations.ts +61 -4
  227. package/dist/operations/result.d.ts +2 -2
  228. package/dist/operations/result.ts +2 -2
  229. package/package.json +1 -4
  230. package/dist/core/answers.d.ts +0 -171
  231. package/dist/core/answers.js +0 -789
  232. package/dist/core/answers.ts +0 -999
  233. package/dist/core/client.d.ts +0 -63
  234. package/dist/core/client.js +0 -249
  235. package/dist/core/client.ts +0 -289
  236. package/dist/core/files.js +0 -171
  237. package/dist/core/files.ts +0 -222
  238. package/dist/integrations/module.d.ts +0 -6
  239. package/dist/integrations/module.js +0 -153
  240. package/dist/integrations/module.ts +0 -191
  241. package/dist/integrity/slopnators/sl_unkept_rows.js +0 -104
  242. package/dist/integrity/slopnators/sl_unkept_rows.ts +0 -111
  243. package/dist/integrity/slopnators/sl_unseamed_reach.js +0 -73
  244. package/dist/integrity/slopnators/sl_unseamed_reach.ts +0 -79
  245. package/dist/operations/cost.d.ts +0 -14
  246. package/dist/operations/cost.js +0 -37
  247. package/dist/operations/cost.ts +0 -58
  248. package/dist/operations/errors/errors.d.ts +0 -52
  249. package/dist/operations/http.js +0 -124
  250. package/dist/operations/http.ts +0 -169
  251. package/dist/operations/memory.d.ts +0 -104
  252. package/dist/operations/memory.js +0 -365
  253. package/dist/operations/memory.ts +0 -470
  254. package/dist/operations/metering.d.ts +0 -55
  255. package/dist/operations/metering.js +0 -247
  256. package/dist/operations/metering.ts +0 -328
  257. package/dist/operations/queue-policy.d.ts +0 -3
  258. package/dist/operations/queue-policy.js +0 -8
  259. package/dist/operations/queue-policy.ts +0 -10
  260. package/dist/operations/seams.d.ts +0 -239
  261. package/dist/operations/seams.js +0 -45
  262. package/dist/operations/seams.ts +0 -364
  263. package/dist/operations/store.d.ts +0 -14
  264. package/dist/operations/store.js +0 -1
  265. package/dist/operations/store.ts +0 -29
  266. /package/dist/{operations → core}/errors/classes.d.ts +0 -0
  267. /package/dist/{operations → core}/errors/classes.js +0 -0
  268. /package/dist/{operations → core}/errors/classes.ts +0 -0
  269. /package/dist/integrity/slopnators/{sl_unkept_rows.d.ts → sl_door_in_a_node.d.ts} +0 -0
  270. /package/dist/integrity/slopnators/{sl_unseamed_reach.d.ts → sl_outside_an_operation.d.ts} +0 -0
@@ -1,10 +1,9 @@
1
1
  import { join } from 'node:path'
2
2
  import { argv, stderr, stdout } from 'node:process'
3
3
 
4
- import { marked, wordsIn } from '../../core/answers.ts'
5
- import { existsSync, readFileSync, writeFileSync } from '../../core/files.ts'
4
+ import { existsSync, readFileSync, writeFileSync } from '../../integrations/disk.ts'
6
5
  import { exit, invoked } from '../../core/invoked.ts'
7
- import { valuesIn } from '../../core/rows.ts'
6
+ import { marked, valuesIn } from '../../core/rows.ts'
8
7
  import { ours } from '../../core/scope.ts'
9
8
  import { findTree, inTree } from '../../core/tree.ts'
10
9
 
@@ -17,8 +16,12 @@ const MANIFEST = 'package.json'
17
16
  interface Row {
18
17
  readonly id?: string
19
18
  readonly at?: string
19
+ readonly derives?: readonly Row[]
20
20
  }
21
21
 
22
+ const addressed = (rows: readonly Row[]): readonly string[] =>
23
+ rows.flatMap((one) => [...(one.at === undefined ? [] : [one.at]), ...addressed(one.derives ?? [])])
24
+
22
25
  const mark = (one: Row): string => marked([one.id ?? '', one.at ?? ''])
23
26
 
24
27
  function releases(): readonly Row[] {
@@ -32,46 +35,44 @@ function releases(): readonly Row[] {
32
35
  }
33
36
  }
34
37
 
35
- function named(): readonly string[] {
36
- const at = inTree(TREE, 'secrets.ts')
37
- if (!existsSync(at)) return []
38
+ function manager(): string {
38
39
  try {
39
- const held = valuesIn(readFileSync(at, 'utf8')) as { SECRETS?: readonly { readonly id: string }[] }
40
- return (held.SECRETS ?? []).map((one) => one.id.toUpperCase()).sort()
40
+ const held = JSON.parse(readFileSync(join(TREE.at, MANIFEST), 'utf8')) as { packageManager?: unknown }
41
+ const said = typeof held.packageManager === 'string' ? held.packageManager.split('@')[0] : undefined
42
+ return said === undefined || said === '' ? 'npm' : said
41
43
  } catch {
42
- return []
44
+ return 'npm'
43
45
  }
44
46
  }
45
47
 
46
48
  function scripted(): { readonly build: string; readonly start: string } {
47
- try {
48
- const held = JSON.parse(readFileSync(join(TREE.at, MANIFEST), 'utf8')) as {
49
- scripts?: Readonly<Record<string, string>>
50
- }
51
- const scripts = held.scripts ?? {}
52
- return {
53
- build: scripts['build'] === undefined ? 'npm run build' : 'npm run build',
54
- start: scripts['start'] === undefined ? 'npm start' : 'npm start',
55
- }
56
- } catch {
57
- return { build: 'npm run build', start: 'npm start' }
58
- }
49
+ const tool = manager()
50
+ return { build: `${tool} run build`, start: tool === 'npm' ? 'npm start' : `${tool} start` }
59
51
  }
60
52
 
61
53
  function dockerfile(build: string, start: string): string {
54
+ const tool = manager()
55
+ const corepack = tool === 'npm' ? [] : ['RUN corepack enable']
56
+ const lock = tool === 'npm' ? 'package-lock.json*' : `${tool}-lock.yaml*`
57
+ const first =
58
+ tool === 'npm'
59
+ ? 'RUN npm ci --omit=dev --ignore-scripts || npm install --omit=dev --ignore-scripts'
60
+ : `RUN ${tool} install --prod --frozen-lockfile --ignore-scripts || ${tool} install --prod --ignore-scripts`
61
+ const all = tool === 'npm' ? 'RUN npm install --ignore-scripts' : `RUN ${tool} install --ignore-scripts`
62
+
62
63
  return [
63
- '# GENERATED by thread — do not edit. It is derived from what this app declares.',
64
- '',
65
64
  'FROM node:22-slim AS build',
66
65
  'WORKDIR /app',
67
- 'COPY package.json package-lock.json* ./',
68
- 'RUN npm ci --omit=dev --ignore-scripts || npm install --omit=dev --ignore-scripts',
66
+ ...corepack,
67
+ `COPY package.json ${lock} ./`,
68
+ first,
69
69
  'COPY . .',
70
- 'RUN npm install --ignore-scripts',
70
+ all,
71
71
  `RUN ${build}`,
72
72
  '',
73
73
  'FROM node:22-slim',
74
74
  'WORKDIR /app',
75
+ ...corepack,
75
76
  'ENV NODE_ENV=production',
76
77
  'COPY --from=build /app ./',
77
78
  `EXPOSE ${String(LISTENS)}`,
@@ -80,22 +81,22 @@ function dockerfile(build: string, start: string): string {
80
81
  ].join('\n')
81
82
  }
82
83
 
83
- function compose(id: string, secrets: readonly string[]): string {
84
+ function compose(id: string): string {
84
85
  return [
85
- '# GENERATED by thread — do not edit. It is derived from what this app declares.',
86
- '',
87
86
  'services:',
88
87
  ` ${id}:`,
89
88
  ' build: .',
90
89
  ' ports:',
91
90
  ` - "${String(LISTENS)}:${String(LISTENS)}"`,
92
- ...(secrets.length === 0
93
- ? []
94
- : [
95
- ' environment:',
91
+ '',
92
+ ].join('\n')
93
+ }
96
94
 
97
- ...secrets.map((one) => ` ${one}: \${${one}}`),
98
- ]),
95
+ function withheld(): string {
96
+ return [
97
+ '.thread',
98
+ 'node_modules',
99
+ '.git',
99
100
  '',
100
101
  ].join('\n')
101
102
  }
@@ -103,7 +104,6 @@ function compose(id: string, secrets: readonly string[]): string {
103
104
  function made(): ReadonlyMap<string, string> {
104
105
  const found = new Map<string, string>()
105
106
  const rows = releases()
106
- const secrets = named()
107
107
  const { build, start } = scripted()
108
108
 
109
109
  for (const one of rows) {
@@ -113,7 +113,8 @@ function made(): ReadonlyMap<string, string> {
113
113
  if (kind === undefined) continue
114
114
  if (kind === 'docker') {
115
115
  found.set('Dockerfile', dockerfile(build, start))
116
- found.set('compose.yaml', compose(id, secrets))
116
+ found.set('compose.yaml', compose(id))
117
+ found.set('.dockerignore', withheld())
117
118
  }
118
119
  }
119
120
  return found
@@ -138,7 +139,14 @@ function run(): number {
138
139
  return 0
139
140
  }
140
141
 
141
- const words = wordsIn(['read'])
142
+ const words = argv.slice(2).filter((one) => !one.startsWith('-'))
143
+ if (words[0] !== undefined && words[0] !== 'read') {
144
+ stderr.write(
145
+ 'release: `' + words[0] + '` is not something this command does. `thread release` writes the release, and ' +
146
+ '`thread release read <mark>` prints one. Nothing was written.\n',
147
+ )
148
+ return 1
149
+ }
142
150
  if (words[0] === 'read') {
143
151
  const which = words[1]
144
152
  const one = rows.find((row) => mark(row) === which)
@@ -153,10 +161,10 @@ function run(): number {
153
161
 
154
162
  const kind = kindOf(one.id ?? '')
155
163
  stdout.write(`${String(one.id)} — ${kind === undefined ? 'a release of a kind this generator does not recognise' : SAID[kind]}\n`)
156
- for (const file of kind === undefined ? [] : WRITES[kind]) {
157
- stdout.write(`\n${file}\n`)
164
+ for (const one of kind === undefined ? [] : WRITES[kind]) {
165
+ stdout.write(`\n${one.at} — ${one.describes}\n`)
158
166
  try {
159
- stdout.write(readFileSync(join(TREE.at, file), 'utf8'))
167
+ stdout.write(readFileSync(join(TREE.at, one.at), 'utf8'))
160
168
  } catch {
161
169
  stdout.write('(nothing is there)\n')
162
170
  }
@@ -167,6 +175,19 @@ function run(): number {
167
175
  const files = made()
168
176
  const checking = argv.includes('--check')
169
177
 
178
+ const stated = new Set(addressed(rows))
179
+ const stray = [...files.keys()].filter((one) => !stated.has(one))
180
+ if (stray.length > 0) {
181
+ stderr.write(
182
+ `release: this release produces ${stray.join(', ')}, and no row names ${stray.length === 1 ? 'it' : 'them'}. ` +
183
+ `Writing ${stray.length === 1 ? 'it' : 'them'} would leave a file this tree does not declare, and the descent ` +
184
+ 'reports that three steps later as a file nobody can account for. Take the release out with `thread remove ' +
185
+ '<its id>` and declare it again — the maker writes one row per file this kind produces, so all of them arrive ' +
186
+ 'together. Nothing was written.\n',
187
+ )
188
+ return 1
189
+ }
190
+
170
191
  const shipped = installed()
171
192
  if (shipped.length > 0) {
172
193
  stderr.write(
@@ -202,8 +223,10 @@ function run(): number {
202
223
 
203
224
  for (const [at, text] of files) writeFileSync(join(TREE.at, at), text, 'utf8')
204
225
  stdout.write(
205
- `release: wrote ${[...files.keys()].join(', ')} — built and started by what this app's own manifest ` +
206
- `declares, listening on ${String(LISTENS)}, naming ${String(named().length)} secret(s) and holding none of them.\n`,
226
+ 'release: wrote ' + [...files.keys()].join(', ') + ' — built and started by what this app\'s own manifest ' +
227
+ 'declares, listening on ' + String(LISTENS) + '.\n' +
228
+ 'release: no secret is named there. A secret is read from the sealed store at the moment it is used, and ' +
229
+ 'naming it here would be telling whoever deploys this to put a credential in the environment instead.\n',
207
230
  )
208
231
  return 0
209
232
  }
@@ -1,8 +1,12 @@
1
1
  export declare const PREFIXES: {
2
- readonly docker: "dk_";
2
+ readonly docker: 'dk_';
3
3
  };
4
- export declare const KINDS: readonly ["docker"];
5
- export declare const WRITES: Readonly<Record<(typeof KINDS)[number], readonly string[]>>;
4
+ export declare const KINDS: readonly ['docker'];
5
+ export interface Written {
6
+ readonly at: string;
7
+ readonly describes: string;
8
+ }
9
+ export declare const WRITES: Readonly<Record<(typeof KINDS)[number], readonly Written[]>>;
6
10
  export declare const LISTENS = 3000;
7
11
  export declare const SAID: Readonly<Record<(typeof KINDS)[number], string>>;
8
12
  export declare const kindOf: (id: string) => (typeof KINDS)[number] | undefined;
@@ -1,7 +1,20 @@
1
1
  export const PREFIXES = { docker: 'dk_' };
2
2
  export const KINDS = ['docker'];
3
3
  export const WRITES = {
4
- docker: ['Dockerfile', 'compose.yaml'],
4
+ docker: [
5
+ {
6
+ at: 'Dockerfile',
7
+ describes: 'How this app becomes an image and what starts it inside one — the build and start commands its own manifest declares, run with the package manager that manifest names, and the port its server listens on.',
8
+ },
9
+ {
10
+ at: 'compose.yaml',
11
+ describes: 'What runs the image this release builds, named beside it because one row states one address.',
12
+ },
13
+ {
14
+ at: '.dockerignore',
15
+ describes: 'What a build of this image must never carry out of this directory: the sealed store and the key that opens it, the installed packages, and the history. Without it the build copies everything beside it, and every sealed value goes into the image and into whoever pulls it.',
16
+ },
17
+ ],
5
18
  };
6
19
  export const LISTENS = 3000;
7
20
  export const SAID = {
@@ -4,8 +4,28 @@ export const PREFIXES = { docker: 'dk_' } as const
4
4
 
5
5
  export const KINDS = ['docker'] as const
6
6
 
7
- export const WRITES: Readonly<Record<(typeof KINDS)[number], readonly string[]>> = {
8
- docker: ['Dockerfile', 'compose.yaml'],
7
+ export interface Written {
8
+ readonly at: string
9
+ readonly describes: string
10
+ }
11
+
12
+ export const WRITES: Readonly<Record<(typeof KINDS)[number], readonly Written[]>> = {
13
+ docker: [
14
+ {
15
+ at: 'Dockerfile',
16
+ describes:
17
+ 'How this app becomes an image and what starts it inside one — the build and start commands its own manifest declares, run with the package manager that manifest names, and the port its server listens on.',
18
+ },
19
+ {
20
+ at: 'compose.yaml',
21
+ describes: 'What runs the image this release builds, named beside it because one row states one address.',
22
+ },
23
+ {
24
+ at: '.dockerignore',
25
+ describes:
26
+ 'What a build of this image must never carry out of this directory: the sealed store and the key that opens it, the installed packages, and the history. Without it the build copies everything beside it, and every sealed value goes into the image and into whoever pulls it.',
27
+ },
28
+ ],
9
29
  }
10
30
 
11
31
  export const LISTENS = 3000
@@ -2,5 +2,9 @@ import type { Attaching } from '../core/attach.ts';
2
2
  export declare const CONCEPTS: readonly {
3
3
  readonly id: string;
4
4
  readonly describes: string;
5
+ readonly cites: readonly {
6
+ readonly on: string;
7
+ readonly said: string;
8
+ }[];
5
9
  }[];
6
10
  export declare const ATTACH: Attaching;
@@ -1,18 +1,35 @@
1
- import { named } from "../core/scope.js";
1
+ import { SHAPED } from '../core/init.js';
2
+ import { NAMED } from '../core/scope.js';
2
3
  const VERSION = 1;
3
- const DESCRIBES = 'The system that states every place this app reaches and every credential it reaches with. A target is a destination and the prefix on its id is how it is reached; what it accepts is entities if it keeps them, calls if it is called over https, and the methods or subcommands it answers to if it is an npm package or a program; what it requires is secrets, and where the reach has an address one of them is it. Nothing else in this app names a host, a connection string or an environment variable, so an operation says which place it needs and the rest is worked out from the declaration.';
4
- const TARGETS = 'Where this app states the places it reaches and what each one accepts. Declare one with `thread create target`, which asks how it is reached and puts the matching prefix on its id — `pg_` for a postgres over the network, `pl_` for one in this process, `zu_` for a store in the browser, `ht_` for something called over https, `mod_` for something this app brings in from outside the repository and calls, `cli_` for a program it runs at a command line — and writes what it accepts and the secrets reaching it needs. A place that keeps things accepts entities; a place that is called accepts the calls declared beside it; a package or a program says in `uses` what it is and in `offers` what it responds to, and takes nothing, because a method name belongs to something outside this tree and `takes` is a demand this tree has to be able to meet. This is the one place the split between what reaches a server and what stays local is written down.';
5
- const SECRETS = 'Where this app states every value it must be given and may not hold. A row here is a name and a sentence and never a value: `sec_` for one only a server may ever hold, `pub_` for one that may reach a browser, and that prefix is the whole of what decides where the target requiring it can be reached from. A secret with a `sends` goes into a header on every call; the one without is the address of the target that requires it.';
4
+ const DESCRIBES = 'The system that states every place this app reaches and every credential it reaches with. A target is a destination and the prefix on its id is how it is reached; what it accepts is entities if it keeps them, and calls if it is called: an endpoint over https, a subcommand of a program, a method of a package; a disk accepts nothing, because every disk answers the same orders; what it requires is secrets, and the target it is reached through when the place can only be got at by way of another one; where it is goes on the row as its address when anybody may read it, and is one of the secrets when it carries a password. Nothing else in this app names a host, a connection string or an environment variable, so an operation says which place it needs and the rest is worked out from the declaration.';
5
+ const TARGETS = 'Where this app states everything outside itself that it deals with, and every way in. An integration is one outside thing — a vendor, a service, a program on the machine — and nothing is reached that is not part of one; declare it with `thread create integration`. A target is one way out of this app into an integration: `thread create target` prints every way a place can be reached, asks which, and puts that prefix on the id, so how it is reached is the prefix and no field says so. A call is one thing a target can be asked to do, declared beside it with `thread create target call`. A webhook is the way back in: `thread create webhook` asks which integration it sits under, the path something outside posts to, and the operation it invokes, and that operation names the webhook back, so neither half opens a door on its own. Nobody writes the file that answers that address, because `thread capabilities` derives it from those two rows. What a target accepts goes in `takes` — entities where it keeps things, calls where it is called, and nothing for a disk. What it needs goes in `requires`: its credentials, and the target it is reached through; where it is goes in address, and a header every call carries goes in headers. This is the one place the split between what reaches a server and what stays local is written down.';
6
+ const SECRETS = 'Where this app states every value it must be given and may not hold. A row here is a name and a sentence and never a value, and every id wears sec_, because only a server may ever hold a secret: a value anybody may read is not one, and goes on the row that needs it instead, as the address of a target or a header it sends. A secret with a sends goes into a header on every call, one with a signs is the key a body is signed with, and one with neither is the connection string of a database, the one address that carries a password.';
6
7
  const VAULT = 'What opens a secret\'s value at the moment of use, written once and then yours. It reads the names off _threads/secrets.ts and opens the values sealed beside the tree, refusing any other name; add a secret with `thread create secret`, which is the only thing that ever takes a value. To open values from somewhere else in production, replace what opens them here.';
7
- const SECRET = 'A value this app must be given and may never hold — a key, a token, a connection string. The row names it and says what it is for, and there is no field on it for the value, because a row is read by everything and a credential is read by one thing at one moment. The prefix says who may ever hold it: `sec_` a server and nothing else, `pub_` anybody including whoever opens the page. That one letter is what refuses a server credential its way into a browser bundle, so it is on the id rather than in a field somebody could forget.';
8
- const CALL = 'One thing a target can be asked to do — the verb and the path, named so that a node names the call and never writes a url. It is a row rather than a field because it is a thing with a name and a sentence like everything else here, and because what a vendor offers is a list that grows: an app reaches one more endpoint by declaring one more of these, not by editing a body.';
9
- const REACH = 'How a target is got at — over a network to a postgres, inside this process, in the browser\'s own memory, over https, as an npm package this app installs and calls directly, or as a program run at a command line. It is the prefix on the target\'s id and never a field, because how a thing is reached decides everything else that may be said about it: what it accepts, whether it can be reached from a page at all, whether anything has to say where it is, and whether anything has to arrive on the machine before it can be reached at all. A field beside the id would be a second answer to a question the id already answers.';
8
+ const SECRET = 'A value this app must be given and may never hold: a key, a token, a connection string. The row names it and says what it is for, and there is no field on it for the value, because a row is read by everything and a credential is read by one thing at one moment. Every id wears sec_, and that prefix says only a server may ever hold it: everything a browser bundle reaches is in the bundle, so a target requiring a secret is refused a browser entry when it is built. A value anybody may read is not a secret and is never sealed; it is written on the row that needs it, as the address of a target or a header every call carries.';
9
+ const CALL = 'One thing a target can be asked to do, named so that a node names the call and never writes a url, a subcommand or a method. Over https it is a verb and a path; on a program it is a subcommand; on a package it is a method. It is a row rather than a field because it is a thing with a name and a sentence like everything else here, and because what a place offers is a list that grows: an app reaches one more endpoint, subcommand or method by declaring one more of these, not by editing a body.';
10
+ const REACH = 'How a target is got at — over a network to a postgres, inside this process, in the browser\'s own memory, over https, as a package this app calls in its own process, as a program run at a command line, or as the disk under it. It is the prefix on the target\'s id and never a field, because how a thing is reached decides everything else that may be said about it: what it accepts, whether it can be reached from a page at all, and whether anything has to say where it is. A field beside the id would be a second answer to a question the id already answers.';
11
+ const INTEGRATION = 'Something outside this app that it reaches into: a vendor, a service, a program on the machine. Every target says which integration it is part of, so supabase is one thing made of its postgres, its https endpoint, the calls on each and the secrets they need, rather than five rows that happen to share a word. Declare it before the first target that reaches into it.';
12
+ const WEBHOOK = 'A way in that something outside uses: it posts to one address this app declares, and that address is the whole of what anybody out there is given. It sits under an integration beside the targets that reach the other way, and it names the operation it runs and the secret its signature is checked against, so what arrives is refused or dispatched and never anything else. Declare one with `thread create webhook`.';
13
+ const INVOKES = 'Which operation a webhook runs when something posts to it. That operation names the webhook back, so both halves agree before anything outside reaches it, and a pairing only one of them states is refused.';
14
+ const ARRIVES = 'Which header a value comes in on. A webhook says which one carries its signature, and an entity that names its callers says which one carries their token — one word, because it answers the same question about a request.';
15
+ const SIGNS = 'What a secret does to a body on its way out: its value is the key a signature is computed with, and the digest goes on the header named here. It is `sends` turned around — `sends` puts the value itself on a header, and this puts the value nowhere at all.';
16
+ const ADDRESS = 'Where a target reached over https is: the one absolute url every call on it is written from, stated on the row because a place is a fact anybody may read. Only the address of a database is sealed instead, because a connection string carries a password.';
17
+ const HEADERS = 'The headers every call on a target carries, stated on its row as name and value: the version of an API this app is written against, or a fixed way of asking. A credential never goes here, because anybody may read a row; a value that has to be withheld is a secret with a sends.';
18
+ const SSOT = { on: '2026-08-29', said: 'SSOT for the integrations, as a whole meta inside thread core' };
19
+ const RULED = { on: '2026-09-22', said: 'remove pub then as an acceptable thing, that\'s definitely slop and it\'s not the first time such a fucking thing has surfaced. Delete and redo it properly.' };
10
20
  export const CONCEPTS = [
11
- { id: 'secret', describes: SECRET },
12
- { id: 'call', describes: CALL },
13
- { id: 'reach', describes: REACH },
21
+ { id: 'secret', describes: SECRET, cites: [SSOT] },
22
+ { id: 'webhook', describes: WEBHOOK, cites: [SSOT] },
23
+ { id: 'invokes', describes: INVOKES, cites: [SSOT] },
24
+ { id: 'arrives', describes: ARRIVES, cites: [SSOT] },
25
+ { id: 'signs', describes: SIGNS, cites: [SSOT] },
26
+ { id: 'address', describes: ADDRESS, cites: [RULED] },
27
+ { id: 'headers', describes: HEADERS, cites: [RULED] },
28
+ { id: 'call', describes: CALL, cites: [SSOT] },
29
+ { id: 'reach', describes: REACH, cites: [SSOT] },
30
+ { id: 'integration', describes: INTEGRATION, cites: [SSOT] },
14
31
  ];
15
- import { quoted } from "../core/rows.js";
32
+ import { quoted } from '../core/rows.js';
16
33
  const concept = (one) => [
17
34
  ' {',
18
35
  ` id: '${one.id}',`,
@@ -20,12 +37,11 @@ const concept = (one) => [
20
37
  ` describes: ${quoted(one.describes)},`,
21
38
  ` version: ${String(VERSION)},`,
22
39
  ' cites: [',
23
- " { on: '2026-08-29', said: 'SSOT for the integrations, as a whole meta inside thread core' },",
24
- " { on: '2026-08-29', said: 'seams declare BOTH target AND secrets' },",
40
+ ...one.cites.map((each) => " { on: '" + each.on + "', said: " + quoted(each.said) + ' },'),
25
41
  ' ],',
26
42
  ' },',
27
43
  ].join('\n');
28
- const INTEGRATIONS_SOURCE = `import type { Threaded } from '../core/thread.ts'
44
+ const INTEGRATIONS_SOURCE = `import type { Threaded } from '${SHAPED}'
29
45
 
30
46
  export const INTEGRATIONS: Threaded = {
31
47
  id: 'integrations',
@@ -48,13 +64,17 @@ export const INTEGRATIONS: Threaded = {
48
64
  ],
49
65
  }
50
66
  `;
51
- const TARGETS_SOURCE = `import type { Call, Target } from './target.ts'
67
+ const TARGETS_SOURCE = `import type { Call, Integration, Target, Webhook } from '${NAMED}/integrations/target'
68
+
69
+ export const INTEGRATIONS: readonly Integration[] = []
52
70
 
53
71
  export const TARGETS: readonly Target[] = []
54
72
 
55
73
  export const CALLS: readonly Call[] = []
74
+
75
+ export const WEBHOOKS: readonly Webhook[] = []
56
76
  `;
57
- const SECRETS_SOURCE = `import type { Secret } from './secret.ts'
77
+ const SECRETS_SOURCE = `import type { Secret } from '${NAMED}/integrations/secret'
58
78
 
59
79
  export const SECRETS: readonly Secret[] = []
60
80
  `;
@@ -88,7 +108,7 @@ const SEEDED = {
88
108
  ' {',
89
109
  ' id: \'integrations\',',
90
110
  ' at: \'_threads/integrations.ts\',',
91
- ' describes: \'The system that states every place this app reaches and every credential it reaches with. A target is a destination and the prefix on its id is how it is reached; what it accepts is entities if it keeps them, calls if it is called over https, and the methods or subcommands it answers to if it is an npm package or a program; what it requires is secrets, and where the reach has an address one of them is it. Nothing else in this app names a host, a connection string or an environment variable, so an operation says which place it needs and the rest is worked out from the declaration.\',',
111
+ ' describes: \'The system that states every place this app reaches and every credential it reaches with. A target is a destination and the prefix on its id is how it is reached; what it accepts is entities if it keeps them, and calls if it is called: an endpoint over https, a subcommand of a program, a method of a package; a disk accepts nothing, because every disk answers the same orders; what it requires is secrets, and the target it is reached through when the place can only be got at by way of another one; where it is goes on the row as its address when anybody may read it, and is one of the secrets when it carries a password. Nothing else in this app names a host, a connection string or an environment variable, so an operation says which place it needs and the rest is worked out from the declaration.\',',
92
112
  ' version: 1,',
93
113
  ' },',
94
114
  ].join('\n'),
@@ -117,43 +137,4 @@ export const ATTACH = {
117
137
  { file: 'secrets.ts', source: SECRETS_SOURCE },
118
138
  ],
119
139
  rows: [...OWN, ...SHIPPED],
120
- retires: [
121
- {
122
- into: 'derivation.ts',
123
- list: 'GENERATORS',
124
- id: 'read-generator',
125
- why: 'renamed to `inspect-generator`. `thread read` is a verb of the CLI itself now — it reads FILES, and this package reads ROWS, which is `thread inspect`. Both were called read, and only one of them could answer.',
126
- },
127
- {
128
- into: 'integrations.ts',
129
- list: 'declares',
130
- id: 'trace',
131
- made: `src/integrations.trace.${'jsonl'}`,
132
- why: 'the record of what went out through an integration is gone. It sealed every line against the one before it, and a chain nobody reads guards nothing. What an integration did is what the tool itself reports, at the moment it runs',
133
- },
134
- {
135
- into: 'derivation.ts',
136
- list: 'GENERATORS',
137
- id: 'trace-generator',
138
- why: 'the record it wrote is gone, so there is nothing for it to make and nothing for `--check` to read',
139
- },
140
- {
141
- into: 'derivation.ts',
142
- list: 'GENERATORS',
143
- id: 'sdk-generator',
144
- why: '`sdk` is a way a target is reached now, not a noun beside one — `thread create target` declares an SDK with ' +
145
- '`--reach sdk`, which installs it, reads what it answers to, and writes one row instead of three. The noun ' +
146
- 'also raised the collision guard against any app declaring a meta called `sdk`, which made every other ' +
147
- 'withdrawal this package ships undeliverable to the trees that needed them.',
148
- },
149
- {
150
- into: 'concepts.ts',
151
- list: 'CONCEPTS',
152
- id: 'sdk',
153
- why: '`sdk` is no longer a noun beside a target — it is one of the ways a target is reached, and its meaning is ' +
154
- `now \`SAID.module\` in \`${named('integrations')}/target\`, beside \`https\` and every other reach, none of which is ` +
155
- 'coined here either. It also collided with an app declaring a meta called `sdk`, which coins the word: two ' +
156
- 'meanings for one word is what `twice-meant` refuses, and it was holding the glossary and the docs red.',
157
- },
158
- ],
159
140
  };