@prestack/thread 0.1.3 → 0.1.4

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 (265) hide show
  1. package/CHANGELOG.md +105 -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 +37 -65
  7. package/dist/core/attach.ts +32 -76
  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 +89 -21
  14. package/dist/core/descend.ts +86 -17
  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 +0 -1
  27. package/dist/core/init.js +48 -4
  28. package/dist/core/init.ts +54 -4
  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/thread.d.ts +3 -3
  38. package/dist/core/tree.d.ts +4 -1
  39. package/dist/core/tree.js +26 -6
  40. package/dist/core/tree.ts +29 -6
  41. package/dist/core/types.d.ts +3 -3
  42. package/dist/core/types.js +3 -1
  43. package/dist/core/types.ts +2 -1
  44. package/dist/derivation/attach.js +1 -1
  45. package/dist/derivation/generators/build.js +6 -6
  46. package/dist/derivation/generators/build.ts +1 -1
  47. package/dist/derivation/generators/check.js +1 -1
  48. package/dist/derivation/generators/determinism.js +1 -1
  49. package/dist/derivation/generators/release.js +60 -47
  50. package/dist/derivation/generators/release.ts +66 -43
  51. package/dist/derivation/release.d.ts +7 -3
  52. package/dist/derivation/release.js +14 -1
  53. package/dist/derivation/release.ts +22 -2
  54. package/dist/integrations/attach.d.ts +4 -0
  55. package/dist/integrations/attach.js +33 -54
  56. package/dist/integrations/attach.ts +50 -57
  57. package/dist/integrations/cli.d.ts +37 -0
  58. package/dist/integrations/cli.js +440 -0
  59. package/dist/integrations/cli.ts +543 -0
  60. package/dist/{core/files.d.ts → integrations/disk.d.ts} +16 -24
  61. package/dist/integrations/disk.js +435 -0
  62. package/dist/integrations/disk.ts +465 -0
  63. package/dist/integrations/generators/read.js +6 -6
  64. package/dist/integrations/generators/read.ts +4 -4
  65. package/dist/integrations/generators/vault.js +10 -11
  66. package/dist/integrations/generators/vault.ts +4 -5
  67. package/dist/{operations/http.d.ts → integrations/https.d.ts} +10 -6
  68. package/dist/integrations/https.js +170 -0
  69. package/dist/integrations/https.ts +216 -0
  70. package/dist/integrations/pglite.d.ts +26 -0
  71. package/dist/integrations/pglite.js +143 -0
  72. package/dist/integrations/pglite.ts +206 -0
  73. package/dist/integrations/secret.d.ts +8 -2
  74. package/dist/integrations/secret.js +3 -5
  75. package/dist/integrations/secret.ts +10 -6
  76. package/dist/integrations/target.d.ts +155 -14
  77. package/dist/integrations/target.js +171 -50
  78. package/dist/integrations/target.ts +377 -61
  79. package/dist/integrations/vault.d.ts +1 -0
  80. package/dist/integrations/vault.js +20 -5
  81. package/dist/integrations/vault.ts +17 -5
  82. package/dist/integrity/attach.js +3 -73
  83. package/dist/integrity/attach.ts +1 -71
  84. package/dist/integrity/check-threads.js +5 -5
  85. package/dist/integrity/check-threads.ts +2 -2
  86. package/dist/integrity/constraint.d.ts +0 -1
  87. package/dist/integrity/constraint.js +267 -366
  88. package/dist/integrity/constraint.ts +267 -375
  89. package/dist/integrity/generators/compiler.js +14 -17
  90. package/dist/integrity/generators/compiler.ts +10 -15
  91. package/dist/integrity/generators/gate.js +15 -15
  92. package/dist/integrity/generators/gate.ts +10 -10
  93. package/dist/integrity/generators/issues.js +7 -7
  94. package/dist/integrity/generators/publish.js +39 -57
  95. package/dist/integrity/generators/publish.ts +44 -55
  96. package/dist/integrity/generators/trial.d.ts +3 -3
  97. package/dist/integrity/generators/trial.js +133 -165
  98. package/dist/integrity/generators/trial.ts +145 -169
  99. package/dist/integrity/lint.js +9 -13
  100. package/dist/integrity/lint.ts +7 -10
  101. package/dist/integrity/lsp/bin.js +5 -4
  102. package/dist/integrity/lsp/bin.ts +3 -1
  103. package/dist/integrity/lsp/diagnostics.js +3 -3
  104. package/dist/integrity/lsp/diagnostics.ts +1 -1
  105. package/dist/integrity/lsp/lint.js +19 -30
  106. package/dist/integrity/lsp/lint.ts +16 -32
  107. package/dist/integrity/lsp/server.js +3 -3
  108. package/dist/integrity/slopnator.d.ts +3 -13
  109. package/dist/integrity/slopnator.js +58 -37
  110. package/dist/integrity/slopnator.ts +53 -37
  111. package/dist/integrity/slopnators/sl_broken_lineage.js +2 -2
  112. package/dist/integrity/slopnators/sl_declared_elsewhere.js +1 -1
  113. package/dist/integrity/slopnators/sl_derived_drift.js +4 -4
  114. package/dist/integrity/slopnators/sl_derived_drift.ts +1 -1
  115. package/dist/integrity/slopnators/sl_door_in_a_node.js +220 -0
  116. package/dist/integrity/slopnators/sl_door_in_a_node.ts +250 -0
  117. package/dist/integrity/slopnators/sl_inert_island.js +10 -8
  118. package/dist/integrity/slopnators/sl_inert_island.ts +8 -6
  119. package/dist/integrity/slopnators/sl_missing_row.js +6 -51
  120. package/dist/integrity/slopnators/sl_missing_row.ts +4 -50
  121. package/dist/integrity/slopnators/sl_no_barrel.js +1 -1
  122. package/dist/integrity/slopnators/sl_no_child_process.js +36 -18
  123. package/dist/integrity/slopnators/sl_no_child_process.ts +44 -18
  124. package/dist/integrity/slopnators/sl_no_dispatch_bypass.js +80 -149
  125. package/dist/integrity/slopnators/sl_no_dispatch_bypass.ts +95 -147
  126. package/dist/integrity/slopnators/sl_no_import_cycle.js +15 -4
  127. package/dist/integrity/slopnators/sl_no_import_cycle.ts +20 -4
  128. package/dist/integrity/slopnators/sl_no_such_command.js +2 -2
  129. package/dist/integrity/slopnators/sl_no_such_command.ts +1 -1
  130. package/dist/integrity/slopnators/sl_outside_an_operation.js +20 -0
  131. package/dist/integrity/slopnators/sl_outside_an_operation.ts +25 -0
  132. package/dist/integrity/slopnators/sl_screen_holds_only_declared.d.ts +2 -0
  133. package/dist/integrity/slopnators/sl_screen_holds_only_declared.js +61 -0
  134. package/dist/integrity/slopnators/sl_screen_holds_only_declared.ts +70 -0
  135. package/dist/integrity/slopnators/sl_secret_literal.js +1 -33
  136. package/dist/integrity/slopnators/sl_secret_literal.ts +1 -33
  137. package/dist/integrity/slopnators/sl_stale_path.js +5 -22
  138. package/dist/integrity/slopnators/sl_stale_path.ts +4 -20
  139. package/dist/integrity/slopnators/sl_twice_meant.js +1 -1
  140. package/dist/integrity/slopnators/sl_twice_parented.js +29 -5
  141. package/dist/integrity/slopnators/sl_twice_parented.ts +28 -4
  142. package/dist/integrity/slopnators/sl_undeclared_crossing.d.ts +4 -1
  143. package/dist/integrity/slopnators/sl_undeclared_crossing.js +426 -62
  144. package/dist/integrity/slopnators/sl_undeclared_crossing.ts +454 -70
  145. package/dist/integrity/slopnators/sl_undeclared_destination.js +59 -25
  146. package/dist/integrity/slopnators/sl_undeclared_destination.ts +69 -21
  147. package/dist/integrity/slopnators/sl_undeclared_list.d.ts +2 -0
  148. package/dist/integrity/slopnators/sl_undeclared_list.js +363 -0
  149. package/dist/integrity/slopnators/sl_undeclared_list.ts +358 -0
  150. package/dist/integrity/slopnators/sl_undeclared_node.js +1 -11
  151. package/dist/integrity/slopnators/sl_undeclared_node.ts +0 -10
  152. package/dist/integrity/slopnators/sl_undeclared_operation.js +111 -29
  153. package/dist/integrity/slopnators/sl_undeclared_operation.ts +134 -26
  154. package/dist/integrity/slopnators/sl_undeclared_variant.js +2 -2
  155. package/dist/integrity/slopnators/sl_undeclared_webhook.d.ts +2 -0
  156. package/dist/integrity/slopnators/sl_undeclared_webhook.js +90 -0
  157. package/dist/integrity/slopnators/sl_undeclared_webhook.ts +108 -0
  158. package/dist/integrity/slopnators/sl_underived_route.d.ts +2 -0
  159. package/dist/integrity/slopnators/sl_underived_route.js +61 -0
  160. package/dist/integrity/slopnators/sl_underived_route.ts +72 -0
  161. package/dist/integrity/slopnators/sl_ungoverned_island.js +3 -3
  162. package/dist/integrity/slopnators/sl_ungoverned_island.ts +1 -1
  163. package/dist/integrity/slopnators/sl_unlisted_program.d.ts +2 -0
  164. package/dist/integrity/slopnators/sl_unlisted_program.js +39 -0
  165. package/dist/integrity/slopnators/sl_unlisted_program.ts +40 -0
  166. package/dist/integrity/slopnators/sl_unnamed_island.js +1 -1
  167. package/dist/integrity/slopnators/sl_unpaired_member.js +3 -3
  168. package/dist/integrity/slopnators/sl_unpaired_member.ts +1 -1
  169. package/dist/integrity/slopnators/sl_unpaired_slopnator.js +4 -4
  170. package/dist/integrity/slopnators/sl_unpaired_slopnator.ts +1 -1
  171. package/dist/integrity/slopnators/sl_unprefixed_member.js +5 -31
  172. package/dist/integrity/slopnators/sl_unprefixed_member.ts +2 -28
  173. package/dist/integrity/slopnators/sl_unthreaded.js +11 -7
  174. package/dist/integrity/slopnators/sl_unthreaded.ts +6 -7
  175. package/dist/integrity/slopnators/sl_untranslated_key.js +2 -25
  176. package/dist/integrity/slopnators/sl_untranslated_key.ts +1 -25
  177. package/dist/integrity/slopnators/sl_unvaulted_secret.d.ts +2 -0
  178. package/dist/integrity/slopnators/sl_unvaulted_secret.js +107 -0
  179. package/dist/integrity/slopnators/sl_unvaulted_secret.ts +132 -0
  180. package/dist/integrity/slopnators/sl_unversioned.js +6 -4
  181. package/dist/integrity/slopnators/sl_unversioned.ts +5 -3
  182. package/dist/integrity/sweep.d.ts +1 -0
  183. package/dist/integrity/sweep.js +105 -42
  184. package/dist/integrity/sweep.ts +115 -42
  185. package/dist/knowledge/attach.js +1 -1
  186. package/dist/knowledge/generators/docsgen.js +8 -6
  187. package/dist/knowledge/generators/docsgen.ts +4 -2
  188. package/dist/knowledge/generators/glossary.js +4 -4
  189. package/dist/knowledge/generators/glossary.ts +1 -1
  190. package/dist/operations/access.d.ts +23 -0
  191. package/dist/operations/access.js +28 -1
  192. package/dist/operations/access.ts +43 -0
  193. package/dist/operations/attach.js +28 -109
  194. package/dist/operations/attach.ts +46 -113
  195. package/dist/operations/capabilities.d.ts +14 -5
  196. package/dist/operations/capabilities.js +13 -4
  197. package/dist/operations/capabilities.ts +27 -8
  198. package/dist/operations/dispatch.d.ts +8 -2
  199. package/dist/operations/dispatch.js +128 -185
  200. package/dist/operations/dispatch.ts +138 -191
  201. package/dist/operations/error-results.d.ts +1 -1
  202. package/dist/operations/error-results.js +2 -2
  203. package/dist/operations/error-results.ts +2 -2
  204. package/dist/operations/generators/capability.d.ts +11 -1
  205. package/dist/operations/generators/capability.js +481 -224
  206. package/dist/operations/generators/capability.ts +562 -233
  207. package/dist/operations/generators/compile.js +66 -39
  208. package/dist/operations/generators/compile.ts +78 -39
  209. package/dist/operations/generators/permits.js +11 -12
  210. package/dist/operations/generators/permits.ts +3 -4
  211. package/dist/operations/generators/seed.d.ts +1 -0
  212. package/dist/operations/generators/seed.js +19 -17
  213. package/dist/operations/generators/seed.ts +22 -14
  214. package/dist/operations/generators/trial.d.ts +6 -8
  215. package/dist/operations/generators/trial.js +200 -70
  216. package/dist/operations/generators/trial.ts +230 -93
  217. package/dist/operations/nodes.d.ts +15 -16
  218. package/dist/operations/nodes.ts +16 -17
  219. package/dist/operations/operations.d.ts +40 -4
  220. package/dist/operations/operations.js +1 -0
  221. package/dist/operations/operations.ts +61 -4
  222. package/dist/operations/result.d.ts +2 -2
  223. package/dist/operations/result.ts +2 -2
  224. package/package.json +1 -4
  225. package/dist/core/answers.d.ts +0 -171
  226. package/dist/core/answers.js +0 -789
  227. package/dist/core/answers.ts +0 -999
  228. package/dist/core/client.d.ts +0 -63
  229. package/dist/core/client.js +0 -249
  230. package/dist/core/client.ts +0 -289
  231. package/dist/core/files.js +0 -171
  232. package/dist/core/files.ts +0 -222
  233. package/dist/integrations/module.d.ts +0 -6
  234. package/dist/integrations/module.js +0 -153
  235. package/dist/integrations/module.ts +0 -191
  236. package/dist/integrity/slopnators/sl_unkept_rows.js +0 -104
  237. package/dist/integrity/slopnators/sl_unkept_rows.ts +0 -111
  238. package/dist/integrity/slopnators/sl_unseamed_reach.js +0 -73
  239. package/dist/integrity/slopnators/sl_unseamed_reach.ts +0 -79
  240. package/dist/operations/cost.d.ts +0 -14
  241. package/dist/operations/cost.js +0 -37
  242. package/dist/operations/cost.ts +0 -58
  243. package/dist/operations/errors/errors.d.ts +0 -52
  244. package/dist/operations/http.js +0 -124
  245. package/dist/operations/http.ts +0 -169
  246. package/dist/operations/memory.d.ts +0 -104
  247. package/dist/operations/memory.js +0 -365
  248. package/dist/operations/memory.ts +0 -470
  249. package/dist/operations/metering.d.ts +0 -55
  250. package/dist/operations/metering.js +0 -247
  251. package/dist/operations/metering.ts +0 -328
  252. package/dist/operations/queue-policy.d.ts +0 -3
  253. package/dist/operations/queue-policy.js +0 -8
  254. package/dist/operations/queue-policy.ts +0 -10
  255. package/dist/operations/seams.d.ts +0 -239
  256. package/dist/operations/seams.js +0 -45
  257. package/dist/operations/seams.ts +0 -364
  258. package/dist/operations/store.d.ts +0 -14
  259. package/dist/operations/store.js +0 -1
  260. package/dist/operations/store.ts +0 -29
  261. /package/dist/{operations → core}/errors/classes.d.ts +0 -0
  262. /package/dist/{operations → core}/errors/classes.js +0 -0
  263. /package/dist/{operations → core}/errors/classes.ts +0 -0
  264. /package/dist/integrity/slopnators/{sl_unkept_rows.d.ts → sl_door_in_a_node.d.ts} +0 -0
  265. /package/dist/integrity/slopnators/{sl_unseamed_reach.d.ts → sl_outside_an_operation.d.ts} +0 -0
package/dist/core/rows.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { createHash } from 'node:crypto';
1
2
  import ts from 'typescript';
2
3
  const PARSED = new Map();
3
4
  function tree(text) {
@@ -109,6 +110,13 @@ export function listBounds(text, list) {
109
110
  const inside = listy(held, list);
110
111
  return inside === undefined ? undefined : spanned(inside);
111
112
  }
113
+ export function nestedBounds(text, where, parent, list) {
114
+ const row = rowAt(text, where, parent);
115
+ if (row === undefined)
116
+ return undefined;
117
+ const inside = listy(row, list);
118
+ return inside === undefined ? undefined : spanned(inside);
119
+ }
112
120
  function elements(text, where) {
113
121
  let found;
114
122
  walk(tree(text), (node) => {
@@ -341,8 +349,26 @@ export function fieldIn(text, where, id, field) {
341
349
  }
342
350
  export function withField(text, where, id, field, value) {
343
351
  const held = rowAt(text, where, id);
344
- if (held === undefined)
345
- return undefined;
352
+ return held === undefined ? undefined : fieldOn(text, held, field, value);
353
+ }
354
+ export function withTopField(text, field, value) {
355
+ const held = headed(text);
356
+ return held === undefined ? undefined : fieldOn(text, held, field, value);
357
+ }
358
+ export function withoutField(text, where, id, field) {
359
+ const held = rowAt(text, where, id);
360
+ const one = held?.properties.find((each) => ts.isPropertyAssignment(each) && keyed(each) === field);
361
+ if (held === undefined || one === undefined)
362
+ return text;
363
+ let ends = one.getEnd();
364
+ if (text[ends] === ',')
365
+ ends += 1;
366
+ const line = text.lastIndexOf('\n', one.getStart());
367
+ if (line > held.getStart())
368
+ return text.slice(0, line) + text.slice(ends);
369
+ return text.slice(0, one.getStart()) + text.slice(text[ends] === ' ' ? ends + 1 : ends);
370
+ }
371
+ function fieldOn(text, held, field, value) {
346
372
  for (const one of held.properties) {
347
373
  if (!ts.isPropertyAssignment(one) || keyed(one) !== field)
348
374
  continue;
@@ -403,6 +429,9 @@ const lines = (held, pad) => {
403
429
  }
404
430
  return [String(held)];
405
431
  };
432
+ export function marked(parts) {
433
+ return createHash('sha256').update(parts.join('\n')).digest('hex').slice(0, 6);
434
+ }
406
435
  export function showing(row, only = []) {
407
436
  const held = row;
408
437
  const keys = Object.keys(held);
package/dist/core/rows.ts CHANGED
@@ -1,3 +1,5 @@
1
+ import { createHash } from 'node:crypto'
2
+
1
3
  import ts from 'typescript'
2
4
 
3
5
  export interface Bounds {
@@ -117,6 +119,13 @@ export function listBounds(text: string, list: string): Bounds | undefined {
117
119
  return inside === undefined ? undefined : spanned(inside)
118
120
  }
119
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
+
120
129
  function elements(text: string, where: Bounds): readonly ts.ObjectLiteralExpression[] {
121
130
  let found: ts.ArrayLiteralExpression | undefined
122
131
  walk(tree(text), (node) => {
@@ -351,7 +360,26 @@ export function withField(
351
360
  value: string,
352
361
  ): string | undefined {
353
362
  const held = rowAt(text, where, id)
354
- if (held === undefined) return undefined
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 {
355
383
  for (const one of held.properties) {
356
384
  if (!ts.isPropertyAssignment(one) || keyed(one) !== field) continue
357
385
  return `${text.slice(0, one.initializer.getStart())}${value}${text.slice(one.initializer.getEnd())}`
@@ -409,6 +437,10 @@ const lines = (held: unknown, pad: string): readonly string[] => {
409
437
  return [String(held)]
410
438
  }
411
439
 
440
+ export function marked(parts: readonly string[]): string {
441
+ return createHash('sha256').update(parts.join('\n')).digest('hex').slice(0, 6)
442
+ }
443
+
412
444
  export function showing(row: object, only: readonly string[] = []): string {
413
445
  const held = row as Record<string, unknown>
414
446
  const keys = Object.keys(held)
@@ -20,7 +20,7 @@ export interface Member extends Threaded {
20
20
  readonly version: number;
21
21
  }
22
22
  export declare const PREFIXES: {
23
- readonly workflow: "wf_";
24
- readonly view: "vw_";
25
- readonly agent: "agt_";
23
+ readonly workflow: 'wf_';
24
+ readonly view: 'vw_';
25
+ readonly agent: 'agt_';
26
26
  };
@@ -1,3 +1,5 @@
1
+ import { FOLDER } from '../integrations/disk.ts';
2
+ export { FOLDER };
1
3
  export interface Tree {
2
4
  readonly at: string;
3
5
  readonly dir: string;
@@ -5,8 +7,9 @@ export interface Tree {
5
7
  readonly root: string;
6
8
  }
7
9
  export declare class UnthreadedRepositoryError extends Error {
8
- constructor(from: string);
10
+ constructor(from: string, floor?: string);
9
11
  }
12
+ export declare function folder(): string;
10
13
  export declare function findTree(from?: string): Tree;
11
14
  export declare function strayFiles(root: string, prefix: string, ends: string): readonly string[];
12
15
  export declare function inTree(tree: Tree, file: string): string;
package/dist/core/tree.js CHANGED
@@ -1,16 +1,34 @@
1
- import { existsSync, readdirSync, statSync } from "./files.js";
1
+ import { FOLDER, attached, existsSync, readdirSync, statSync } from '../integrations/disk.js';
2
2
  import { dirname, join, relative, resolve } from 'node:path';
3
- import { cwd } from 'node:process';
3
+ import { cwd, env } from 'node:process';
4
+ export { FOLDER };
4
5
  const NAMES = ['_threads', 'threads'];
5
6
  const ROOT = 'thread.ts';
6
7
  export class UnthreadedRepositoryError extends Error {
7
- constructor(from) {
8
- super(`no tree above ${from} — looked for a ${NAMES.map((one) => `${one}/`).join(' or ')} directory in every directory up to the filesystem root. ` +
8
+ constructor(from, floor) {
9
+ super('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') + '. ' +
9
10
  'Run `thread init` to write one.');
10
11
  this.name = 'UnthreadedRepositoryError';
11
12
  }
12
13
  }
13
- export function findTree(from = cwd()) {
14
+ function given() {
15
+ const named = env[FOLDER];
16
+ return named === undefined ? '' : named;
17
+ }
18
+ export function folder() {
19
+ const named = given();
20
+ if (named !== '')
21
+ return resolve(named);
22
+ if (attached())
23
+ throw new Error('this is answering for a tree on another machine and nothing said which folder that is. ' +
24
+ FOLDER +
25
+ ' carries it and is not set here. Falling back to the folder this process was started in would read ' +
26
+ 'the wrong tree and report it as yours, so it stopped instead.');
27
+ return cwd();
28
+ }
29
+ export function findTree(from = folder()) {
30
+ const named = given();
31
+ const floor = attached() && named !== '' ? resolve(named) : undefined;
14
32
  let at = resolve(from);
15
33
  for (;;) {
16
34
  for (const name of NAMES) {
@@ -20,7 +38,9 @@ export function findTree(from = cwd()) {
20
38
  }
21
39
  const up = dirname(at);
22
40
  if (up === at)
23
- throw new UnthreadedRepositoryError(from);
41
+ throw new UnthreadedRepositoryError(from, floor);
42
+ if (at === floor)
43
+ throw new UnthreadedRepositoryError(from, floor);
24
44
  at = up;
25
45
  }
26
46
  }
package/dist/core/tree.ts CHANGED
@@ -1,6 +1,8 @@
1
- import { existsSync, readdirSync, statSync } from './files.ts'
1
+ import { FOLDER, attached, existsSync, readdirSync, statSync } from '../integrations/disk.ts'
2
2
  import { dirname, join, relative, resolve } from 'node:path'
3
- import { cwd } from 'node:process'
3
+ import { cwd, env } from 'node:process'
4
+
5
+ export { FOLDER }
4
6
 
5
7
  const NAMES = ['_threads', 'threads'] as const
6
8
  const ROOT = 'thread.ts'
@@ -13,16 +15,36 @@ export interface Tree {
13
15
  }
14
16
 
15
17
  export class UnthreadedRepositoryError extends Error {
16
- constructor(from: string) {
18
+ constructor(from: string, floor?: string) {
17
19
  super(
18
- `no tree above ${from} — looked for a ${NAMES.map((one) => `${one}/`).join(' or ')} directory in every directory up to the filesystem root. ` +
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') + '. ' +
19
21
  'Run `thread init` to write one.',
20
22
  )
21
23
  this.name = 'UnthreadedRepositoryError'
22
24
  }
23
25
  }
24
26
 
25
- export function findTree(from: string = cwd()): Tree {
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
26
48
  let at = resolve(from)
27
49
  for (;;) {
28
50
  for (const name of NAMES) {
@@ -30,7 +52,8 @@ export function findTree(from: string = cwd()): Tree {
30
52
  if (existsSync(dir) && statSync(dir).isDirectory()) return { at, dir, name, root: join(dir, ROOT) }
31
53
  }
32
54
  const up = dirname(at)
33
- if (up === at) throw new UnthreadedRepositoryError(from)
55
+ if (up === at) throw new UnthreadedRepositoryError(from, floor)
56
+ if (at === floor) throw new UnthreadedRepositoryError(from, floor)
34
57
  at = up
35
58
  }
36
59
  }
@@ -1,8 +1,8 @@
1
- export declare const VALUE_TYPES: readonly ["text", "number", "boolean", "date", "timestamp", "json"];
1
+ export declare const VALUE_TYPES: readonly ['text', 'number', 'boolean', 'date', 'timestamp', 'json'];
2
2
  export type ValueType = (typeof VALUE_TYPES)[number];
3
- export declare const KEYBOARDS: readonly ["text", "numeric", "decimal", "tel", "email", "url", "search"];
3
+ export declare const KEYBOARDS: readonly ['text', 'numeric', 'decimal', 'tel', 'email', 'url', 'search'];
4
4
  export type Keyboard = (typeof KEYBOARDS)[number];
5
- export declare const ENTRY_MODES: readonly ["plain", "calculator", "masked"];
5
+ export declare const ENTRY_MODES: readonly ['plain', 'calculator', 'masked'];
6
6
  export type EntryMode = (typeof ENTRY_MODES)[number];
7
7
  export type Checked = {
8
8
  readonly ok: true;
@@ -233,7 +233,9 @@ export const FORMATS = [
233
233
  check: instant,
234
234
  requires: ['now'],
235
235
  shown: (value, facts) => {
236
- const now = facts.now ?? Date.parse(new Date().toISOString());
236
+ const now = facts.now;
237
+ if (now === undefined)
238
+ return String(value);
237
239
  const gap = Date.parse(String(value)) - now;
238
240
  const UNITS = [
239
241
  ['year', 31_536_000_000],
@@ -270,7 +270,8 @@ export const FORMATS: readonly Format[] = [
270
270
  check: instant,
271
271
  requires: ['now'],
272
272
  shown: (value, facts) => {
273
- const now = facts.now ?? Date.parse(new Date().toISOString())
273
+ const now = facts.now
274
+ if (now === undefined) return String(value)
274
275
  const gap = Date.parse(String(value)) - now
275
276
  const UNITS: readonly (readonly [Intl.RelativeTimeFormatUnit, number])[] = [
276
277
  ['year', 31_536_000_000],
@@ -1,4 +1,4 @@
1
- import { quoted } from "../core/rows.js";
1
+ import { quoted } from '../core/rows.js';
2
2
  const DESCRIBES = 'What turns a declaration into a file: the rows that state something is to be made, and the generators that make it. They are kept apart so you can read what an app declares without opening the machinery that builds it.';
3
3
  const DERIVATION_SOURCE = [
4
4
  "import type { Threaded } from '../core/thread.ts'",
@@ -10,12 +10,12 @@ import { relative, resolve } from 'node:path';
10
10
  import { argv, stderr, stdout } from 'node:process';
11
11
  import { pathToFileURL } from 'node:url';
12
12
  import { build } from 'esbuild';
13
- import { descend, reached } from "../../core/descend.js";
14
- import { existsSync, readFileSync } from "../../core/files.js";
15
- import { exit, invoked } from "../../core/invoked.js";
16
- import { valuesIn } from "../../core/rows.js";
17
- import { findTree } from "../../core/tree.js";
18
- import { servesBrowser } from "../../integrations/target.js";
13
+ import { descend, reached } from '../../core/descend.js';
14
+ import { existsSync, readFileSync } from '../../integrations/disk.js';
15
+ import { exit, invoked } from '../../core/invoked.js';
16
+ import { valuesIn } from '../../core/rows.js';
17
+ import { findTree } from '../../core/tree.js';
18
+ import { servesBrowser } from '../../integrations/target.js';
19
19
  const TREE = findTree();
20
20
  const ROOT = TREE.at;
21
21
  const OUTSIDE = /(^|\/)node_modules(\/|$)/;
@@ -5,7 +5,7 @@ import { pathToFileURL } from 'node:url'
5
5
  import { build } from 'esbuild'
6
6
 
7
7
  import { descend, reached, type Pass } from '../../core/descend.ts'
8
- import { existsSync, readFileSync } from '../../core/files.ts'
8
+ import { existsSync, readFileSync } from '../../integrations/disk.ts'
9
9
  import { exit, invoked } from '../../core/invoked.ts'
10
10
  import { valuesIn } from '../../core/rows.ts'
11
11
  import { findTree } from '../../core/tree.ts'
@@ -1,4 +1,4 @@
1
- import { bodyHash, parseGenerated } from "./determinism.js";
1
+ import { bodyHash, parseGenerated } from './determinism.js';
2
2
  export function checkGenerated(expected, read, options = {}) {
3
3
  const checks = [];
4
4
  const expectedPaths = new Set(expected.map((file) => file.path));
@@ -1,5 +1,5 @@
1
1
  import { createHash } from 'node:crypto';
2
- import { named } from "../../core/scope.js";
2
+ import { named } from '../../core/scope.js';
3
3
  export function sortedBy(items, key) {
4
4
  return [...items].sort((a, b) => compare(key(a), key(b)));
5
5
  }
@@ -1,14 +1,14 @@
1
1
  import { join } from 'node:path';
2
2
  import { argv, stderr, stdout } from 'node:process';
3
- import { marked, wordsIn } from "../../core/answers.js";
4
- import { existsSync, readFileSync, writeFileSync } from "../../core/files.js";
5
- import { exit, invoked } from "../../core/invoked.js";
6
- import { valuesIn } from "../../core/rows.js";
7
- import { ours } from "../../core/scope.js";
8
- import { findTree, inTree } from "../../core/tree.js";
9
- import { CHECKED_BY, KINDS, LISTENS, SAID, WRITES, WRITTEN_BY, kindOf } from "../release.js";
3
+ import { existsSync, readFileSync, writeFileSync } from '../../integrations/disk.js';
4
+ import { exit, invoked } from '../../core/invoked.js';
5
+ import { marked, valuesIn } from '../../core/rows.js';
6
+ import { ours } from '../../core/scope.js';
7
+ import { findTree, inTree } from '../../core/tree.js';
8
+ import { CHECKED_BY, KINDS, LISTENS, SAID, WRITES, WRITTEN_BY, kindOf } from '../release.js';
10
9
  const TREE = findTree();
11
10
  const MANIFEST = 'package.json';
11
+ const addressed = (rows) => rows.flatMap((one) => [...(one.at === undefined ? [] : [one.at]), ...addressed(one.derives ?? [])]);
12
12
  const mark = (one) => marked([one.id ?? '', one.at ?? '']);
13
13
  function releases() {
14
14
  const at = inTree(TREE, 'derivation.ts');
@@ -22,45 +22,41 @@ function releases() {
22
22
  return [];
23
23
  }
24
24
  }
25
- function named() {
26
- const at = inTree(TREE, 'secrets.ts');
27
- if (!existsSync(at))
28
- return [];
25
+ function manager() {
29
26
  try {
30
- const held = valuesIn(readFileSync(at, 'utf8'));
31
- return (held.SECRETS ?? []).map((one) => one.id.toUpperCase()).sort();
27
+ const held = JSON.parse(readFileSync(join(TREE.at, MANIFEST), 'utf8'));
28
+ const said = typeof held.packageManager === 'string' ? held.packageManager.split('@')[0] : undefined;
29
+ return said === undefined || said === '' ? 'npm' : said;
32
30
  }
33
31
  catch {
34
- return [];
32
+ return 'npm';
35
33
  }
36
34
  }
37
35
  function scripted() {
38
- try {
39
- const held = JSON.parse(readFileSync(join(TREE.at, MANIFEST), 'utf8'));
40
- const scripts = held.scripts ?? {};
41
- return {
42
- build: scripts['build'] === undefined ? 'npm run build' : 'npm run build',
43
- start: scripts['start'] === undefined ? 'npm start' : 'npm start',
44
- };
45
- }
46
- catch {
47
- return { build: 'npm run build', start: 'npm start' };
48
- }
36
+ const tool = manager();
37
+ return { build: `${tool} run build`, start: tool === 'npm' ? 'npm start' : `${tool} start` };
49
38
  }
50
39
  function dockerfile(build, start) {
40
+ const tool = manager();
41
+ const corepack = tool === 'npm' ? [] : ['RUN corepack enable'];
42
+ const lock = tool === 'npm' ? 'package-lock.json*' : `${tool}-lock.yaml*`;
43
+ const first = tool === 'npm'
44
+ ? 'RUN npm ci --omit=dev --ignore-scripts || npm install --omit=dev --ignore-scripts'
45
+ : `RUN ${tool} install --prod --frozen-lockfile --ignore-scripts || ${tool} install --prod --ignore-scripts`;
46
+ const all = tool === 'npm' ? 'RUN npm install --ignore-scripts' : `RUN ${tool} install --ignore-scripts`;
51
47
  return [
52
- '# GENERATED by thread — do not edit. It is derived from what this app declares.',
53
- '',
54
48
  'FROM node:22-slim AS build',
55
49
  'WORKDIR /app',
56
- 'COPY package.json package-lock.json* ./',
57
- 'RUN npm ci --omit=dev --ignore-scripts || npm install --omit=dev --ignore-scripts',
50
+ ...corepack,
51
+ `COPY package.json ${lock} ./`,
52
+ first,
58
53
  'COPY . .',
59
- 'RUN npm install --ignore-scripts',
54
+ all,
60
55
  `RUN ${build}`,
61
56
  '',
62
57
  'FROM node:22-slim',
63
58
  'WORKDIR /app',
59
+ ...corepack,
64
60
  'ENV NODE_ENV=production',
65
61
  'COPY --from=build /app ./',
66
62
  `EXPOSE ${String(LISTENS)}`,
@@ -68,28 +64,27 @@ function dockerfile(build, start) {
68
64
  '',
69
65
  ].join('\n');
70
66
  }
71
- function compose(id, secrets) {
67
+ function compose(id) {
72
68
  return [
73
- '# GENERATED by thread — do not edit. It is derived from what this app declares.',
74
- '',
75
69
  'services:',
76
70
  ` ${id}:`,
77
71
  ' build: .',
78
72
  ' ports:',
79
73
  ` - "${String(LISTENS)}:${String(LISTENS)}"`,
80
- ...(secrets.length === 0
81
- ? []
82
- : [
83
- ' environment:',
84
- ...secrets.map((one) => ` ${one}: \${${one}}`),
85
- ]),
74
+ '',
75
+ ].join('\n');
76
+ }
77
+ function withheld() {
78
+ return [
79
+ '.thread',
80
+ 'node_modules',
81
+ '.git',
86
82
  '',
87
83
  ].join('\n');
88
84
  }
89
85
  function made() {
90
86
  const found = new Map();
91
87
  const rows = releases();
92
- const secrets = named();
93
88
  const { build, start } = scripted();
94
89
  for (const one of rows) {
95
90
  const id = one.id;
@@ -100,7 +95,8 @@ function made() {
100
95
  continue;
101
96
  if (kind === 'docker') {
102
97
  found.set('Dockerfile', dockerfile(build, start));
103
- found.set('compose.yaml', compose(id, secrets));
98
+ found.set('compose.yaml', compose(id));
99
+ found.set('.dockerignore', withheld());
104
100
  }
105
101
  }
106
102
  return found;
@@ -120,7 +116,12 @@ function run() {
120
116
  `\`thread create release\` — it is ${KINDS.join(' or ')}, and the prefix on its id says which.\n`);
121
117
  return 0;
122
118
  }
123
- const words = wordsIn(['read']);
119
+ const words = argv.slice(2).filter((one) => !one.startsWith('-'));
120
+ if (words[0] !== undefined && words[0] !== 'read') {
121
+ stderr.write('release: `' + words[0] + '` is not something this command does. `thread release` writes the release, and ' +
122
+ '`thread release read <mark>` prints one. Nothing was written.\n');
123
+ return 1;
124
+ }
124
125
  if (words[0] === 'read') {
125
126
  const which = words[1];
126
127
  const one = rows.find((row) => mark(row) === which);
@@ -132,10 +133,10 @@ function run() {
132
133
  }
133
134
  const kind = kindOf(one.id ?? '');
134
135
  stdout.write(`${String(one.id)} — ${kind === undefined ? 'a release of a kind this generator does not recognise' : SAID[kind]}\n`);
135
- for (const file of kind === undefined ? [] : WRITES[kind]) {
136
- stdout.write(`\n${file}\n`);
136
+ for (const one of kind === undefined ? [] : WRITES[kind]) {
137
+ stdout.write(`\n${one.at} — ${one.describes}\n`);
137
138
  try {
138
- stdout.write(readFileSync(join(TREE.at, file), 'utf8'));
139
+ stdout.write(readFileSync(join(TREE.at, one.at), 'utf8'));
139
140
  }
140
141
  catch {
141
142
  stdout.write('(nothing is there)\n');
@@ -145,6 +146,16 @@ function run() {
145
146
  }
146
147
  const files = made();
147
148
  const checking = argv.includes('--check');
149
+ const stated = new Set(addressed(rows));
150
+ const stray = [...files.keys()].filter((one) => !stated.has(one));
151
+ if (stray.length > 0) {
152
+ stderr.write(`release: this release produces ${stray.join(', ')}, and no row names ${stray.length === 1 ? 'it' : 'them'}. ` +
153
+ `Writing ${stray.length === 1 ? 'it' : 'them'} would leave a file this tree does not declare, and the descent ` +
154
+ 'reports that three steps later as a file nobody can account for. Take the release out with `thread remove ' +
155
+ '<its id>` and declare it again — the maker writes one row per file this kind produces, so all of them arrive ' +
156
+ 'together. Nothing was written.\n');
157
+ return 1;
158
+ }
148
159
  const shipped = installed();
149
160
  if (shipped.length > 0) {
150
161
  stderr.write(`release: this app installs ${shipped.join(', ')} as a dependency, and what a host runs may hold ` +
@@ -176,8 +187,10 @@ function run() {
176
187
  }
177
188
  for (const [at, text] of files)
178
189
  writeFileSync(join(TREE.at, at), text, 'utf8');
179
- stdout.write(`release: wrote ${[...files.keys()].join(', ')} — built and started by what this app's own manifest ` +
180
- `declares, listening on ${String(LISTENS)}, naming ${String(named().length)} secret(s) and holding none of them.\n`);
190
+ stdout.write('release: wrote ' + [...files.keys()].join(', ') + ' — built and started by what this app\'s own manifest ' +
191
+ 'declares, listening on ' + String(LISTENS) + '.\n' +
192
+ 'release: no secret is named there. A secret is read from the sealed store at the moment it is used, and ' +
193
+ 'naming it here would be telling whoever deploys this to put a credential in the environment instead.\n');
181
194
  return 0;
182
195
  }
183
196
  if (invoked(import.meta.url))