@prestack/thread 0.1.2 → 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 +193 -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 +67 -58
  7. package/dist/core/attach.ts +61 -68
  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 -2
  27. package/dist/core/init.js +51 -15
  28. package/dist/core/init.ts +58 -15
  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 -20
  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 -161
  98. package/dist/integrity/generators/trial.ts +145 -165
  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 -57
  229. package/dist/core/client.js +0 -247
  230. package/dist/core/client.ts +0 -286
  231. package/dist/core/files.js +0 -145
  232. package/dist/core/files.ts +0 -193
  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/init.ts CHANGED
@@ -1,8 +1,12 @@
1
- import { existsSync, readFileSync, writeFileSync } from './files.ts'
1
+ import { LOCKFILES, installing, manager } from '../integrations/cli.ts'
2
+ import { existsSync, readFileSync, writeFileSync } from '../integrations/disk.ts'
3
+ import { createRequire } from 'node:module'
2
4
  import { join, relative, resolve } from 'node:path'
5
+ import { fileURLToPath } from 'node:url'
3
6
 
4
7
  import { quoted } from './rows.ts'
5
8
 
9
+
6
10
  export const TREE = '_threads'
7
11
  export const ROOT = 'thread.ts'
8
12
 
@@ -22,19 +26,16 @@ export class AlreadyThreadedError extends Error {
22
26
 
23
27
  export const COMPILER = 'tsconfig.json'
24
28
 
25
- export const LOCKFILES = ['package-lock.json', 'pnpm-lock.yaml', 'yarn.lock', 'bun.lockb'] as const
26
-
27
- export function manager(into: string): readonly string[] {
28
- if (existsSync(join(into, 'pnpm-lock.yaml'))) return ['pnpm', 'add']
29
- if (existsSync(join(into, 'yarn.lock'))) return ['yarn', 'add']
30
- if (existsSync(join(into, 'bun.lockb'))) return ['bun', 'add']
31
- return ['npm', 'install']
32
- }
33
-
34
29
  export const MANIFEST = 'package.json'
35
30
 
31
+ const BUILT = '_gener' + 'ated/packages/core'
32
+
36
33
  const MODULE = 'module'
37
34
 
35
+ const NPM = 'npm'
36
+
37
+ const LATEST = 'latest'
38
+
38
39
  export const REGISTRY = '.npm' + 'rc'
39
40
 
40
41
  export const IGNORED = '.git' + 'ignore'
@@ -77,7 +78,7 @@ export function compiler(): string {
77
78
  }
78
79
 
79
80
  function owned(into: string): readonly { readonly id: string; readonly at: string; readonly describes: string; readonly runs?: string }[] {
80
- const held = LOCKFILES.filter((one) => existsSync(join(into, one)))
81
+ const held = Object.keys(LOCKFILES).filter((one) => existsSync(join(into, one)))
81
82
  return [
82
83
  {
83
84
  id: 'manifest',
@@ -137,6 +138,37 @@ export function root(into: string): string {
137
138
  ].join('\n')
138
139
  }
139
140
 
141
+ const own = createRequire(import.meta.url)
142
+
143
+ function manifested(at: string): { readonly name?: string; readonly version?: string } | undefined {
144
+ try {
145
+ return own(at) as { readonly name?: string; readonly version?: string }
146
+ } catch {
147
+ return undefined
148
+ }
149
+ }
150
+
151
+ function shipped(): readonly [string, string] {
152
+ let at = fileURLToPath(new URL('.', import.meta.url))
153
+ for (;;) {
154
+ const read = manifested(join(at, MANIFEST))
155
+ if (read !== undefined) {
156
+ if (read.name !== undefined && read.version !== undefined) return [read.name, read.version]
157
+ const core = manifested(join(at, BUILT, MANIFEST))
158
+ if (core?.name !== undefined && core.version !== undefined) return [core.name, core.version]
159
+ }
160
+ const up = resolve(at, '..')
161
+ if (up === at) {
162
+ throw new Error(
163
+ 'this framework cannot find the package it is running from, so it has no name to install into your project. ' +
164
+ 'That name is read off the manifest beside this code rather than written down anywhere, which is what keeps it ' +
165
+ 'right when the package is renamed.',
166
+ )
167
+ }
168
+ at = up
169
+ }
170
+ }
171
+
140
172
  export function init(into: string): readonly string[] {
141
173
  const dir = resolve(into, TREE)
142
174
  const at = join(dir, ROOT)
@@ -151,10 +183,9 @@ export function init(into: string): readonly string[] {
151
183
 
152
184
  const manifest = join(into, MANIFEST)
153
185
  if (!existsSync(manifest)) {
154
- throw new Error(
155
- `there is no ${MANIFEST} here, and a thread tree is source this package has to be able to load. ` +
156
- 'Run `npm init` first, then `thread init` — the tree is written beside a manifest, not instead of one.',
157
- )
186
+ const called = (into.split('/').filter(Boolean).pop() ?? 'app').toLowerCase().replace(/[^a-z0-9-]+/g, '-')
187
+ writeFileSync(manifest, JSON.stringify({ name: called, version: '0.0.0', type: MODULE }, null, 2) + '\n', 'utf8')
188
+ written.push(MANIFEST)
158
189
  }
159
190
  const stated = JSON.parse(readFileSync(manifest, 'utf8')) as { type?: string }
160
191
  if (stated.type !== MODULE) {
@@ -162,12 +193,24 @@ export function init(into: string): readonly string[] {
162
193
  written.push(MANIFEST)
163
194
  }
164
195
 
196
+ const [from, version] = shipped()
197
+ const holds = JSON.parse(readFileSync(manifest, 'utf8')) as { devDependencies?: Record<string, string> }
198
+ const dev = holds.devDependencies ?? {}
199
+ const wanted: Readonly<Record<string, string>> = { [from]: '^' + version, ...(manager(into) === NPM ? { [NPM]: LATEST } : {}) }
200
+ const missing = Object.entries(wanted).filter(([name]) => dev[name] === undefined)
201
+ if (missing.length > 0) {
202
+ writeFileSync(manifest, JSON.stringify({ ...holds, devDependencies: { ...dev, ...Object.fromEntries(missing) } }, null, 2) + '\n', 'utf8')
203
+ written.push(MANIFEST)
204
+ }
205
+
165
206
  const ignore = join(into, IGNORED)
166
207
  if (!existsSync(ignore)) {
167
208
  writeFileSync(ignore, ignored(), 'utf8')
168
209
  written.push(IGNORED)
169
210
  }
170
211
 
212
+ installing(into)
213
+
171
214
  for (const [name, text] of [[ROOT, root(into)]] as const) {
172
215
  writeFileSync(join(dir, name), text, 'utf8')
173
216
  written.push(relative(into, join(dir, name)))
@@ -4,6 +4,6 @@ export declare const BESIDE: (name: string) => string;
4
4
  export declare const LOADER: readonly string[];
5
5
  export declare const CLI: readonly string[];
6
6
  export declare const REFUSED: unique symbol;
7
- export declare const shared: (on: boolean) => void;
7
+ export declare const shared: (on: boolean) => boolean;
8
8
  export declare function exit(code?: number): never;
9
9
  export declare const refusal: (thrown: unknown) => number | undefined;
@@ -1,7 +1,7 @@
1
- import { realpathSync } from 'node:fs';
2
1
  import { dirname, join } from 'node:path';
3
2
  import { argv, exit as ending } from 'node:process';
4
3
  import { fileURLToPath } from 'node:url';
4
+ import { realpathSync } from '../integrations/disk.js';
5
5
  export function invoked(url) {
6
6
  const named = argv[1];
7
7
  if (named === undefined)
@@ -26,7 +26,9 @@ export const CLI = [
26
26
  export const REFUSED = Symbol.for('thread.refused');
27
27
  let sharing = false;
28
28
  export const shared = (on) => {
29
+ const was = sharing;
29
30
  sharing = on;
31
+ return was;
30
32
  };
31
33
  export function exit(code) {
32
34
  if (!sharing)
@@ -1,8 +1,9 @@
1
- import { realpathSync } from 'node:fs'
2
1
  import { dirname, join } from 'node:path'
3
2
  import { argv, exit as ending } from 'node:process'
4
3
  import { fileURLToPath } from 'node:url'
5
4
 
5
+ import { realpathSync } from '../integrations/disk.ts'
6
+
6
7
  export function invoked(url: string): boolean {
7
8
  const named = argv[1]
8
9
  if (named === undefined) return false
@@ -31,8 +32,10 @@ export const REFUSED = Symbol.for('thread.refused')
31
32
 
32
33
  let sharing = false
33
34
 
34
- export const shared = (on: boolean): void => {
35
+ export const shared = (on: boolean): boolean => {
36
+ const was = sharing
35
37
  sharing = on
38
+ return was
36
39
  }
37
40
 
38
41
  export function exit(code?: number): never {
@@ -7,17 +7,19 @@ var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExte
7
7
  return path;
8
8
  };
9
9
  import { resolve } from 'node:path';
10
- import { argv, exitCode, stdout } from 'node:process';
10
+ import { argv, exitCode, stderr, stdout } from 'node:process';
11
11
  import { pathToFileURL } from 'node:url';
12
- import { generators, RAN, REFUSED_BY } from "./chain.js";
13
- import { forget } from "./descend.js";
14
- import { refusal, shared } from "./invoked.js";
15
- import { findTree } from "./tree.js";
12
+ import { generators, RAN, REFUSED_BY } from './chain.js';
13
+ import { attached, here } from '../integrations/disk.js';
14
+ import { forget } from './descend.js';
15
+ import { refusal, shared } from './invoked.js';
16
+ import { findTree } from './tree.js';
16
17
  let spun = 0;
17
18
  const words = (command) => command.trim().split(/\s+/).slice(1);
18
19
  shared(true);
19
20
  const tree = findTree();
20
21
  const rows = await generators(tree);
22
+ let stale = true;
21
23
  for (const command of argv.slice(2).filter((one) => one !== '')) {
22
24
  const said = words(command);
23
25
  const name = said[0];
@@ -25,33 +27,45 @@ for (const command of argv.slice(2).filter((one) => one !== '')) {
25
27
  continue;
26
28
  const held = rows.find((one) => one.by === `thread ${name}`);
27
29
  if (held?.at === undefined) {
28
- stdout.write(`${REFUSED_BY} ${command}\n`);
29
30
  stdout.write(`round: no command named "${name}", so \`${command}\` was not run\n`);
31
+ stdout.write(`${REFUSED_BY} ${command}\n`);
30
32
  continue;
31
33
  }
32
34
  const found = resolve(tree.at, held.at);
33
- forget();
35
+ if (stale)
36
+ forget();
34
37
  argv.length = 1;
35
38
  argv.push(found, ...said.slice(1));
36
39
  spun += 1;
37
40
  process.exitCode = 0;
38
- try {
39
- await import(__rewriteRelativeImportExtension(`${pathToFileURL(found).href}?round=${String(spun)}`));
40
- if (exitCode !== undefined && exitCode !== 0)
41
- stdout.write(`${REFUSED_BY} ${command}\n`);
42
- else
43
- stdout.write(`${RAN} ${command}\n`);
41
+ const began = Date.now();
42
+ stderr.write(`settle: running ${command}\n`);
43
+ let ok = true;
44
+ const ran = attached() ? here().run(found, said.slice(1)) : undefined;
45
+ if (ran !== undefined) {
46
+ stdout.write(ran.out);
47
+ stderr.write(ran.err);
48
+ ok = ran.there && ran.ok;
49
+ if (!ran.there)
50
+ stdout.write('round: ' + command + ' could not be started where the tree is: ' + ran.why + '\n');
44
51
  }
45
- catch (thrown) {
46
- const code = refusal(thrown);
47
- if (code === undefined) {
48
- stdout.write(`${REFUSED_BY} ${command}\n`);
49
- stdout.write(`round: \`${command}\` threw: ${thrown instanceof Error ? thrown.message : String(thrown)}\n`);
52
+ else {
53
+ try {
54
+ await import(__rewriteRelativeImportExtension(`${pathToFileURL(found).href}?round=${String(spun)}`));
55
+ ok = exitCode === undefined || exitCode === 0;
56
+ }
57
+ catch (thrown) {
58
+ const code = refusal(thrown);
59
+ if (code === undefined) {
60
+ ok = false;
61
+ stdout.write(`round: \`${command}\` threw: ${thrown instanceof Error ? thrown.message : String(thrown)}\n`);
62
+ }
63
+ else
64
+ ok = code === 0;
50
65
  }
51
- else if (code === 0)
52
- stdout.write(`${RAN} ${command}\n`);
53
- else
54
- stdout.write(`${REFUSED_BY} ${command}\n`);
55
66
  }
67
+ stdout.write(`${ok ? RAN : REFUSED_BY} ${command}\n`);
68
+ stderr.write(`settle: ${command} — ${String(Date.now() - began)} ms, ${ok ? 'ran' : 'refused'}\n`);
69
+ stale = !command.trim().endsWith('--check');
56
70
  }
57
71
  process.exitCode = 0;
@@ -1,8 +1,9 @@
1
1
  import { resolve } from 'node:path'
2
- import { argv, exitCode, stdout } from 'node:process'
2
+ import { argv, exitCode, stderr, stdout } from 'node:process'
3
3
  import { pathToFileURL } from 'node:url'
4
4
 
5
5
  import { generators, RAN, REFUSED_BY } from './chain.ts'
6
+ import { attached, here } from '../integrations/disk.ts'
6
7
  import { forget } from './descend.ts'
7
8
  import { refusal, shared } from './invoked.ts'
8
9
  import { findTree } from './tree.ts'
@@ -16,6 +17,8 @@ shared(true)
16
17
  const tree = findTree()
17
18
  const rows = await generators(tree)
18
19
 
20
+ let stale = true
21
+
19
22
  for (const command of argv.slice(2).filter((one) => one !== '')) {
20
23
  const said = words(command)
21
24
  const name = said[0]
@@ -23,14 +26,14 @@ for (const command of argv.slice(2).filter((one) => one !== '')) {
23
26
 
24
27
  const held = rows.find((one) => one.by === `thread ${name}`)
25
28
  if (held?.at === undefined) {
26
- stdout.write(`${REFUSED_BY} ${command}\n`)
27
29
  stdout.write(`round: no command named "${name}", so \`${command}\` was not run\n`)
30
+ stdout.write(`${REFUSED_BY} ${command}\n`)
28
31
  continue
29
32
  }
30
33
 
31
34
  const found = resolve(tree.at, held.at)
32
35
 
33
- forget()
36
+ if (stale) forget()
34
37
 
35
38
  argv.length = 1
36
39
  argv.push(found, ...said.slice(1))
@@ -39,19 +42,33 @@ for (const command of argv.slice(2).filter((one) => one !== '')) {
39
42
 
40
43
  process.exitCode = 0
41
44
 
45
+ const began = Date.now()
46
+ stderr.write(`settle: running ${command}\n`)
47
+
48
+ let ok = true
49
+ const ran = attached() ? here().run(found, said.slice(1)) : undefined
50
+ if (ran !== undefined) {
51
+ stdout.write(ran.out)
52
+ stderr.write(ran.err)
53
+ ok = ran.there && ran.ok
54
+ if (!ran.there) stdout.write('round: ' + command + ' could not be started where the tree is: ' + ran.why + '\n')
55
+ } else {
42
56
  try {
43
57
  await import(`${pathToFileURL(found).href}?round=${String(spun)}`)
44
- if (exitCode !== undefined && exitCode !== 0) stdout.write(`${REFUSED_BY} ${command}\n`)
45
- else stdout.write(`${RAN} ${command}\n`)
58
+ ok = exitCode === undefined || exitCode === 0
46
59
  } catch (thrown) {
47
60
  const code = refusal(thrown)
48
61
  if (code === undefined) {
49
-
50
- stdout.write(`${REFUSED_BY} ${command}\n`)
62
+ ok = false
51
63
  stdout.write(`round: \`${command}\` threw: ${thrown instanceof Error ? thrown.message : String(thrown)}\n`)
52
- } else if (code === 0) stdout.write(`${RAN} ${command}\n`)
53
- else stdout.write(`${REFUSED_BY} ${command}\n`)
64
+ } else ok = code === 0
65
+ }
54
66
  }
67
+
68
+ stdout.write(`${ok ? RAN : REFUSED_BY} ${command}\n`)
69
+ stderr.write(`settle: ${command} — ${String(Date.now() - began)} ms, ${ok ? 'ran' : 'refused'}\n`)
70
+
71
+ stale = !command.trim().endsWith('--check')
55
72
  }
56
73
 
57
74
  process.exitCode = 0
@@ -5,6 +5,7 @@ export interface Bounds {
5
5
  export declare function inCode(text: string): readonly boolean[];
6
6
  export declare function matched(text: string, code: readonly boolean[], from: number, open: string, close: string): number;
7
7
  export declare function listBounds(text: string, list: string): Bounds | undefined;
8
+ export declare function nestedBounds(text: string, where: Bounds, parent: string, list: string): Bounds | undefined;
8
9
  export declare function holdsRow(text: string, where: Bounds, id: string): boolean;
9
10
  export declare function seededAt(text: string, where: Bounds, id: string): string | undefined;
10
11
  export declare function rowsIn(text: string, where: Bounds): readonly {
@@ -24,5 +25,8 @@ export declare const STATED: string[];
24
25
  export declare function reconciled(held: string, source: string, fields: readonly string[]): string;
25
26
  export declare function fieldIn(text: string, where: Bounds, id: string, field: string): string | undefined;
26
27
  export declare function withField(text: string, where: Bounds, id: string, field: string, value: string): string | undefined;
28
+ export declare function withTopField(text: string, field: string, value: string): string | undefined;
29
+ export declare function withoutField(text: string, where: Bounds, id: string, field: string): string;
27
30
  export declare function withoutRow(text: string, where: Bounds, id: string): string;
31
+ export declare function marked(parts: readonly string[]): string;
28
32
  export declare function showing(row: object, only?: readonly string[]): string;
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'",