@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.
- package/CHANGELOG.md +310 -0
- package/dist/core/{asking.d.ts → answers.d.ts} +6 -14
- package/dist/core/{asking.js → answers.js} +13 -332
- package/dist/core/{asking.ts → answers.ts} +14 -357
- package/dist/core/attach.js +2 -2
- package/dist/core/attach.ts +2 -3
- package/dist/core/chain.js +4 -11
- package/dist/core/chain.ts +4 -3
- package/dist/core/client.d.ts +57 -0
- package/dist/core/client.js +243 -0
- package/dist/core/client.ts +279 -0
- package/dist/core/descend.d.ts +2 -0
- package/dist/core/descend.js +109 -16
- package/dist/core/descend.ts +120 -16
- package/dist/core/files.d.ts +45 -0
- package/dist/core/files.js +145 -0
- package/dist/core/files.ts +193 -0
- package/dist/core/init.d.ts +1 -0
- package/dist/core/init.js +10 -2
- package/dist/core/init.ts +8 -2
- package/dist/core/invoked.js +1 -1
- package/dist/core/invoked.ts +1 -1
- package/dist/core/rows.d.ts +2 -0
- package/dist/core/rows.js +65 -0
- package/dist/core/rows.ts +58 -0
- package/dist/core/tree.js +1 -1
- package/dist/core/tree.ts +1 -1
- package/dist/derivation/attach.js +0 -12
- package/dist/derivation/attach.ts +0 -12
- package/dist/derivation/generators/build.js +8 -7
- package/dist/derivation/generators/build.ts +8 -7
- package/dist/derivation/generators/release.d.ts +1 -1
- package/dist/derivation/generators/release.js +15 -23
- package/dist/derivation/generators/release.ts +15 -15
- package/dist/integrations/attach.js +1 -19
- package/dist/integrations/attach.ts +1 -19
- package/dist/integrations/generators/read.js +2 -2
- package/dist/integrations/generators/read.ts +4 -4
- package/dist/integrations/generators/vault.js +4 -12
- package/dist/integrations/generators/vault.ts +4 -4
- package/dist/integrations/module.js +5 -3
- package/dist/integrations/module.ts +5 -3
- package/dist/integrations/vault.js +5 -5
- package/dist/integrations/vault.ts +5 -5
- package/dist/integrity/attach.js +0 -12
- package/dist/integrity/attach.ts +0 -12
- package/dist/integrity/check-threads.js +1 -1
- package/dist/integrity/check-threads.ts +1 -1
- package/dist/integrity/constraint.d.ts +11 -2
- package/dist/integrity/constraint.js +163 -20
- package/dist/integrity/constraint.ts +174 -22
- package/dist/integrity/generators/compiler.js +7 -1
- package/dist/integrity/generators/compiler.ts +10 -1
- package/dist/integrity/generators/gate.js +23 -18
- package/dist/integrity/generators/gate.ts +29 -9
- package/dist/integrity/generators/publish.js +14 -26
- package/dist/integrity/generators/publish.ts +14 -17
- package/dist/integrity/generators/trial.d.ts +2 -2
- package/dist/integrity/generators/trial.js +108 -50
- package/dist/integrity/generators/trial.ts +107 -48
- package/dist/integrity/lint.js +3 -3
- package/dist/integrity/lint.ts +3 -3
- package/dist/integrity/lsp/diagnostics.js +1 -1
- package/dist/integrity/lsp/diagnostics.ts +1 -1
- package/dist/integrity/lsp/lint.js +1 -1
- package/dist/integrity/lsp/lint.ts +1 -1
- package/dist/integrity/slopnator.d.ts +1 -14
- package/dist/integrity/slopnator.js +8 -40
- package/dist/integrity/slopnator.ts +9 -51
- package/dist/integrity/slopnators/sl_broken_lineage.js +4 -2
- package/dist/integrity/slopnators/sl_broken_lineage.ts +4 -2
- package/dist/integrity/slopnators/sl_declared_elsewhere.js +24 -3
- package/dist/integrity/slopnators/sl_declared_elsewhere.ts +24 -3
- package/dist/integrity/slopnators/sl_derived_drift.js +10 -4
- package/dist/integrity/slopnators/sl_derived_drift.ts +10 -4
- package/dist/integrity/slopnators/sl_inert_island.js +7 -13
- package/dist/integrity/slopnators/sl_inert_island.ts +7 -16
- package/dist/integrity/slopnators/sl_inert_slopnator.js +8 -8
- package/dist/integrity/slopnators/sl_inert_slopnator.ts +8 -8
- package/dist/integrity/slopnators/sl_missing_row.js +14 -9
- package/dist/integrity/slopnators/sl_missing_row.ts +14 -10
- package/dist/integrity/slopnators/sl_no_child_process.js +8 -8
- package/dist/integrity/slopnators/sl_no_child_process.ts +8 -8
- package/dist/integrity/slopnators/sl_no_import_cycle.js +1 -1
- package/dist/integrity/slopnators/sl_no_import_cycle.ts +1 -1
- package/dist/integrity/slopnators/sl_no_such_command.js +20 -5
- package/dist/integrity/slopnators/sl_no_such_command.ts +22 -5
- package/dist/integrity/slopnators/sl_only_takes_a_literal.js +8 -8
- package/dist/integrity/slopnators/sl_only_takes_a_literal.ts +8 -8
- package/dist/integrity/slopnators/sl_stale_path.js +91 -18
- package/dist/integrity/slopnators/sl_stale_path.ts +97 -18
- package/dist/integrity/slopnators/sl_twice_meant.js +25 -2
- package/dist/integrity/slopnators/sl_twice_meant.ts +28 -2
- package/dist/integrity/slopnators/sl_twice_parented.js +26 -5
- package/dist/integrity/slopnators/sl_twice_parented.ts +26 -5
- package/dist/integrity/slopnators/sl_undeclared_node.js +18 -2
- package/dist/integrity/slopnators/sl_undeclared_node.ts +18 -2
- package/dist/integrity/slopnators/sl_undeclared_operation.js +3 -1
- package/dist/integrity/slopnators/sl_undeclared_operation.ts +3 -1
- package/dist/integrity/slopnators/sl_undeclared_variant.js +6 -2
- package/dist/integrity/slopnators/sl_undeclared_variant.ts +6 -2
- package/dist/integrity/slopnators/sl_ungoverned_island.js +3 -4
- package/dist/integrity/slopnators/sl_ungoverned_island.ts +3 -4
- package/dist/integrity/slopnators/sl_unkept_rows.js +1 -1
- package/dist/integrity/slopnators/sl_unkept_rows.ts +1 -1
- package/dist/integrity/slopnators/sl_unpaired_member.js +13 -8
- package/dist/integrity/slopnators/sl_unpaired_member.ts +16 -8
- package/dist/integrity/slopnators/sl_unpaired_slopnator.js +16 -3
- package/dist/integrity/slopnators/sl_unpaired_slopnator.ts +16 -3
- package/dist/integrity/slopnators/sl_unthreaded.d.ts +1 -0
- package/dist/integrity/slopnators/sl_unthreaded.js +18 -10
- package/dist/integrity/slopnators/sl_unthreaded.ts +19 -11
- package/dist/integrity/slopnators/sl_untranslated_key.js +1 -1
- package/dist/integrity/slopnators/sl_untranslated_key.ts +1 -1
- package/dist/integrity/slopnators/sl_unversioned.js +16 -2
- package/dist/integrity/slopnators/sl_unversioned.ts +16 -2
- package/dist/integrity/sweep.js +17 -3
- package/dist/integrity/sweep.ts +20 -3
- package/dist/knowledge/attach.js +0 -6
- package/dist/knowledge/attach.ts +0 -6
- package/dist/knowledge/generators/docsgen.js +12 -3
- package/dist/knowledge/generators/docsgen.ts +12 -2
- package/dist/knowledge/generators/glossary.js +1 -1
- package/dist/knowledge/generators/glossary.ts +1 -1
- package/dist/operations/attach.js +2 -26
- package/dist/operations/attach.ts +2 -27
- package/dist/operations/dispatch.js +2 -3
- package/dist/operations/dispatch.ts +2 -3
- package/dist/operations/generators/capability.js +4 -7
- package/dist/operations/generators/capability.ts +3 -6
- package/dist/operations/generators/compile.js +8 -13
- package/dist/operations/generators/compile.ts +8 -4
- package/dist/operations/generators/permits.js +4 -13
- package/dist/operations/generators/permits.ts +3 -3
- package/dist/operations/generators/seed.js +5 -2
- package/dist/operations/generators/seed.ts +6 -2
- package/dist/operations/generators/trial.js +10 -10
- package/dist/operations/generators/trial.ts +10 -10
- package/dist/operations/http.js +4 -1
- package/dist/operations/http.ts +6 -1
- package/package.json +2 -2
- package/dist/core/cli.d.ts +0 -2
- package/dist/core/cli.js +0 -1761
- package/dist/core/cli.ts +0 -2004
- package/dist/core/generators/meta.d.ts +0 -2
- package/dist/core/generators/meta.js +0 -829
- package/dist/core/generators/meta.ts +0 -1006
- package/dist/derivation/generators/derivation.d.ts +0 -3
- package/dist/derivation/generators/derivation.js +0 -273
- package/dist/derivation/generators/derivation.ts +0 -347
- package/dist/derivation/makers/release.d.ts +0 -1
- package/dist/derivation/makers/release.js +0 -107
- package/dist/derivation/makers/release.ts +0 -135
- package/dist/integrations/generators/secret.d.ts +0 -1
- package/dist/integrations/generators/secret.js +0 -233
- package/dist/integrations/generators/secret.ts +0 -289
- package/dist/integrations/generators/target.d.ts +0 -1
- package/dist/integrations/generators/target.js +0 -775
- package/dist/integrations/generators/target.ts +0 -944
- package/dist/integrity/generators/rule.d.ts +0 -2
- package/dist/integrity/generators/rule.js +0 -213
- package/dist/integrity/generators/rule.ts +0 -271
- package/dist/integrity/generators/seal.d.ts +0 -4
- package/dist/integrity/generators/seal.js +0 -101
- package/dist/integrity/generators/seal.ts +0 -117
- package/dist/integrity/sealed.d.ts +0 -1
- package/dist/integrity/sealed.js +0 -56
- package/dist/integrity/sealed.ts +0 -56
- package/dist/knowledge/generators/skills.d.ts +0 -1
- package/dist/knowledge/generators/skills.js +0 -222
- package/dist/knowledge/generators/skills.ts +0 -263
- package/dist/operations/generators/grant.d.ts +0 -1
- package/dist/operations/generators/grant.js +0 -267
- package/dist/operations/generators/grant.ts +0 -336
- package/dist/operations/generators/operation.d.ts +0 -1
- package/dist/operations/generators/operation.js +0 -811
- package/dist/operations/generators/operation.ts +0 -972
- package/dist/operations/makers/capability.d.ts +0 -1
- package/dist/operations/makers/capability.js +0 -117
- package/dist/operations/makers/capability.ts +0 -166
- package/dist/operations/makers/trial.d.ts +0 -1
- package/dist/operations/makers/trial.js +0 -413
- 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
|
+
}
|
package/dist/core/init.d.ts
CHANGED
|
@@ -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,
|
|
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,
|
|
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)
|
package/dist/core/invoked.js
CHANGED
|
@@ -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;
|
package/dist/core/invoked.ts
CHANGED
|
@@ -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')
|
package/dist/core/rows.d.ts
CHANGED
|
@@ -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
package/dist/core/tree.ts
CHANGED
|
@@ -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
|
-
|
|
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 = (
|
|
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
|
-
|
|
73
|
-
const 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 =
|
|
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 ?
|
|
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
|
-
|
|
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 = (
|
|
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
|
-
|
|
74
|
-
const 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 =
|
|
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 ?
|
|
197
|
+
const refusals = forBrowser ? withheld(pass) : new Map<string, string>()
|
|
197
198
|
|
|
198
199
|
const held = await build({
|
|
199
200
|
entryPoints: [resolve(ROOT, entry)],
|