@prestack/thread 0.0.2 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (183) hide show
  1. package/CHANGELOG.md +98 -0
  2. package/dist/core/{asking.d.ts → answers.d.ts} +6 -14
  3. package/dist/core/{asking.js → answers.js} +13 -332
  4. package/dist/core/{asking.ts → answers.ts} +14 -357
  5. package/dist/core/attach.js +2 -2
  6. package/dist/core/attach.ts +2 -3
  7. package/dist/core/chain.js +4 -11
  8. package/dist/core/chain.ts +4 -3
  9. package/dist/core/client.d.ts +57 -0
  10. package/dist/core/client.js +186 -0
  11. package/dist/core/client.ts +227 -0
  12. package/dist/core/descend.d.ts +2 -0
  13. package/dist/core/descend.js +109 -16
  14. package/dist/core/descend.ts +120 -16
  15. package/dist/core/files.d.ts +45 -0
  16. package/dist/core/files.js +95 -0
  17. package/dist/core/files.ts +137 -0
  18. package/dist/core/init.d.ts +1 -0
  19. package/dist/core/init.js +10 -2
  20. package/dist/core/init.ts +8 -2
  21. package/dist/core/invoked.js +1 -1
  22. package/dist/core/invoked.ts +1 -1
  23. package/dist/core/rows.d.ts +2 -0
  24. package/dist/core/rows.js +65 -0
  25. package/dist/core/rows.ts +58 -0
  26. package/dist/core/tree.js +1 -1
  27. package/dist/core/tree.ts +1 -1
  28. package/dist/derivation/attach.js +0 -12
  29. package/dist/derivation/attach.ts +0 -12
  30. package/dist/derivation/generators/build.js +8 -7
  31. package/dist/derivation/generators/build.ts +8 -7
  32. package/dist/derivation/generators/release.d.ts +1 -1
  33. package/dist/derivation/generators/release.js +15 -23
  34. package/dist/derivation/generators/release.ts +15 -15
  35. package/dist/integrations/attach.js +1 -19
  36. package/dist/integrations/attach.ts +1 -19
  37. package/dist/integrations/generators/read.js +2 -2
  38. package/dist/integrations/generators/read.ts +4 -4
  39. package/dist/integrations/generators/vault.js +4 -12
  40. package/dist/integrations/generators/vault.ts +4 -4
  41. package/dist/integrations/module.js +5 -3
  42. package/dist/integrations/module.ts +5 -3
  43. package/dist/integrations/vault.js +5 -5
  44. package/dist/integrations/vault.ts +5 -5
  45. package/dist/integrity/attach.js +0 -12
  46. package/dist/integrity/attach.ts +0 -12
  47. package/dist/integrity/check-threads.js +1 -1
  48. package/dist/integrity/check-threads.ts +1 -1
  49. package/dist/integrity/constraint.d.ts +11 -2
  50. package/dist/integrity/constraint.js +163 -20
  51. package/dist/integrity/constraint.ts +174 -22
  52. package/dist/integrity/generators/compiler.js +1 -1
  53. package/dist/integrity/generators/compiler.ts +1 -1
  54. package/dist/integrity/generators/gate.js +8 -16
  55. package/dist/integrity/generators/gate.ts +8 -7
  56. package/dist/integrity/generators/publish.js +14 -26
  57. package/dist/integrity/generators/publish.ts +14 -17
  58. package/dist/integrity/generators/trial.d.ts +2 -2
  59. package/dist/integrity/generators/trial.js +108 -50
  60. package/dist/integrity/generators/trial.ts +107 -48
  61. package/dist/integrity/lint.js +3 -3
  62. package/dist/integrity/lint.ts +3 -3
  63. package/dist/integrity/lsp/diagnostics.js +1 -1
  64. package/dist/integrity/lsp/diagnostics.ts +1 -1
  65. package/dist/integrity/lsp/lint.js +1 -1
  66. package/dist/integrity/lsp/lint.ts +1 -1
  67. package/dist/integrity/slopnator.d.ts +1 -14
  68. package/dist/integrity/slopnator.js +8 -40
  69. package/dist/integrity/slopnator.ts +9 -51
  70. package/dist/integrity/slopnators/sl_broken_lineage.js +4 -2
  71. package/dist/integrity/slopnators/sl_broken_lineage.ts +4 -2
  72. package/dist/integrity/slopnators/sl_declared_elsewhere.js +24 -3
  73. package/dist/integrity/slopnators/sl_declared_elsewhere.ts +24 -3
  74. package/dist/integrity/slopnators/sl_derived_drift.js +10 -4
  75. package/dist/integrity/slopnators/sl_derived_drift.ts +10 -4
  76. package/dist/integrity/slopnators/sl_inert_island.js +7 -13
  77. package/dist/integrity/slopnators/sl_inert_island.ts +7 -16
  78. package/dist/integrity/slopnators/sl_inert_slopnator.js +8 -8
  79. package/dist/integrity/slopnators/sl_inert_slopnator.ts +8 -8
  80. package/dist/integrity/slopnators/sl_missing_row.js +14 -9
  81. package/dist/integrity/slopnators/sl_missing_row.ts +14 -10
  82. package/dist/integrity/slopnators/sl_no_child_process.js +8 -8
  83. package/dist/integrity/slopnators/sl_no_child_process.ts +8 -8
  84. package/dist/integrity/slopnators/sl_no_import_cycle.js +1 -1
  85. package/dist/integrity/slopnators/sl_no_import_cycle.ts +1 -1
  86. package/dist/integrity/slopnators/sl_no_such_command.js +20 -5
  87. package/dist/integrity/slopnators/sl_no_such_command.ts +22 -5
  88. package/dist/integrity/slopnators/sl_only_takes_a_literal.js +8 -8
  89. package/dist/integrity/slopnators/sl_only_takes_a_literal.ts +8 -8
  90. package/dist/integrity/slopnators/sl_stale_path.js +91 -18
  91. package/dist/integrity/slopnators/sl_stale_path.ts +97 -18
  92. package/dist/integrity/slopnators/sl_twice_meant.js +25 -2
  93. package/dist/integrity/slopnators/sl_twice_meant.ts +28 -2
  94. package/dist/integrity/slopnators/sl_twice_parented.js +26 -5
  95. package/dist/integrity/slopnators/sl_twice_parented.ts +26 -5
  96. package/dist/integrity/slopnators/sl_undeclared_node.js +18 -2
  97. package/dist/integrity/slopnators/sl_undeclared_node.ts +18 -2
  98. package/dist/integrity/slopnators/sl_undeclared_operation.js +3 -1
  99. package/dist/integrity/slopnators/sl_undeclared_operation.ts +3 -1
  100. package/dist/integrity/slopnators/sl_undeclared_variant.js +6 -2
  101. package/dist/integrity/slopnators/sl_undeclared_variant.ts +6 -2
  102. package/dist/integrity/slopnators/sl_ungoverned_island.js +3 -4
  103. package/dist/integrity/slopnators/sl_ungoverned_island.ts +3 -4
  104. package/dist/integrity/slopnators/sl_unkept_rows.js +1 -1
  105. package/dist/integrity/slopnators/sl_unkept_rows.ts +1 -1
  106. package/dist/integrity/slopnators/sl_unpaired_member.js +13 -8
  107. package/dist/integrity/slopnators/sl_unpaired_member.ts +16 -8
  108. package/dist/integrity/slopnators/sl_unpaired_slopnator.js +16 -3
  109. package/dist/integrity/slopnators/sl_unpaired_slopnator.ts +16 -3
  110. package/dist/integrity/slopnators/sl_unthreaded.d.ts +1 -0
  111. package/dist/integrity/slopnators/sl_unthreaded.js +18 -10
  112. package/dist/integrity/slopnators/sl_unthreaded.ts +19 -11
  113. package/dist/integrity/slopnators/sl_untranslated_key.js +1 -1
  114. package/dist/integrity/slopnators/sl_untranslated_key.ts +1 -1
  115. package/dist/integrity/slopnators/sl_unversioned.js +16 -2
  116. package/dist/integrity/slopnators/sl_unversioned.ts +16 -2
  117. package/dist/integrity/sweep.js +17 -3
  118. package/dist/integrity/sweep.ts +20 -3
  119. package/dist/knowledge/attach.js +0 -6
  120. package/dist/knowledge/attach.ts +0 -6
  121. package/dist/knowledge/generators/docsgen.js +12 -3
  122. package/dist/knowledge/generators/docsgen.ts +12 -2
  123. package/dist/knowledge/generators/glossary.js +1 -1
  124. package/dist/knowledge/generators/glossary.ts +1 -1
  125. package/dist/operations/attach.js +2 -26
  126. package/dist/operations/attach.ts +2 -27
  127. package/dist/operations/dispatch.js +2 -3
  128. package/dist/operations/dispatch.ts +2 -3
  129. package/dist/operations/generators/capability.js +4 -7
  130. package/dist/operations/generators/capability.ts +3 -6
  131. package/dist/operations/generators/compile.js +8 -13
  132. package/dist/operations/generators/compile.ts +8 -4
  133. package/dist/operations/generators/permits.js +4 -13
  134. package/dist/operations/generators/permits.ts +3 -3
  135. package/dist/operations/generators/seed.js +5 -2
  136. package/dist/operations/generators/seed.ts +6 -2
  137. package/dist/operations/generators/trial.js +10 -10
  138. package/dist/operations/generators/trial.ts +10 -10
  139. package/dist/operations/http.js +4 -1
  140. package/dist/operations/http.ts +6 -1
  141. package/package.json +2 -2
  142. package/dist/core/cli.d.ts +0 -2
  143. package/dist/core/cli.js +0 -1761
  144. package/dist/core/cli.ts +0 -2004
  145. package/dist/core/generators/meta.d.ts +0 -2
  146. package/dist/core/generators/meta.js +0 -829
  147. package/dist/core/generators/meta.ts +0 -1006
  148. package/dist/derivation/generators/derivation.d.ts +0 -3
  149. package/dist/derivation/generators/derivation.js +0 -273
  150. package/dist/derivation/generators/derivation.ts +0 -347
  151. package/dist/derivation/makers/release.d.ts +0 -1
  152. package/dist/derivation/makers/release.js +0 -107
  153. package/dist/derivation/makers/release.ts +0 -135
  154. package/dist/integrations/generators/secret.d.ts +0 -1
  155. package/dist/integrations/generators/secret.js +0 -233
  156. package/dist/integrations/generators/secret.ts +0 -289
  157. package/dist/integrations/generators/target.d.ts +0 -1
  158. package/dist/integrations/generators/target.js +0 -775
  159. package/dist/integrations/generators/target.ts +0 -944
  160. package/dist/integrity/generators/rule.d.ts +0 -2
  161. package/dist/integrity/generators/rule.js +0 -213
  162. package/dist/integrity/generators/rule.ts +0 -271
  163. package/dist/integrity/generators/seal.d.ts +0 -4
  164. package/dist/integrity/generators/seal.js +0 -101
  165. package/dist/integrity/generators/seal.ts +0 -117
  166. package/dist/integrity/sealed.d.ts +0 -1
  167. package/dist/integrity/sealed.js +0 -56
  168. package/dist/integrity/sealed.ts +0 -56
  169. package/dist/knowledge/generators/skills.d.ts +0 -1
  170. package/dist/knowledge/generators/skills.js +0 -222
  171. package/dist/knowledge/generators/skills.ts +0 -263
  172. package/dist/operations/generators/grant.d.ts +0 -1
  173. package/dist/operations/generators/grant.js +0 -267
  174. package/dist/operations/generators/grant.ts +0 -336
  175. package/dist/operations/generators/operation.d.ts +0 -1
  176. package/dist/operations/generators/operation.js +0 -811
  177. package/dist/operations/generators/operation.ts +0 -972
  178. package/dist/operations/makers/capability.d.ts +0 -1
  179. package/dist/operations/makers/capability.js +0 -117
  180. package/dist/operations/makers/capability.ts +0 -166
  181. package/dist/operations/makers/trial.d.ts +0 -1
  182. package/dist/operations/makers/trial.js +0 -413
  183. package/dist/operations/makers/trial.ts +0 -520
@@ -1,5 +1,5 @@
1
1
  import { spawnSync } from 'node:child_process'
2
- import { existsSync, readdirSync, readFileSync, realpathSync, statSync } from 'node:fs'
2
+ import { attached, existsSync, kind, readdirSync, readFileSync, realpathSync, statSync, writeFileSync } from './files.ts'
3
3
  import { dirname, join, relative, resolve } from 'node:path'
4
4
 
5
5
  import ts from 'typescript'
@@ -35,37 +35,46 @@ export const ROOT = TREE_AT?.at ?? NOWHERE
35
35
  export const TREE = `${TREE_AT?.name ?? NOWHERE}/`
36
36
  export const THREAD_AT = `${TREE_AT?.name ?? NOWHERE}/thread.ts`
37
37
 
38
- const OWN_IGNORES: readonly string[] | undefined = (() => {
39
- if (spawnSync('git', ['rev-parse', '--is-inside-work-tree'], { cwd: ROOT, stdio: 'ignore' }).status === 0) {
40
- return undefined
38
+ let ignores: readonly string[] | undefined
39
+ let readIgnores = false
40
+
41
+ function ownIgnores(): readonly string[] | undefined {
42
+ if (readIgnores) return ignores
43
+ readIgnores = true
44
+ if (!attached() && spawnSync('git', ['rev-parse', '--is-inside-work-tree'], { cwd: ROOT, stdio: 'ignore' }).status === 0) {
45
+ return ignores
41
46
  }
42
47
  try {
43
- return readFileSync(join(ROOT, '.gitignore'), 'utf8')
48
+ ignores = readFileSync(join(ROOT, '.gitignore'), 'utf8')
44
49
  .split('\n')
45
50
  .map((one) => one.trim())
46
51
  .filter((one) => one !== '' && !one.startsWith('#') && !one.startsWith('!'))
47
52
  .map((one) => one.replace(/^\/+|\/+$/g, ''))
48
53
  .filter((one) => one !== '' && !one.includes('*'))
49
54
  } catch {
50
- return []
55
+ ignores = []
51
56
  }
52
- })()
57
+ return ignores
58
+ }
53
59
 
54
60
  const asked = new Map<string, boolean>()
55
61
 
56
62
  export function unkept(at: string): boolean {
57
63
  const held = asked.get(at)
58
64
  if (held !== undefined) return held
65
+ const own = ownIgnores()
59
66
  const found =
60
- OWN_IGNORES === undefined
67
+ own === undefined
61
68
  ? spawnSync('git', ['check-ignore', '-q', '--', at], { cwd: ROOT, stdio: 'ignore' }).status === 0
62
- : OWN_IGNORES.some((one) => at === one || at.startsWith(`${one}/`) || at.split('/').includes(one))
69
+ : own.some((one) => at === one || at.startsWith(`${one}/`) || at.split('/').includes(one))
63
70
  asked.set(at, found)
64
71
  return found
65
72
  }
66
73
 
67
74
  export const LISTS = ['declares', 'derives', 'seeds'] as const
68
75
 
76
+ export const ROW_LISTS = [...LISTS, 'holds'] as const
77
+
69
78
  const SOURCE = /\.(ts|tsx|mts|cts)$/
70
79
  const RUNNER = /^(?:pnpm|npm|yarn) (?:run )?([\w:.-]+)/
71
80
 
@@ -198,12 +207,77 @@ const INVARIANT: ts.CompilerOptions = {
198
207
  jsx: ts.JsxEmit.ReactJSX,
199
208
  }
200
209
 
210
+ const NEVER: readonly string[] = ['node_modules', 'bower_components', 'jspm_packages']
211
+
212
+ interface Wanted {
213
+ readonly under: string
214
+ readonly ending: string
215
+ }
216
+
217
+ function bounds(from: string, one: string): Wanted {
218
+ const star = one.indexOf('*')
219
+ if (star === -1) return { under: resolve(from, one), ending: '' }
220
+ const head = one.slice(0, star)
221
+ const cut = head.lastIndexOf('/')
222
+ const tail = one.slice(star)
223
+ const dot = tail.lastIndexOf('.')
224
+ const end = dot === -1 ? '' : tail.slice(dot)
225
+ return {
226
+ under: resolve(from, cut === -1 ? '' : head.slice(0, cut)),
227
+ ending: end.includes('/') || end.includes('*') ? '' : end,
228
+ }
229
+ }
230
+
231
+ function wanted(at: string, only: readonly Wanted[]): boolean {
232
+ if (only.length === 0) return true
233
+ return only.some((one) => (at === one.under || at.startsWith(`${one.under}/`)) && at.endsWith(one.ending))
234
+ }
235
+
236
+ function listing(at: string, take: readonly string[], skip: readonly string[], only: readonly Wanted[], deep: number): readonly string[] {
237
+ const found: string[] = []
238
+ if (deep < 0 || !existsSync(at)) return found
239
+ for (const one of readdirSync(at, { withFileTypes: true })) {
240
+ const full = join(at, one.name)
241
+ if (skip.some((each) => full === each || full.startsWith(`${each}/`))) continue
242
+ if (one.isDirectory()) {
243
+ if (NEVER.includes(one.name)) continue
244
+ found.push(...listing(full, take, skip, only, deep - 1))
245
+ } else if (take.some((each) => one.name.endsWith(each)) && wanted(full, only)) {
246
+ found.push(full)
247
+ }
248
+ }
249
+ return found
250
+ }
251
+
252
+ function parsing(from: string): ts.ParseConfigHost {
253
+ return {
254
+ useCaseSensitiveFileNames: true,
255
+ fileExists: (at) => existsSync(at),
256
+ readFile: (at) => {
257
+ try {
258
+ return readFileSync(at)
259
+ } catch {
260
+ return undefined
261
+ }
262
+ },
263
+ readDirectory: (at, take, skip, only, deep) =>
264
+ listing(
265
+ at,
266
+ take,
267
+ (skip ?? []).map((one) => resolve(from, one.endsWith('/**') ? one.slice(0, -3) : one)),
268
+ only.map((one) => bounds(from, one)),
269
+ deep ?? Number.MAX_SAFE_INTEGER,
270
+ ),
271
+ }
272
+ }
273
+
201
274
  export function configured(): { readonly options: ts.CompilerOptions; readonly from: string; readonly trouble?: string } {
202
275
  for (const name of CONFIGS) {
203
276
  const at = join(ROOT, name)
204
- if (!ts.sys.fileExists(at)) continue
277
+ if (!existsSync(at)) continue
205
278
 
206
- const held = ts.readConfigFile(at, ts.sys.readFile.bind(ts.sys))
279
+ const host = parsing(ROOT)
280
+ const held = ts.readConfigFile(at, host.readFile)
207
281
  if (held.error !== undefined) {
208
282
  return {
209
283
  options: { ...FALLBACK, ...INVARIANT },
@@ -212,7 +286,7 @@ export function configured(): { readonly options: ts.CompilerOptions; readonly f
212
286
  }
213
287
  }
214
288
 
215
- const parsed = ts.parseJsonConfigFileContent(held.config as object, ts.sys, dirname(at))
289
+ const parsed = ts.parseJsonConfigFileContent(held.config as object, host, dirname(at))
216
290
  const complained = parsed.errors[0]
217
291
  if (complained !== undefined) {
218
292
  return {
@@ -227,13 +301,37 @@ export function configured(): { readonly options: ts.CompilerOptions; readonly f
227
301
  return { options: { ...FALLBACK, ...INVARIANT }, from: '' }
228
302
  }
229
303
 
304
+ export function hosting(options: ts.CompilerOptions): ts.CompilerHost {
305
+ const host = ts.createCompilerHost(options, true)
306
+ const lib = host.getDefaultLibLocation?.()
307
+ const toolchain = (at: string): boolean => lib !== undefined && (at === lib || at.startsWith(`${lib}/`))
308
+ return {
309
+ ...host,
310
+ fileExists: (at) => (toolchain(at) ? host.fileExists(at) : kind(at) === 'file'),
311
+ directoryExists: (at) => (toolchain(at) ? (host.directoryExists?.(at) ?? false) : kind(at) === 'folder'),
312
+ readFile: (at) => (toolchain(at) ? host.readFile(at) : kind(at) === 'file' ? readFileSync(at) : undefined),
313
+ getSourceFile: (at, version) => {
314
+ if (toolchain(at)) return host.getSourceFile(at, version)
315
+ if (kind(at) !== 'file') return undefined
316
+ return ts.createSourceFile(at, readFileSync(at), version, true)
317
+ },
318
+ getDirectories: (at) =>
319
+ kind(at) === 'folder' ? readdirSync(at, { withFileTypes: true }).filter((one) => one.isDirectory()).map((one) => one.name) : [],
320
+ realpath: (at) => (toolchain(at) ? (host.realpath?.(at) ?? at) : realpathSync(at)),
321
+ writeFile: (at, said) => {
322
+ writeFileSync(at, said)
323
+ },
324
+ }
325
+ }
326
+
230
327
  let built: { program: ts.Program; checker: ts.TypeChecker } | undefined
231
328
 
232
329
  function compiled(): { program: ts.Program; checker: ts.TypeChecker } {
233
330
  rooted()
234
331
  if (built !== undefined) return built
235
332
  const roots = [THREAD_AT, ...sources(), ...shipped()].filter((one) => SOURCE.test(one)).map((one) => resolve(ROOT, one))
236
- const program = ts.createProgram([...new Set(roots)], configured().options)
333
+ const options = configured().options
334
+ const program = ts.createProgram([...new Set(roots)], options, hosting(options))
237
335
  built = { program, checker: program.getTypeChecker() }
238
336
  return built
239
337
  }
@@ -659,6 +757,9 @@ export function forget(): void {
659
757
  scanned = undefined
660
758
  commands = undefined
661
759
  source.clear()
760
+ asked.clear()
761
+ ignores = undefined
762
+ readIgnores = false
662
763
  }
663
764
 
664
765
  export function descend(): Pass {
@@ -846,7 +947,10 @@ export function descend(): Pass {
846
947
  }
847
948
 
848
949
  for (const one of value.elements) {
849
- if (!ts.isObjectLiteralExpression(one)) {
950
+ const declaration = ts.isIdentifier(one) ? checker.getSymbolAtLocation(one)?.valueDeclaration : undefined
951
+ const initial = declaration !== undefined && ts.isVariableDeclaration(declaration) ? unwrap(declaration.initializer) : undefined
952
+ const object = ts.isObjectLiteralExpression(one) ? one : initial !== undefined && ts.isObjectLiteralExpression(initial) ? initial : undefined
953
+ if (object === undefined) {
850
954
  findings.push({
851
955
  file: at,
852
956
  detail: `\`${by}\` holds something in \`${list}\` that is not a row written out here`,
@@ -857,14 +961,14 @@ export function descend(): Pass {
857
961
  rows += 1
858
962
  const wanted = checker.getContextualType(one)
859
963
  const kind = wanted === undefined ? undefined : elementOf(checker, wanted)
860
- const row = rowOf(one, kind === undefined ? type : checker.typeToString(kind), at, kind)
964
+ const row = rowOf(object, kind === undefined ? type : checker.typeToString(kind), at, kind)
861
965
  coin(row.id, row.describes, under, by)
862
966
  judge(row, by, list, false)
863
967
  if (row.at !== undefined) {
864
968
  threaded.set(row.at, [...(threaded.get(row.at) ?? []), { list, by, row }])
865
969
  if (list === 'derives' && row.id !== undefined) derived.set(row.id, row)
866
970
  }
867
- if (LISTS.some((name) => row.fields.has(name))) into(one, row.type, at, under, by, true)
971
+ if (LISTS.some((name) => row.fields.has(name))) into(object, row.type, at, under, by, true)
868
972
  if (row.at !== undefined) open(row.at, wanted === undefined ? undefined : elementOf(checker, wanted), under, row.id ?? by, by)
869
973
  }
870
974
  }
@@ -0,0 +1,45 @@
1
+ import * as disk from 'node:fs';
2
+ export type Kind = 'file' | 'folder' | 'other' | 'none';
3
+ export interface Listed {
4
+ readonly name: string;
5
+ readonly kind: Exclude<Kind, 'none'>;
6
+ }
7
+ export interface Measured {
8
+ readonly size: number;
9
+ readonly modified: number;
10
+ }
11
+ export interface Answering {
12
+ readonly kind: (at: string) => Kind;
13
+ readonly read: (at: string) => string;
14
+ readonly write: (at: string, said: string) => void;
15
+ readonly list: (at: string) => readonly Listed[];
16
+ readonly stat: (at: string) => Measured;
17
+ readonly remove: (at: string) => void;
18
+ readonly real: (at: string) => string;
19
+ }
20
+ export declare function answeredBy(one: Answering | undefined): void;
21
+ export declare function attached(): boolean;
22
+ export declare function kind(at: string): Kind;
23
+ export declare function existsSync(at: disk.PathLike): boolean;
24
+ export declare function readFileSync(at: disk.PathOrFileDescriptor, how?: BufferEncoding): string;
25
+ export declare function writeFileSync(at: disk.PathOrFileDescriptor, said: string, how?: BufferEncoding): void;
26
+ export interface Named {
27
+ readonly name: string;
28
+ readonly isFile: () => boolean;
29
+ readonly isDirectory: () => boolean;
30
+ }
31
+ export declare function readdirSync(at: disk.PathLike): string[];
32
+ export declare function readdirSync(at: disk.PathLike, how: {
33
+ readonly withFileTypes: true;
34
+ }): readonly Named[];
35
+ export interface Held extends Named, Measured {
36
+ readonly mtimeMs: number;
37
+ }
38
+ export declare function statSync(at: disk.PathLike): Held;
39
+ export declare function statSync(at: disk.PathLike, how: {
40
+ readonly throwIfNoEntry: false;
41
+ }): Held | undefined;
42
+ export declare function rmSync(at: disk.PathLike, how?: disk.RmOptions): void;
43
+ export declare function renameSync(from: disk.PathLike, to: disk.PathLike): void;
44
+ export declare function appendFileSync(at: disk.PathOrFileDescriptor, said: string): void;
45
+ export declare function realpathSync(at: disk.PathLike): string;
@@ -0,0 +1,95 @@
1
+ import * as disk from 'node:fs';
2
+ import { dirname } from 'node:path';
3
+ let answering;
4
+ export function answeredBy(one) {
5
+ answering = one;
6
+ }
7
+ const asked = (at) => (typeof at === 'string' ? answering : undefined);
8
+ export function attached() {
9
+ return answering !== undefined;
10
+ }
11
+ export function kind(at) {
12
+ const one = answering;
13
+ if (one !== undefined)
14
+ return one.kind(at);
15
+ const held = disk.statSync(at, { throwIfNoEntry: false });
16
+ if (held === undefined)
17
+ return 'none';
18
+ return held.isFile() ? 'file' : held.isDirectory() ? 'folder' : 'other';
19
+ }
20
+ export function existsSync(at) {
21
+ return typeof at === 'string' ? kind(at) !== 'none' : disk.existsSync(at);
22
+ }
23
+ export function readFileSync(at, how) {
24
+ const one = asked(at);
25
+ return one === undefined ? disk.readFileSync(at, how ?? 'utf8') : one.read(at);
26
+ }
27
+ export function writeFileSync(at, said, how) {
28
+ const one = asked(at);
29
+ if (one !== undefined) {
30
+ one.write(at, said);
31
+ return;
32
+ }
33
+ if (typeof at === 'string')
34
+ disk.mkdirSync(dirname(at), { recursive: true });
35
+ disk.writeFileSync(at, said, how ?? 'utf8');
36
+ }
37
+ export function readdirSync(at, how) {
38
+ const one = asked(at);
39
+ if (one === undefined)
40
+ return how === undefined ? disk.readdirSync(at) : disk.readdirSync(at, how);
41
+ const held = one.list(at);
42
+ return how === undefined
43
+ ? held.map((each) => each.name)
44
+ : held.map((each) => ({ name: each.name, isFile: () => each.kind === 'file', isDirectory: () => each.kind === 'folder' }));
45
+ }
46
+ export function statSync(at, how) {
47
+ const one = asked(at);
48
+ if (one === undefined)
49
+ return (how === undefined ? disk.statSync(at) : disk.statSync(at, how));
50
+ const path = at;
51
+ const held = one.kind(path);
52
+ if (held === 'none') {
53
+ if (how !== undefined)
54
+ return undefined;
55
+ throw new Error(`${path} is not there, so nothing can be said about it.`);
56
+ }
57
+ const measured = one.stat(path);
58
+ return {
59
+ name: path,
60
+ size: measured.size,
61
+ modified: measured.modified,
62
+ mtimeMs: measured.modified,
63
+ isFile: () => held === 'file',
64
+ isDirectory: () => held === 'folder',
65
+ };
66
+ }
67
+ export function rmSync(at, how) {
68
+ const one = asked(at);
69
+ if (one === undefined)
70
+ disk.rmSync(at, how);
71
+ else
72
+ one.remove(at);
73
+ }
74
+ export function renameSync(from, to) {
75
+ const one = asked(from);
76
+ if (one === undefined) {
77
+ disk.renameSync(from, to);
78
+ return;
79
+ }
80
+ one.write(to, one.read(from));
81
+ one.remove(from);
82
+ }
83
+ export function appendFileSync(at, said) {
84
+ const one = asked(at);
85
+ if (one === undefined) {
86
+ disk.appendFileSync(at, said);
87
+ return;
88
+ }
89
+ const path = at;
90
+ one.write(path, `${one.kind(path) === 'none' ? '' : one.read(path)}${said}`);
91
+ }
92
+ export function realpathSync(at) {
93
+ const one = asked(at);
94
+ return one === undefined ? disk.realpathSync(at) : one.real(at);
95
+ }
@@ -0,0 +1,137 @@
1
+ import * as disk from 'node:fs'
2
+ import { dirname } from 'node:path'
3
+
4
+ export type Kind = 'file' | 'folder' | 'other' | 'none'
5
+
6
+ export interface Listed {
7
+ readonly name: string
8
+ readonly kind: Exclude<Kind, 'none'>
9
+ }
10
+
11
+ export interface Measured {
12
+ readonly size: number
13
+ readonly modified: number
14
+ }
15
+
16
+ export interface Answering {
17
+ readonly kind: (at: string) => Kind
18
+ readonly read: (at: string) => string
19
+ readonly write: (at: string, said: string) => void
20
+ readonly list: (at: string) => readonly Listed[]
21
+ readonly stat: (at: string) => Measured
22
+ readonly remove: (at: string) => void
23
+ readonly real: (at: string) => string
24
+ }
25
+
26
+ let answering: Answering | undefined
27
+
28
+ export function answeredBy(one: Answering | undefined): void {
29
+ answering = one
30
+ }
31
+
32
+ const asked = (at: unknown): Answering | undefined => (typeof at === 'string' ? answering : undefined)
33
+
34
+ export function attached(): boolean {
35
+ return answering !== undefined
36
+ }
37
+
38
+ export function kind(at: string): Kind {
39
+ const one = answering
40
+ if (one !== undefined) return one.kind(at)
41
+ const held = disk.statSync(at, { throwIfNoEntry: false })
42
+ if (held === undefined) return 'none'
43
+ return held.isFile() ? 'file' : held.isDirectory() ? 'folder' : 'other'
44
+ }
45
+
46
+ export function existsSync(at: disk.PathLike): boolean {
47
+ return typeof at === 'string' ? kind(at) !== 'none' : disk.existsSync(at)
48
+ }
49
+
50
+ export function readFileSync(at: disk.PathOrFileDescriptor, how?: BufferEncoding): string {
51
+ const one = asked(at)
52
+ return one === undefined ? disk.readFileSync(at, how ?? 'utf8') : one.read(at as string)
53
+ }
54
+
55
+ export function writeFileSync(at: disk.PathOrFileDescriptor, said: string, how?: BufferEncoding): void {
56
+ const one = asked(at)
57
+ if (one !== undefined) {
58
+ one.write(at as string, said)
59
+ return
60
+ }
61
+ if (typeof at === 'string') disk.mkdirSync(dirname(at), { recursive: true })
62
+ disk.writeFileSync(at, said, how ?? 'utf8')
63
+ }
64
+
65
+ export interface Named {
66
+ readonly name: string
67
+ readonly isFile: () => boolean
68
+ readonly isDirectory: () => boolean
69
+ }
70
+
71
+ export function readdirSync(at: disk.PathLike): string[]
72
+ export function readdirSync(at: disk.PathLike, how: { readonly withFileTypes: true }): readonly Named[]
73
+ export function readdirSync(at: disk.PathLike, how?: { readonly withFileTypes: true }): string[] | readonly Named[] {
74
+ const one = asked(at)
75
+ if (one === undefined) return how === undefined ? disk.readdirSync(at) : disk.readdirSync(at, how)
76
+ const held = one.list(at as string)
77
+ return how === undefined
78
+ ? held.map((each) => each.name)
79
+ : held.map((each) => ({ name: each.name, isFile: () => each.kind === 'file', isDirectory: () => each.kind === 'folder' }))
80
+ }
81
+
82
+ export interface Held extends Named, Measured {
83
+ readonly mtimeMs: number
84
+ }
85
+
86
+ export function statSync(at: disk.PathLike): Held
87
+ export function statSync(at: disk.PathLike, how: { readonly throwIfNoEntry: false }): Held | undefined
88
+ export function statSync(at: disk.PathLike, how?: { readonly throwIfNoEntry: false }): Held | undefined {
89
+ const one = asked(at)
90
+ if (one === undefined) return (how === undefined ? disk.statSync(at) : disk.statSync(at, how)) as Held | undefined
91
+ const path = at as string
92
+ const held = one.kind(path)
93
+ if (held === 'none') {
94
+ if (how !== undefined) return undefined
95
+ throw new Error(`${path} is not there, so nothing can be said about it.`)
96
+ }
97
+ const measured = one.stat(path)
98
+ return {
99
+ name: path,
100
+ size: measured.size,
101
+ modified: measured.modified,
102
+ mtimeMs: measured.modified,
103
+ isFile: () => held === 'file',
104
+ isDirectory: () => held === 'folder',
105
+ }
106
+ }
107
+
108
+ export function rmSync(at: disk.PathLike, how?: disk.RmOptions): void {
109
+ const one = asked(at)
110
+ if (one === undefined) disk.rmSync(at, how)
111
+ else one.remove(at as string)
112
+ }
113
+
114
+ export function renameSync(from: disk.PathLike, to: disk.PathLike): void {
115
+ const one = asked(from)
116
+ if (one === undefined) {
117
+ disk.renameSync(from, to)
118
+ return
119
+ }
120
+ one.write(to as string, one.read(from as string))
121
+ one.remove(from as string)
122
+ }
123
+
124
+ export function appendFileSync(at: disk.PathOrFileDescriptor, said: string): void {
125
+ const one = asked(at)
126
+ if (one === undefined) {
127
+ disk.appendFileSync(at, said)
128
+ return
129
+ }
130
+ const path = at as string
131
+ one.write(path, `${one.kind(path) === 'none' ? '' : one.read(path)}${said}`)
132
+ }
133
+
134
+ export function realpathSync(at: disk.PathLike): string {
135
+ const one = asked(at)
136
+ return one === undefined ? disk.realpathSync(at as string) : one.real(at as string)
137
+ }
@@ -6,6 +6,7 @@ export declare class AlreadyThreadedError extends Error {
6
6
  }
7
7
  export declare const COMPILER = "tsconfig.json";
8
8
  export declare const LOCKFILES: readonly ["package-lock.json", "pnpm-lock.yaml", "yarn.lock", "bun.lockb"];
9
+ export declare function manager(into: string): readonly string[];
9
10
  export declare const MANIFEST = "package.json";
10
11
  export declare const REGISTRY: string;
11
12
  export declare const IGNORED: string;
package/dist/core/init.js CHANGED
@@ -1,4 +1,4 @@
1
- import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
1
+ import { existsSync, readFileSync, writeFileSync } from "./files.js";
2
2
  import { join, relative, resolve } from 'node:path';
3
3
  import { quoted } from "./rows.js";
4
4
  export const TREE = '_threads';
@@ -15,6 +15,15 @@ export class AlreadyThreadedError extends Error {
15
15
  }
16
16
  export const COMPILER = 'tsconfig.json';
17
17
  export const LOCKFILES = ['package-lock.json', 'pnpm-lock.yaml', 'yarn.lock', 'bun.lockb'];
18
+ export function manager(into) {
19
+ if (existsSync(join(into, 'pnpm-lock.yaml')))
20
+ return ['pnpm', 'add'];
21
+ if (existsSync(join(into, 'yarn.lock')))
22
+ return ['yarn', 'add'];
23
+ if (existsSync(join(into, 'bun.lockb')))
24
+ return ['bun', 'add'];
25
+ return ['npm', 'install'];
26
+ }
18
27
  export const MANIFEST = 'package.json';
19
28
  const MODULE = 'module';
20
29
  export const REGISTRY = '.npm' + 'rc';
@@ -104,7 +113,6 @@ export function init(into) {
104
113
  const at = join(dir, ROOT);
105
114
  if (existsSync(at))
106
115
  throw new AlreadyThreadedError(relative(into, at));
107
- mkdirSync(dir, { recursive: true });
108
116
  const written = [];
109
117
  const held = join(into, COMPILER);
110
118
  if (!existsSync(held)) {
package/dist/core/init.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs'
1
+ import { existsSync, readFileSync, writeFileSync } from './files.ts'
2
2
  import { join, relative, resolve } from 'node:path'
3
3
 
4
4
  import { quoted } from './rows.ts'
@@ -24,6 +24,13 @@ export const COMPILER = 'tsconfig.json'
24
24
 
25
25
  export const LOCKFILES = ['package-lock.json', 'pnpm-lock.yaml', 'yarn.lock', 'bun.lockb'] as const
26
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
+
27
34
  export const MANIFEST = 'package.json'
28
35
 
29
36
  const MODULE = 'module'
@@ -134,7 +141,6 @@ export function init(into: string): readonly string[] {
134
141
  const dir = resolve(into, TREE)
135
142
  const at = join(dir, ROOT)
136
143
  if (existsSync(at)) throw new AlreadyThreadedError(relative(into, at))
137
- mkdirSync(dir, { recursive: true })
138
144
  const written: string[] = []
139
145
 
140
146
  const held = join(into, COMPILER)
@@ -21,7 +21,7 @@ export const BESIDE = (name) => `${name}${SOURCE ? '.ts' : '.js'}`;
21
21
  export const LOADER = SOURCE ? ['--import', 'tsx'] : [];
22
22
  export const CLI = [
23
23
  ...LOADER,
24
- join(dirname(fileURLToPath(import.meta.url)), BESIDE('cli')),
24
+ join(dirname(fileURLToPath(import.meta.url)), '..', '..', 'cli', BESIDE('cli')),
25
25
  ];
26
26
  export const REFUSED = Symbol.for('thread.refused');
27
27
  let sharing = false;
@@ -24,7 +24,7 @@ export const LOADER: readonly string[] = SOURCE ? ['--import', 'tsx'] : []
24
24
 
25
25
  export const CLI: readonly string[] = [
26
26
  ...LOADER,
27
- join(dirname(fileURLToPath(import.meta.url)), BESIDE('cli')),
27
+ join(dirname(fileURLToPath(import.meta.url)), '..', '..', 'cli', BESIDE('cli')),
28
28
  ]
29
29
 
30
30
  export const REFUSED = Symbol.for('thread.refused')
@@ -18,6 +18,8 @@ export declare function withRequires(text: string, where: Bounds, id: string, re
18
18
  export declare function seedsIn(text: string, where: Bounds, id: string): Bounds | undefined;
19
19
  export declare function afterRow(text: string, where: Bounds, follows: string, row: string): string | undefined;
20
20
  export declare const quoted: (one: string) => string;
21
+ export declare function valuesIn(text: string): Readonly<Record<string, unknown>>;
22
+ export declare function unread(text: string): readonly string[];
21
23
  export declare const STATED: string[];
22
24
  export declare function reconciled(held: string, source: string, fields: readonly string[]): string;
23
25
  export declare function fieldIn(text: string, where: Bounds, id: string, field: string): string | undefined;
package/dist/core/rows.js CHANGED
@@ -239,6 +239,71 @@ function stated(node, field) {
239
239
  return undefined;
240
240
  }
241
241
  export const quoted = (one) => `'${one.replace(/\\/g, '\\\\').replace(/'/g, "\\'").replace(/\n/g, '\\n')}'`;
242
+ const UNREAD = Symbol.for('thread.unread');
243
+ function valued(node) {
244
+ if (ts.isStringLiteral(node) || ts.isNoSubstitutionTemplateLiteral(node))
245
+ return node.text;
246
+ if (ts.isNumericLiteral(node))
247
+ return Number(node.text);
248
+ if (node.kind === ts.SyntaxKind.TrueKeyword)
249
+ return true;
250
+ if (node.kind === ts.SyntaxKind.FalseKeyword)
251
+ return false;
252
+ if (node.kind === ts.SyntaxKind.NullKeyword)
253
+ return null;
254
+ if (ts.isAsExpression(node) || ts.isSatisfiesExpression(node) || ts.isParenthesizedExpression(node))
255
+ return valued(node.expression);
256
+ if (ts.isPrefixUnaryExpression(node) && node.operator === ts.SyntaxKind.MinusToken) {
257
+ const held = valued(node.operand);
258
+ return typeof held === 'number' ? -held : UNREAD;
259
+ }
260
+ if (ts.isArrayLiteralExpression(node)) {
261
+ const held = [];
262
+ for (const one of node.elements) {
263
+ const each = valued(one);
264
+ if (each === UNREAD)
265
+ return UNREAD;
266
+ held.push(each);
267
+ }
268
+ return held;
269
+ }
270
+ if (ts.isObjectLiteralExpression(node)) {
271
+ const held = {};
272
+ for (const one of node.properties) {
273
+ if (!ts.isPropertyAssignment(one))
274
+ return UNREAD;
275
+ const key = keyed(one);
276
+ if (key === undefined)
277
+ return UNREAD;
278
+ const each = valued(one.initializer);
279
+ if (each === UNREAD)
280
+ return UNREAD;
281
+ held[key] = each;
282
+ }
283
+ return held;
284
+ }
285
+ return UNREAD;
286
+ }
287
+ function taken(text) {
288
+ const found = [];
289
+ walk(tree(text), (node) => {
290
+ if (!ts.isVariableDeclaration(node) || node.initializer === undefined || !ts.isIdentifier(node.name))
291
+ return;
292
+ found.push([node.name.text, valued(node.initializer)]);
293
+ });
294
+ return found;
295
+ }
296
+ export function valuesIn(text) {
297
+ const found = {};
298
+ for (const [name, held] of taken(text)) {
299
+ if (held !== UNREAD)
300
+ found[name] = held;
301
+ }
302
+ return found;
303
+ }
304
+ export function unread(text) {
305
+ return taken(text).filter(([, held]) => held === UNREAD).map(([name]) => name);
306
+ }
242
307
  export const STATED = ['at', 'describes', 'version'];
243
308
  export function reconciled(held, source, fields) {
244
309
  const ships = headed(source);