@prestack/thread 0.0.3 → 0.1.1

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 +310 -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 +243 -0
  11. package/dist/core/client.ts +279 -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 +145 -0
  17. package/dist/core/files.ts +193 -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 +7 -1
  53. package/dist/integrity/generators/compiler.ts +10 -1
  54. package/dist/integrity/generators/gate.js +23 -18
  55. package/dist/integrity/generators/gate.ts +29 -9
  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
@@ -0,0 +1,193 @@
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
+ const CHANNEL = 'THREAD_CHANNEL'
27
+
28
+ const WAITING = 180000
29
+
30
+ function overFiles(dir: string): Answering {
31
+ const idle = new Int32Array(new SharedArrayBuffer(4))
32
+ let spun = 0
33
+
34
+ const ask = (body: Readonly<Record<string, unknown>>): Readonly<Record<string, unknown>> => {
35
+ spun += 1
36
+ const name = `${dir}/${String(process.pid)}-${String(spun)}`
37
+ disk.writeFileSync(`${name}.asking`, JSON.stringify(body))
38
+ disk.renameSync(`${name}.asking`, `${name}.order`)
39
+ const until = Date.now() + WAITING
40
+ for (;;) {
41
+ let held: string | undefined
42
+ try {
43
+ held = disk.readFileSync(`${name}.answer`, 'utf8')
44
+ } catch {
45
+ held = undefined
46
+ }
47
+ if (held !== undefined) {
48
+ disk.rmSync(`${name}.answer`, { force: true })
49
+ const said = JSON.parse(held) as Readonly<Record<string, unknown>>
50
+ if (said.answered === 'refused') throw new Error(String(said.because))
51
+ return said
52
+ }
53
+ if (Date.now() > until)
54
+ throw new Error(
55
+ 'nothing answered ' +
56
+ String(body.order) +
57
+ ' ' +
58
+ String(body.at) +
59
+ ' for three minutes, so this stopped waiting rather than spinning forever. Whoever was answering is gone. It waits longer than a server waits before giving up on a run, on purpose: if an answer were still coming it would have arrived. Nothing here is half-written — one order is one file, and it was either answered or it was not.',
60
+ )
61
+ Atomics.wait(idle, 0, 0, 2)
62
+ }
63
+ }
64
+
65
+ return {
66
+ kind: (at) => ask({ order: 'kind', at }).held as Kind,
67
+ read: (at) => ask({ order: 'read', at }).said as string,
68
+ write: (at, said) => {
69
+ ask({ order: 'write', at, said })
70
+ },
71
+ list: (at) => ask({ order: 'list', at }).held as readonly Listed[],
72
+ stat: (at) => ask({ order: 'stat', at }).held as Measured,
73
+ remove: (at) => {
74
+ ask({ order: 'remove', at })
75
+ },
76
+ real: (at) => ask({ order: 'real', at }).at as string,
77
+ }
78
+ }
79
+
80
+ const WHERE = process.env[CHANNEL]
81
+
82
+ let answering: Answering | undefined = WHERE !== undefined && WHERE !== '' ? overFiles(WHERE) : undefined
83
+
84
+ export function answeredBy(one: Answering | undefined): void {
85
+ answering = one
86
+ }
87
+
88
+ const asked = (at: unknown): Answering | undefined => (typeof at === 'string' ? answering : undefined)
89
+
90
+ export function attached(): boolean {
91
+ return answering !== undefined
92
+ }
93
+
94
+ export function kind(at: string): Kind {
95
+ const one = answering
96
+ if (one !== undefined) return one.kind(at)
97
+ const held = disk.statSync(at, { throwIfNoEntry: false })
98
+ if (held === undefined) return 'none'
99
+ return held.isFile() ? 'file' : held.isDirectory() ? 'folder' : 'other'
100
+ }
101
+
102
+ export function existsSync(at: disk.PathLike): boolean {
103
+ return typeof at === 'string' ? kind(at) !== 'none' : disk.existsSync(at)
104
+ }
105
+
106
+ export function readFileSync(at: disk.PathOrFileDescriptor, how?: BufferEncoding): string {
107
+ const one = asked(at)
108
+ return one === undefined ? disk.readFileSync(at, how ?? 'utf8') : one.read(at as string)
109
+ }
110
+
111
+ export function writeFileSync(at: disk.PathOrFileDescriptor, said: string, how?: BufferEncoding): void {
112
+ const one = asked(at)
113
+ if (one !== undefined) {
114
+ one.write(at as string, said)
115
+ return
116
+ }
117
+ if (typeof at === 'string') disk.mkdirSync(dirname(at), { recursive: true })
118
+ disk.writeFileSync(at, said, how ?? 'utf8')
119
+ }
120
+
121
+ export interface Named {
122
+ readonly name: string
123
+ readonly isFile: () => boolean
124
+ readonly isDirectory: () => boolean
125
+ }
126
+
127
+ export function readdirSync(at: disk.PathLike): string[]
128
+ export function readdirSync(at: disk.PathLike, how: { readonly withFileTypes: true }): readonly Named[]
129
+ export function readdirSync(at: disk.PathLike, how?: { readonly withFileTypes: true }): string[] | readonly Named[] {
130
+ const one = asked(at)
131
+ if (one === undefined) return how === undefined ? disk.readdirSync(at) : disk.readdirSync(at, how)
132
+ const held = one.list(at as string)
133
+ return how === undefined
134
+ ? held.map((each) => each.name)
135
+ : held.map((each) => ({ name: each.name, isFile: () => each.kind === 'file', isDirectory: () => each.kind === 'folder' }))
136
+ }
137
+
138
+ export interface Held extends Named, Measured {
139
+ readonly mtimeMs: number
140
+ }
141
+
142
+ export function statSync(at: disk.PathLike): Held
143
+ export function statSync(at: disk.PathLike, how: { readonly throwIfNoEntry: false }): Held | undefined
144
+ export function statSync(at: disk.PathLike, how?: { readonly throwIfNoEntry: false }): Held | undefined {
145
+ const one = asked(at)
146
+ if (one === undefined) return (how === undefined ? disk.statSync(at) : disk.statSync(at, how)) as Held | undefined
147
+ const path = at as string
148
+ const held = one.kind(path)
149
+ if (held === 'none') {
150
+ if (how !== undefined) return undefined
151
+ throw new Error(`${path} is not there, so nothing can be said about it.`)
152
+ }
153
+ const measured = one.stat(path)
154
+ return {
155
+ name: path,
156
+ size: measured.size,
157
+ modified: measured.modified,
158
+ mtimeMs: measured.modified,
159
+ isFile: () => held === 'file',
160
+ isDirectory: () => held === 'folder',
161
+ }
162
+ }
163
+
164
+ export function rmSync(at: disk.PathLike, how?: disk.RmOptions): void {
165
+ const one = asked(at)
166
+ if (one === undefined) disk.rmSync(at, how)
167
+ else one.remove(at as string)
168
+ }
169
+
170
+ export function renameSync(from: disk.PathLike, to: disk.PathLike): void {
171
+ const one = asked(from)
172
+ if (one === undefined) {
173
+ disk.renameSync(from, to)
174
+ return
175
+ }
176
+ one.write(to as string, one.read(from as string))
177
+ one.remove(from as string)
178
+ }
179
+
180
+ export function appendFileSync(at: disk.PathOrFileDescriptor, said: string): void {
181
+ const one = asked(at)
182
+ if (one === undefined) {
183
+ disk.appendFileSync(at, said)
184
+ return
185
+ }
186
+ const path = at as string
187
+ one.write(path, `${one.kind(path) === 'none' ? '' : one.read(path)}${said}`)
188
+ }
189
+
190
+ export function realpathSync(at: disk.PathLike): string {
191
+ const one = asked(at)
192
+ return one === undefined ? disk.realpathSync(at as string) : one.real(at as string)
193
+ }
@@ -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);
package/dist/core/rows.ts CHANGED
@@ -252,6 +252,64 @@ function stated(node: ts.ObjectLiteralExpression, field: string): Bounds | undef
252
252
 
253
253
  export const quoted = (one: string): string => `'${one.replace(/\\/g, '\\\\').replace(/'/g, "\\'").replace(/\n/g, '\\n')}'`
254
254
 
255
+ const UNREAD = Symbol.for('thread.unread')
256
+
257
+ function valued(node: ts.Expression): unknown {
258
+ if (ts.isStringLiteral(node) || ts.isNoSubstitutionTemplateLiteral(node)) return node.text
259
+ if (ts.isNumericLiteral(node)) return Number(node.text)
260
+ if (node.kind === ts.SyntaxKind.TrueKeyword) return true
261
+ if (node.kind === ts.SyntaxKind.FalseKeyword) return false
262
+ if (node.kind === ts.SyntaxKind.NullKeyword) return null
263
+ if (ts.isAsExpression(node) || ts.isSatisfiesExpression(node) || ts.isParenthesizedExpression(node)) return valued(node.expression)
264
+ if (ts.isPrefixUnaryExpression(node) && node.operator === ts.SyntaxKind.MinusToken) {
265
+ const held = valued(node.operand)
266
+ return typeof held === 'number' ? -held : UNREAD
267
+ }
268
+ if (ts.isArrayLiteralExpression(node)) {
269
+ const held: unknown[] = []
270
+ for (const one of node.elements) {
271
+ const each = valued(one)
272
+ if (each === UNREAD) return UNREAD
273
+ held.push(each)
274
+ }
275
+ return held
276
+ }
277
+ if (ts.isObjectLiteralExpression(node)) {
278
+ const held: Record<string, unknown> = {}
279
+ for (const one of node.properties) {
280
+ if (!ts.isPropertyAssignment(one)) return UNREAD
281
+ const key = keyed(one)
282
+ if (key === undefined) return UNREAD
283
+ const each = valued(one.initializer)
284
+ if (each === UNREAD) return UNREAD
285
+ held[key] = each
286
+ }
287
+ return held
288
+ }
289
+ return UNREAD
290
+ }
291
+
292
+ function taken(text: string): readonly (readonly [string, unknown])[] {
293
+ const found: (readonly [string, unknown])[] = []
294
+ walk(tree(text), (node) => {
295
+ if (!ts.isVariableDeclaration(node) || node.initializer === undefined || !ts.isIdentifier(node.name)) return
296
+ found.push([node.name.text, valued(node.initializer)])
297
+ })
298
+ return found
299
+ }
300
+
301
+ export function valuesIn(text: string): Readonly<Record<string, unknown>> {
302
+ const found: Record<string, unknown> = {}
303
+ for (const [name, held] of taken(text)) {
304
+ if (held !== UNREAD) found[name] = held
305
+ }
306
+ return found
307
+ }
308
+
309
+ export function unread(text: string): readonly string[] {
310
+ return taken(text).filter(([, held]) => held === UNREAD).map(([name]) => name)
311
+ }
312
+
255
313
  export const STATED = ['at', 'describes', 'version']
256
314
 
257
315
  export function reconciled(held: string, source: string, fields: readonly string[]): string {
package/dist/core/tree.js CHANGED
@@ -1,4 +1,4 @@
1
- import { existsSync, readdirSync, statSync } from 'node:fs';
1
+ import { existsSync, readdirSync, statSync } from "./files.js";
2
2
  import { dirname, join, relative, resolve } from 'node:path';
3
3
  import { cwd } from 'node:process';
4
4
  const NAMES = ['_threads', 'threads'];
package/dist/core/tree.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { existsSync, readdirSync, statSync } from 'node:fs'
1
+ import { existsSync, readdirSync, statSync } from './files.ts'
2
2
  import { dirname, join, relative, resolve } from 'node:path'
3
3
  import { cwd } from 'node:process'
4
4
 
@@ -39,18 +39,6 @@ const SHIPPED = [
39
39
  id: 'release-generator',
40
40
  source: " {\n id: 'release-generator',\n at: 'node_modules/@prestack/thread/dist/derivation/generators/release.js',\n describes: 'The writer of what a host reads to run this app — it derives the config from what the app already declares, its own build and start commands and the names of every secret it says it must be given, and refuses to write while this app installs a package of this framework as a dependency, because what a host runs may hold nothing of the scaffolding that assembled it.',\n version: 1,\n by: 'thread release',\n runs: 'thread release --check',\n },",
41
41
  },
42
- {
43
- into: 'derivation.ts',
44
- list: 'GENERATORS',
45
- id: 'release-noun',
46
- source: " {\n id: 'release-noun',\n at: 'node_modules/@prestack/thread/dist/derivation/makers/release.js',\n describes: 'The command that declares where this app goes when it leaves your machine. It writes the row saying where this app goes and nothing else, because everything the config needs is already declared somewhere and asking for it again would be the app’s own facts typed a second time.',\n version: 1,\n by: 'thread create release',\n },",
47
- },
48
- {
49
- into: 'derivation.ts',
50
- list: 'GENERATORS',
51
- id: 'generator-noun',
52
- source: " {\n id: 'generator-noun',\n at: 'node_modules/@prestack/thread/dist/derivation/generators/derivation.js',\n describes: 'Declares a generator this app derives a file with. `thread create generator` asks what it is, where its file goes and the command that writes it, then writes the row and seeds the file beside it — so a generator arrives through the same wizard as everything else, and the command it names exists from that moment because the CLI reads every `by:` the tree states.',\n version: 1,\n by: 'thread create generator',\n },",
53
- },
54
42
  ];
55
43
  export const ATTACH = {
56
44
  attaches: SEEDED.attaches,
@@ -46,18 +46,6 @@ const SHIPPED: readonly { readonly into: string; readonly list: string; readonly
46
46
  id: 'release-generator',
47
47
  source: " {\n id: 'release-generator',\n at: 'node_modules/@prestack/thread/dist/derivation/generators/release.js',\n describes: 'The writer of what a host reads to run this app — it derives the config from what the app already declares, its own build and start commands and the names of every secret it says it must be given, and refuses to write while this app installs a package of this framework as a dependency, because what a host runs may hold nothing of the scaffolding that assembled it.',\n version: 1,\n by: 'thread release',\n runs: 'thread release --check',\n },",
48
48
  },
49
- {
50
- into: 'derivation.ts',
51
- list: 'GENERATORS',
52
- id: 'release-noun',
53
- source: " {\n id: 'release-noun',\n at: 'node_modules/@prestack/thread/dist/derivation/makers/release.js',\n describes: 'The command that declares where this app goes when it leaves your machine. It writes the row saying where this app goes and nothing else, because everything the config needs is already declared somewhere and asking for it again would be the app’s own facts typed a second time.',\n version: 1,\n by: 'thread create release',\n },",
54
- },
55
- {
56
- into: 'derivation.ts',
57
- list: 'GENERATORS',
58
- id: 'generator-noun',
59
- source: " {\n id: 'generator-noun',\n at: 'node_modules/@prestack/thread/dist/derivation/generators/derivation.js',\n describes: 'Declares a generator this app derives a file with. `thread create generator` asks what it is, where its file goes and the command that writes it, then writes the row and seeds the file beside it — so a generator arrives through the same wizard as everything else, and the command it names exists from that moment because the CLI reads every `by:` the tree states.',\n version: 1,\n by: 'thread create generator',\n },",
60
- },
61
49
  ]
62
50
  export const ATTACH: Attaching = {
63
51
  attaches: SEEDED.attaches,
@@ -6,13 +6,14 @@ var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExte
6
6
  }
7
7
  return path;
8
8
  };
9
- import { existsSync, readFileSync } from 'node:fs';
10
9
  import { relative, resolve } from 'node:path';
11
10
  import { argv, stderr, stdout } from 'node:process';
12
11
  import { pathToFileURL } from 'node:url';
13
12
  import { build } from 'esbuild';
14
13
  import { descend, reached } from "../../core/descend.js";
14
+ import { existsSync, readFileSync } from "../../core/files.js";
15
15
  import { exit, invoked } from "../../core/invoked.js";
16
+ import { valuesIn } from "../../core/rows.js";
16
17
  import { findTree } from "../../core/tree.js";
17
18
  import { servesBrowser } from "../../integrations/target.js";
18
19
  const TREE = findTree();
@@ -57,23 +58,23 @@ const JSX = /\.(c|m)?(t|j)sx$/;
57
58
  function browsing(entry) {
58
59
  return JSX.test(entry);
59
60
  }
60
- async function sheet(file, list) {
61
+ function sheet(file, list) {
61
62
  const path = resolve(ROOT, `${TREE.name}/${file}`);
62
63
  if (!existsSync(path))
63
64
  return [];
64
65
  try {
65
- const held = (await import(__rewriteRelativeImportExtension(pathToFileURL(path).href)));
66
+ const held = valuesIn(readFileSync(path, 'utf8'));
66
67
  return held[list] ?? [];
67
68
  }
68
69
  catch {
69
70
  return [];
70
71
  }
71
72
  }
72
- async function withheld(pass) {
73
- const targets = await sheet('targets.ts', 'TARGETS');
73
+ function withheld(pass) {
74
+ const targets = sheet('targets.ts', 'TARGETS');
74
75
  if (targets.length === 0)
75
76
  return new Map();
76
- const secrets = await sheet('secrets.ts', 'SECRETS');
77
+ const secrets = sheet('secrets.ts', 'SECRETS');
77
78
  const because = new Map();
78
79
  for (const one of targets) {
79
80
  const said = servesBrowser(one, secrets);
@@ -184,7 +185,7 @@ async function main() {
184
185
  exit(1);
185
186
  }
186
187
  const forBrowser = browsing(entry);
187
- const refusals = forBrowser ? await withheld(pass) : new Map();
188
+ const refusals = forBrowser ? withheld(pass) : new Map();
188
189
  const held = await build({
189
190
  entryPoints: [resolve(ROOT, entry)],
190
191
  bundle: true,
@@ -1,4 +1,3 @@
1
- import { existsSync, readFileSync } from 'node:fs'
2
1
  import { relative, resolve } from 'node:path'
3
2
  import { argv, stderr, stdout } from 'node:process'
4
3
  import { pathToFileURL } from 'node:url'
@@ -6,7 +5,9 @@ import { pathToFileURL } from 'node:url'
6
5
  import { build } from 'esbuild'
7
6
 
8
7
  import { descend, reached, type Pass } from '../../core/descend.ts'
8
+ import { existsSync, readFileSync } from '../../core/files.ts'
9
9
  import { exit, invoked } from '../../core/invoked.ts'
10
+ import { valuesIn } from '../../core/rows.ts'
10
11
  import { findTree } from '../../core/tree.ts'
11
12
  import type { Secret } from '../../integrations/secret.ts'
12
13
  import { servesBrowser, type Target } from '../../integrations/target.ts'
@@ -59,21 +60,21 @@ function browsing(entry: string): boolean {
59
60
  return JSX.test(entry)
60
61
  }
61
62
 
62
- async function sheet<T>(file: string, list: string): Promise<readonly T[]> {
63
+ function sheet<T>(file: string, list: string): readonly T[] {
63
64
  const path = resolve(ROOT, `${TREE.name}/${file}`)
64
65
  if (!existsSync(path)) return []
65
66
  try {
66
- const held = (await import(pathToFileURL(path).href)) as Record<string, readonly T[] | undefined>
67
+ const held = valuesIn(readFileSync(path, 'utf8')) as Record<string, readonly T[] | undefined>
67
68
  return held[list] ?? []
68
69
  } catch {
69
70
  return []
70
71
  }
71
72
  }
72
73
 
73
- async function withheld(pass: Pass): Promise<ReadonlyMap<string, string>> {
74
- const targets = await sheet<Target>('targets.ts', 'TARGETS')
74
+ function withheld(pass: Pass): ReadonlyMap<string, string> {
75
+ const targets = sheet<Target>('targets.ts', 'TARGETS')
75
76
  if (targets.length === 0) return new Map()
76
- const secrets = await sheet<Secret>('secrets.ts', 'SECRETS')
77
+ const secrets = sheet<Secret>('secrets.ts', 'SECRETS')
77
78
 
78
79
  const because = new Map<string, string>()
79
80
  for (const one of targets) {
@@ -193,7 +194,7 @@ async function main(): Promise<void> {
193
194
  }
194
195
 
195
196
  const forBrowser = browsing(entry)
196
- const refusals = forBrowser ? await withheld(pass) : new Map<string, string>()
197
+ const refusals = forBrowser ? withheld(pass) : new Map<string, string>()
197
198
 
198
199
  const held = await build({
199
200
  entryPoints: [resolve(ROOT, entry)],
@@ -1,3 +1,3 @@
1
1
  import { CHECKED_BY, WRITES } from '../release.ts';
2
- declare function made(): Promise<ReadonlyMap<string, string>>;
2
+ declare function made(): ReadonlyMap<string, string>;
3
3
  export { CHECKED_BY, WRITES, made };