@prestack/thread 0.1.19 → 0.1.21
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 +170 -0
- package/dist/core/descend.d.ts +2 -0
- package/dist/core/descend.js +12 -0
- package/dist/core/descend.ts +13 -1
- package/dist/core/invoked.d.ts +1 -0
- package/dist/core/invoked.js +1 -0
- package/dist/core/invoked.ts +2 -0
- package/dist/core/rows.d.ts +5 -0
- package/dist/core/rows.js +45 -0
- package/dist/core/rows.ts +47 -0
- package/dist/integrations/cli.d.ts +4 -9
- package/dist/integrations/cli.js +31 -29
- package/dist/integrations/cli.ts +34 -41
- package/dist/integrations/declared.d.ts +8 -0
- package/dist/integrations/declared.js +22 -0
- package/dist/integrations/declared.ts +34 -0
- package/dist/integrations/target.d.ts +1 -1
- package/dist/integrations/target.js +20 -6
- package/dist/integrations/target.ts +22 -6
- package/dist/integrity/generators/publish.js +2 -1
- package/dist/integrity/generators/publish.ts +2 -1
- package/dist/integrity/slopnators/sl_unlisted_program.js +2 -2
- package/dist/integrity/slopnators/sl_unlisted_program.ts +2 -2
- package/dist/operations/generators/compile.d.ts +21 -0
- package/dist/operations/generators/compile.js +1 -1
- package/dist/operations/generators/compile.ts +1 -1
- package/dist/operations/generators/permits.d.ts +2 -1
- package/dist/operations/generators/permits.js +52 -47
- package/dist/operations/generators/permits.ts +66 -61
- package/package.json +1 -1
package/dist/integrations/cli.js
CHANGED
|
@@ -6,11 +6,10 @@ import { allowedNodeEnvironmentFlags, env, execPath, kill, pid, stderr, stdout }
|
|
|
6
6
|
import { fileURLToPath } from 'node:url';
|
|
7
7
|
import { CHANNEL, FOLDER, across, attached, createFileSync, existsSync, here, readFileSync, readdirSync, realpathSync, renameSync, rmSync, statSync, writeFileSync } from '../integrations/disk.js';
|
|
8
8
|
import { VAULT_DIR } from '../integrations/vault.js';
|
|
9
|
-
import {
|
|
9
|
+
import { LOCKFILES, ORDERS, PACKAGER, REACH, managerOf, programOf, unlisted } from '../integrations/target.js';
|
|
10
10
|
import { BESIDE, CLI } from '../core/invoked.js';
|
|
11
|
-
import { valuesIn } from '../core/rows.js';
|
|
12
11
|
import { THING } from '../core/scope.js';
|
|
13
|
-
import { findTree
|
|
12
|
+
import { findTree } from '../core/tree.js';
|
|
14
13
|
const answered = (went) => ({
|
|
15
14
|
there: went.error === undefined,
|
|
16
15
|
ok: went.status === 0,
|
|
@@ -141,7 +140,7 @@ export function claims(root, runs) {
|
|
|
141
140
|
return { by: { pid: 0, runs: '', since: Date.now() } };
|
|
142
141
|
}
|
|
143
142
|
export function programFor(name, at) {
|
|
144
|
-
const found = programOf(name, at, existsSync);
|
|
143
|
+
const found = programOf(name, at, existsSync, (path) => readFileSync(path, 'utf8'));
|
|
145
144
|
if (found === undefined)
|
|
146
145
|
throw new Error(unlisted(name));
|
|
147
146
|
return found;
|
|
@@ -170,6 +169,7 @@ export function signing() {
|
|
|
170
169
|
const OWN = resolve(dirname(fileURLToPath(import.meta.url)), '..', '..');
|
|
171
170
|
export const PROGRAM = REACH.cli + THING;
|
|
172
171
|
export const COPIES = THING + ' create copy';
|
|
172
|
+
export const COPY_AT = join(VAULT_DIR, THING + '.json');
|
|
173
173
|
const UNCOPIED = 'the thread command sits beside none of the framework code this server runs, because no package ships it, and the ' +
|
|
174
174
|
'folder this server was started from keeps no compiled copy of it. Run `' + COPIES + '` in that folder from a server ' +
|
|
175
175
|
'that runs the framework itself, and the next command here starts that copy.';
|
|
@@ -187,10 +187,10 @@ function startable() {
|
|
|
187
187
|
catch {
|
|
188
188
|
return UNCOPIED;
|
|
189
189
|
}
|
|
190
|
-
const copied = (
|
|
191
|
-
if (
|
|
190
|
+
const copied = resolve(root, COPY_AT);
|
|
191
|
+
if (!existsSync(copied))
|
|
192
192
|
return UNCOPIED;
|
|
193
|
-
const said = readFileSync(
|
|
193
|
+
const said = readFileSync(copied, 'utf8');
|
|
194
194
|
const own = join(root, CACHED, createHash('sha256').update(said).digest('hex').slice(0, 16));
|
|
195
195
|
const entry = join(own, 'cli', BESIDE('cli'));
|
|
196
196
|
if (!existsSync(entry)) {
|
|
@@ -245,6 +245,23 @@ const READS = new Set(Object.entries(ORDERS).filter(([order, answer]) => order =
|
|
|
245
245
|
function kept(one, asked, held) {
|
|
246
246
|
if (READS.has(asked.order) && held.answered !== 'refused')
|
|
247
247
|
one.known.set(told(asked), held);
|
|
248
|
+
if (asked.order !== 'write' || held.answered !== ORDERS.write)
|
|
249
|
+
return;
|
|
250
|
+
one.known.set(told({ order: 'read', at: asked.at }), { answered: 'read', said: asked.said ?? '' });
|
|
251
|
+
one.known.set(told({ order: 'kind', at: asked.at }), { answered: 'kind', found: 'file' });
|
|
252
|
+
}
|
|
253
|
+
const CHANGES = new Set(['write', 'remove']);
|
|
254
|
+
const within = (at, under) => under === '' || at === under || at.startsWith(under + '/');
|
|
255
|
+
function forgets(one, asked) {
|
|
256
|
+
if (!CHANGES.has(asked.order)) {
|
|
257
|
+
one.known.clear();
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
260
|
+
for (const key of [...one.known.keys()]) {
|
|
261
|
+
const [order = '', at = ''] = JSON.parse(key);
|
|
262
|
+
if (order === 'real' || within(at, asked.at) || within(asked.at, at))
|
|
263
|
+
one.known.delete(key);
|
|
264
|
+
}
|
|
248
265
|
}
|
|
249
266
|
const plainly = (one) => one.order + ' ' + (one.at === '' ? '.' : one.at);
|
|
250
267
|
const outside = (root, at) => {
|
|
@@ -299,7 +316,7 @@ function watching(one) {
|
|
|
299
316
|
continue;
|
|
300
317
|
}
|
|
301
318
|
if (!READS.has(asked.order))
|
|
302
|
-
one
|
|
319
|
+
forgets(one, asked);
|
|
303
320
|
const due = one.replaying.shift();
|
|
304
321
|
if (due !== undefined) {
|
|
305
322
|
if (told(due.order) === told(asked)) {
|
|
@@ -426,31 +443,15 @@ export function running(name, subcommands, at) {
|
|
|
426
443
|
? async (args, shows, within, how) => served(word, args, within, how)
|
|
427
444
|
: async (args, shows, within, how) => {
|
|
428
445
|
const where = how?.at ?? at;
|
|
429
|
-
const program = programFor(name, where);
|
|
446
|
+
const [program = '', ...lead] = programFor(name, where);
|
|
430
447
|
if ((shows ?? false) !== false)
|
|
431
|
-
return handed(where, program, [word, ...args], shows ?? false, within, how?.hears);
|
|
432
|
-
return await started(program, [word, ...args], { at: where, ...(within === undefined ? {} : { within }), ...(how?.hears === undefined ? {} : { hears: how.hears }) });
|
|
448
|
+
return handed(where, program, [...lead, word, ...args], shows ?? false, within, how?.hears);
|
|
449
|
+
return await started(program, [...lead, word, ...args], { at: where, ...(within === undefined ? {} : { within }), ...(how?.hears === undefined ? {} : { hears: how.hears }) });
|
|
433
450
|
};
|
|
434
451
|
}
|
|
435
452
|
return out;
|
|
436
453
|
}
|
|
437
|
-
|
|
438
|
-
const path = inTree(findTree(at), 'targets.ts');
|
|
439
|
-
return existsSync(path) ? valuesIn(readFileSync(path, 'utf8')) : {};
|
|
440
|
-
}
|
|
441
|
-
export function expansions(at) {
|
|
442
|
-
const held = sheet(at);
|
|
443
|
-
return (held.TARGETS ?? [])
|
|
444
|
-
.filter((one) => one.id.startsWith(REACH.cli))
|
|
445
|
-
.map((one) => ({ program: one.id.slice(REACH.cli.length), target: one, calls: (held.CALLS ?? []).filter((each) => each.of === one.id) }));
|
|
446
|
-
}
|
|
447
|
-
export function calls(target, at) {
|
|
448
|
-
const named = {};
|
|
449
|
-
for (const one of sheet(at).CALLS ?? [])
|
|
450
|
-
if (one.of === target)
|
|
451
|
-
named[one.id.replace(CALL_PREFIX, '')] = one.path;
|
|
452
|
-
return running(target.replace(REACH.cli, ''), named, at);
|
|
453
|
-
}
|
|
454
|
+
export { calls, expansions } from '../integrations/declared.js';
|
|
454
455
|
export { LOCKFILES };
|
|
455
456
|
export const manager = (into) => managerOf(into, existsSync);
|
|
456
457
|
const ADDS = { pnpm: 'add', yarn: 'add', bun: 'add', npm: 'install' };
|
|
@@ -488,7 +489,8 @@ export function installing(at) {
|
|
|
488
489
|
if (attached())
|
|
489
490
|
return here().fetch(at);
|
|
490
491
|
const tool = manager(at);
|
|
491
|
-
const
|
|
492
|
+
const [program = '', ...lead] = programFor(PACKAGER, at);
|
|
493
|
+
const ran = starts(program, [...lead, 'install'], { at });
|
|
492
494
|
const said = (ran.out + ran.err).trim();
|
|
493
495
|
if (!ran.there) {
|
|
494
496
|
throw new Error(tool + ' is what the lockfile in ' + at + ' picks, and it is not on this machine, so nothing here can fetch ' +
|
package/dist/integrations/cli.ts
CHANGED
|
@@ -8,11 +8,10 @@ import { fileURLToPath } from 'node:url'
|
|
|
8
8
|
import { CHANNEL, FOLDER, across, attached, createFileSync, existsSync, here, readFileSync, readdirSync, realpathSync, renameSync, rmSync, statSync, writeFileSync } from '../integrations/disk.ts'
|
|
9
9
|
import { VAULT_DIR } from '../integrations/vault.ts'
|
|
10
10
|
|
|
11
|
-
import {
|
|
11
|
+
import { LOCKFILES, ORDERS, PACKAGER, REACH, managerOf, programOf, unlisted, type Answer, type How, type Order, type Ran, type Recorded, type Running } from '../integrations/target.ts'
|
|
12
12
|
import { BESIDE, CLI } from '../core/invoked.ts'
|
|
13
|
-
import { valuesIn } from '../core/rows.ts'
|
|
14
13
|
import { THING } from '../core/scope.ts'
|
|
15
|
-
import { findTree
|
|
14
|
+
import { findTree } from '../core/tree.ts'
|
|
16
15
|
|
|
17
16
|
export interface Starting {
|
|
18
17
|
|
|
@@ -180,8 +179,8 @@ export function claims(root: string, runs: string): Held {
|
|
|
180
179
|
return { by: { pid: 0, runs: '', since: Date.now() } }
|
|
181
180
|
}
|
|
182
181
|
|
|
183
|
-
export function programFor(name: string, at: string): string {
|
|
184
|
-
const found = programOf(name, at, existsSync)
|
|
182
|
+
export function programFor(name: string, at: string): readonly string[] {
|
|
183
|
+
const found = programOf(name, at, existsSync, (path) => readFileSync(path, 'utf8'))
|
|
185
184
|
if (found === undefined) throw new Error(unlisted(name))
|
|
186
185
|
return found
|
|
187
186
|
}
|
|
@@ -217,6 +216,8 @@ export const PROGRAM = REACH.cli + THING
|
|
|
217
216
|
|
|
218
217
|
export const COPIES = THING + ' create copy'
|
|
219
218
|
|
|
219
|
+
export const COPY_AT = join(VAULT_DIR, THING + '.json')
|
|
220
|
+
|
|
220
221
|
interface Started {
|
|
221
222
|
readonly words: readonly string[]
|
|
222
223
|
readonly own: string
|
|
@@ -244,9 +245,9 @@ function startable(): Started | string {
|
|
|
244
245
|
} catch {
|
|
245
246
|
return UNCOPIED
|
|
246
247
|
}
|
|
247
|
-
const copied = (
|
|
248
|
-
if (
|
|
249
|
-
const said = readFileSync(
|
|
248
|
+
const copied = resolve(root, COPY_AT)
|
|
249
|
+
if (!existsSync(copied)) return UNCOPIED
|
|
250
|
+
const said = readFileSync(copied, 'utf8')
|
|
250
251
|
const own = join(root, CACHED, createHash('sha256').update(said).digest('hex').slice(0, 16))
|
|
251
252
|
const entry = join(own, 'cli', BESIDE('cli'))
|
|
252
253
|
if (!existsSync(entry)) {
|
|
@@ -337,6 +338,24 @@ const READS: ReadonlySet<string> = new Set(Object.entries(ORDERS).filter(([order
|
|
|
337
338
|
|
|
338
339
|
function kept(one: Serving, asked: Order, held: Answer): void {
|
|
339
340
|
if (READS.has(asked.order) && held.answered !== 'refused') one.known.set(told(asked), held)
|
|
341
|
+
if (asked.order !== 'write' || held.answered !== ORDERS.write) return
|
|
342
|
+
one.known.set(told({ order: 'read', at: asked.at }), { answered: 'read', said: asked.said ?? '' })
|
|
343
|
+
one.known.set(told({ order: 'kind', at: asked.at }), { answered: 'kind', found: 'file' })
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
const CHANGES: ReadonlySet<string> = new Set(['write', 'remove'])
|
|
347
|
+
|
|
348
|
+
const within = (at: string, under: string): boolean => under === '' || at === under || at.startsWith(under + '/')
|
|
349
|
+
|
|
350
|
+
function forgets(one: Serving, asked: Order): void {
|
|
351
|
+
if (!CHANGES.has(asked.order)) {
|
|
352
|
+
one.known.clear()
|
|
353
|
+
return
|
|
354
|
+
}
|
|
355
|
+
for (const key of [...one.known.keys()]) {
|
|
356
|
+
const [order = '', at = ''] = JSON.parse(key) as readonly string[]
|
|
357
|
+
if (order === 'real' || within(at, asked.at) || within(asked.at, at)) one.known.delete(key)
|
|
358
|
+
}
|
|
340
359
|
}
|
|
341
360
|
|
|
342
361
|
const plainly = (one: Order): string => one.order + ' ' + (one.at === '' ? '.' : one.at)
|
|
@@ -390,7 +409,7 @@ function watching(one: Serving): void {
|
|
|
390
409
|
reply(one.dir, short, known)
|
|
391
410
|
continue
|
|
392
411
|
}
|
|
393
|
-
if (!READS.has(asked.order)) one
|
|
412
|
+
if (!READS.has(asked.order)) forgets(one, asked)
|
|
394
413
|
const due = one.replaying.shift()
|
|
395
414
|
if (due !== undefined) {
|
|
396
415
|
if (told(due.order) === told(asked)) {
|
|
@@ -513,42 +532,15 @@ export function running(name: string, subcommands: Readonly<Record<string, strin
|
|
|
513
532
|
? async (args, shows, within, how) => served(word, args, within, how)
|
|
514
533
|
: async (args, shows, within, how) => {
|
|
515
534
|
const where = how?.at ?? at
|
|
516
|
-
const program = programFor(name, where)
|
|
517
|
-
if ((shows ?? false) !== false) return handed(where, program, [word, ...args], shows ?? false, within, how?.hears)
|
|
518
|
-
return await started(program, [word, ...args], { at: where, ...(within === undefined ? {} : { within }), ...(how?.hears === undefined ? {} : { hears: how.hears }) })
|
|
535
|
+
const [program = '', ...lead] = programFor(name, where)
|
|
536
|
+
if ((shows ?? false) !== false) return handed(where, program, [...lead, word, ...args], shows ?? false, within, how?.hears)
|
|
537
|
+
return await started(program, [...lead, word, ...args], { at: where, ...(within === undefined ? {} : { within }), ...(how?.hears === undefined ? {} : { hears: how.hears }) })
|
|
519
538
|
}
|
|
520
539
|
}
|
|
521
540
|
return out
|
|
522
541
|
}
|
|
523
542
|
|
|
524
|
-
|
|
525
|
-
readonly TARGETS?: readonly Target[]
|
|
526
|
-
readonly CALLS?: readonly Call[]
|
|
527
|
-
}
|
|
528
|
-
|
|
529
|
-
function sheet(at: string): Sheet {
|
|
530
|
-
const path = inTree(findTree(at), 'targets.ts')
|
|
531
|
-
return existsSync(path) ? (valuesIn(readFileSync(path, 'utf8')) as Sheet) : {}
|
|
532
|
-
}
|
|
533
|
-
|
|
534
|
-
export interface Expansion {
|
|
535
|
-
readonly program: string
|
|
536
|
-
readonly target: Target
|
|
537
|
-
readonly calls: readonly Call[]
|
|
538
|
-
}
|
|
539
|
-
|
|
540
|
-
export function expansions(at: string): readonly Expansion[] {
|
|
541
|
-
const held = sheet(at)
|
|
542
|
-
return (held.TARGETS ?? [])
|
|
543
|
-
.filter((one) => one.id.startsWith(REACH.cli))
|
|
544
|
-
.map((one) => ({ program: one.id.slice(REACH.cli.length), target: one, calls: (held.CALLS ?? []).filter((each) => each.of === one.id) }))
|
|
545
|
-
}
|
|
546
|
-
|
|
547
|
-
export function calls(target: string, at: string): Running {
|
|
548
|
-
const named: Record<string, string> = {}
|
|
549
|
-
for (const one of sheet(at).CALLS ?? []) if (one.of === target) named[one.id.replace(CALL_PREFIX, '')] = one.path
|
|
550
|
-
return running(target.replace(REACH.cli, ''), named, at)
|
|
551
|
-
}
|
|
543
|
+
export { calls, expansions, type Expansion } from '../integrations/declared.ts'
|
|
552
544
|
|
|
553
545
|
export { LOCKFILES }
|
|
554
546
|
|
|
@@ -595,7 +587,8 @@ export function needing(into: string, needs: Readonly<Record<string, string>>):
|
|
|
595
587
|
export function installing(at: string): string {
|
|
596
588
|
if (attached()) return here().fetch(at)
|
|
597
589
|
const tool = manager(at)
|
|
598
|
-
const
|
|
590
|
+
const [program = '', ...lead] = programFor(PACKAGER, at)
|
|
591
|
+
const ran = starts(program, [...lead, 'install'], { at })
|
|
599
592
|
const said = (ran.out + ran.err).trim()
|
|
600
593
|
if (!ran.there) {
|
|
601
594
|
throw new Error(
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type Call, type Running, type Target } from '../integrations/target.ts';
|
|
2
|
+
export interface Expansion {
|
|
3
|
+
readonly program: string;
|
|
4
|
+
readonly target: Target;
|
|
5
|
+
readonly calls: readonly Call[];
|
|
6
|
+
}
|
|
7
|
+
export declare function expansions(at: string): readonly Expansion[];
|
|
8
|
+
export declare function calls(target: string, at: string): Running;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { running } from '../integrations/cli.js';
|
|
2
|
+
import { existsSync, readFileSync } from '../integrations/disk.js';
|
|
3
|
+
import { CALL_PREFIX, REACH } from '../integrations/target.js';
|
|
4
|
+
import { valuesIn } from '../core/rows.js';
|
|
5
|
+
import { findTree, inTree } from '../core/tree.js';
|
|
6
|
+
function sheet(at) {
|
|
7
|
+
const path = inTree(findTree(at), 'targets.ts');
|
|
8
|
+
return existsSync(path) ? valuesIn(readFileSync(path, 'utf8')) : {};
|
|
9
|
+
}
|
|
10
|
+
export function expansions(at) {
|
|
11
|
+
const held = sheet(at);
|
|
12
|
+
return (held.TARGETS ?? [])
|
|
13
|
+
.filter((one) => one.id.startsWith(REACH.cli))
|
|
14
|
+
.map((one) => ({ program: one.id.slice(REACH.cli.length), target: one, calls: (held.CALLS ?? []).filter((each) => each.of === one.id) }));
|
|
15
|
+
}
|
|
16
|
+
export function calls(target, at) {
|
|
17
|
+
const named = {};
|
|
18
|
+
for (const one of sheet(at).CALLS ?? [])
|
|
19
|
+
if (one.of === target)
|
|
20
|
+
named[one.id.replace(CALL_PREFIX, '')] = one.path;
|
|
21
|
+
return running(target.replace(REACH.cli, ''), named, at);
|
|
22
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { running } from '../integrations/cli.ts'
|
|
2
|
+
import { existsSync, readFileSync } from '../integrations/disk.ts'
|
|
3
|
+
import { CALL_PREFIX, REACH, type Call, type Running, type Target } from '../integrations/target.ts'
|
|
4
|
+
import { valuesIn } from '../core/rows.ts'
|
|
5
|
+
import { findTree, inTree } from '../core/tree.ts'
|
|
6
|
+
|
|
7
|
+
interface Sheet {
|
|
8
|
+
readonly TARGETS?: readonly Target[]
|
|
9
|
+
readonly CALLS?: readonly Call[]
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function sheet(at: string): Sheet {
|
|
13
|
+
const path = inTree(findTree(at), 'targets.ts')
|
|
14
|
+
return existsSync(path) ? (valuesIn(readFileSync(path, 'utf8')) as Sheet) : {}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface Expansion {
|
|
18
|
+
readonly program: string
|
|
19
|
+
readonly target: Target
|
|
20
|
+
readonly calls: readonly Call[]
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function expansions(at: string): readonly Expansion[] {
|
|
24
|
+
const held = sheet(at)
|
|
25
|
+
return (held.TARGETS ?? [])
|
|
26
|
+
.filter((one) => one.id.startsWith(REACH.cli))
|
|
27
|
+
.map((one) => ({ program: one.id.slice(REACH.cli.length), target: one, calls: (held.CALLS ?? []).filter((each) => each.of === one.id) }))
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function calls(target: string, at: string): Running {
|
|
31
|
+
const named: Record<string, string> = {}
|
|
32
|
+
for (const one of sheet(at).CALLS ?? []) if (one.of === target) named[one.id.replace(CALL_PREFIX, '')] = one.path
|
|
33
|
+
return running(target.replace(REACH.cli, ''), named, at)
|
|
34
|
+
}
|
|
@@ -178,7 +178,7 @@ export declare const unreachable: (targets: readonly Target[]) => readonly strin
|
|
|
178
178
|
export declare const LOCKFILES: Readonly<Record<string, string>>;
|
|
179
179
|
export declare const PACKAGER = "packager";
|
|
180
180
|
export declare const managerOf: (at: string, there: (path: string) => boolean) => string;
|
|
181
|
-
export declare function programOf(name: string, at: string, there: (path: string) => boolean): string | undefined;
|
|
181
|
+
export declare function programOf(name: string, at: string, there: (path: string) => boolean, read: (path: string) => string): readonly string[] | undefined;
|
|
182
182
|
export declare const unlisted: (name: string) => string;
|
|
183
183
|
export declare function misnamed(target: string, name: string): string | undefined;
|
|
184
184
|
export declare function misaccepted(targets: readonly Target[], entities: readonly string[], calls: readonly Call[]): readonly string[];
|
|
@@ -143,17 +143,31 @@ export const PACKAGER = 'packager';
|
|
|
143
143
|
const NAMED = ['git', 'thread'];
|
|
144
144
|
const RUNTIME = 'node';
|
|
145
145
|
export const managerOf = (at, there) => Object.entries(LOCKFILES).find(([lock]) => there(join(at, lock)))?.[1] ?? 'npm';
|
|
146
|
-
export function programOf(name, at, there) {
|
|
146
|
+
export function programOf(name, at, there, read) {
|
|
147
147
|
if (NAMED.includes(name))
|
|
148
|
-
return name;
|
|
148
|
+
return [name];
|
|
149
149
|
if (name === RUNTIME)
|
|
150
|
-
return execPath;
|
|
150
|
+
return [execPath];
|
|
151
151
|
if (name === PACKAGER)
|
|
152
|
-
return managerOf(at, there);
|
|
152
|
+
return [managerOf(at, there)];
|
|
153
153
|
const installed = join(at, 'node_modules', '.bin', name);
|
|
154
|
-
|
|
154
|
+
if (there(installed))
|
|
155
|
+
return [installed];
|
|
156
|
+
const shipped = join(at, 'node_modules', name);
|
|
157
|
+
const manifest = join(shipped, 'package.json');
|
|
158
|
+
if (!there(manifest))
|
|
159
|
+
return undefined;
|
|
160
|
+
let bin;
|
|
161
|
+
try {
|
|
162
|
+
bin = JSON.parse(read(manifest)).bin;
|
|
163
|
+
}
|
|
164
|
+
catch {
|
|
165
|
+
return undefined;
|
|
166
|
+
}
|
|
167
|
+
const entry = typeof bin === 'string' ? bin : typeof bin === 'object' && bin !== null ? bin[name] : undefined;
|
|
168
|
+
return typeof entry === 'string' ? [execPath, join(shipped, entry)] : undefined;
|
|
155
169
|
}
|
|
156
|
-
export const unlisted = (name) => '`' + REACH.cli + name + '` names ' + name + ', and nothing lists a program by that name: it is not under node_modules/.bin, so this tree did not install it; it is not git, node or thread; and it is not `' + PACKAGER + '`, the word for whichever package manager the lockfile picks. A program the tree did not install is whatever the machine happens to have, and a target on that is a shell by another name. Add the package that provides it and declare the target again, or ask for it through a declared call on `' + REACH.cli + PACKAGER + '`';
|
|
170
|
+
export const unlisted = (name) => '`' + REACH.cli + name + '` names ' + name + ', and nothing lists a program by that name: it is not linked under node_modules/.bin, and no package installed here under that name names it as its program, so this tree did not install it; it is not git, node or thread; and it is not `' + PACKAGER + '`, the word for whichever package manager the lockfile picks. A program the tree did not install is whatever the machine happens to have, and a target on that is a shell by another name. Add the package that provides it and declare the target again, or ask for it through a declared call on `' + REACH.cli + PACKAGER + '`';
|
|
157
171
|
const SHAPED = {
|
|
158
172
|
cli: /^-{0,2}[A-Za-z][A-Za-z0-9-]*$/,
|
|
159
173
|
};
|
|
@@ -412,16 +412,32 @@ const RUNTIME = 'node'
|
|
|
412
412
|
export const managerOf = (at: string, there: (path: string) => boolean): string =>
|
|
413
413
|
Object.entries(LOCKFILES).find(([lock]) => there(join(at, lock)))?.[1] ?? 'npm'
|
|
414
414
|
|
|
415
|
-
export function programOf(
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
415
|
+
export function programOf(
|
|
416
|
+
name: string,
|
|
417
|
+
at: string,
|
|
418
|
+
there: (path: string) => boolean,
|
|
419
|
+
read: (path: string) => string,
|
|
420
|
+
): readonly string[] | undefined {
|
|
421
|
+
if (NAMED.includes(name)) return [name]
|
|
422
|
+
if (name === RUNTIME) return [execPath]
|
|
423
|
+
if (name === PACKAGER) return [managerOf(at, there)]
|
|
419
424
|
const installed = join(at, 'node_modules', '.bin', name)
|
|
420
|
-
|
|
425
|
+
if (there(installed)) return [installed]
|
|
426
|
+
const shipped = join(at, 'node_modules', name)
|
|
427
|
+
const manifest = join(shipped, 'package.json')
|
|
428
|
+
if (!there(manifest)) return undefined
|
|
429
|
+
let bin: unknown
|
|
430
|
+
try {
|
|
431
|
+
bin = (JSON.parse(read(manifest)) as { readonly bin?: unknown }).bin
|
|
432
|
+
} catch {
|
|
433
|
+
return undefined
|
|
434
|
+
}
|
|
435
|
+
const entry = typeof bin === 'string' ? bin : typeof bin === 'object' && bin !== null ? (bin as Readonly<Record<string, unknown>>)[name] : undefined
|
|
436
|
+
return typeof entry === 'string' ? [execPath, join(shipped, entry)] : undefined
|
|
421
437
|
}
|
|
422
438
|
|
|
423
439
|
export const unlisted = (name: string): string =>
|
|
424
|
-
'`' + REACH.cli + name + '` names ' + name + ', and nothing lists a program by that name: it is not under node_modules/.bin, so this tree did not install it; it is not git, node or thread; and it is not `' + PACKAGER + '`, the word for whichever package manager the lockfile picks. A program the tree did not install is whatever the machine happens to have, and a target on that is a shell by another name. Add the package that provides it and declare the target again, or ask for it through a declared call on `' + REACH.cli + PACKAGER + '`'
|
|
440
|
+
'`' + REACH.cli + name + '` names ' + name + ', and nothing lists a program by that name: it is not linked under node_modules/.bin, and no package installed here under that name names it as its program, so this tree did not install it; it is not git, node or thread; and it is not `' + PACKAGER + '`, the word for whichever package manager the lockfile picks. A program the tree did not install is whatever the machine happens to have, and a target on that is a shell by another name. Add the package that provides it and declare the target again, or ask for it through a declared call on `' + REACH.cli + PACKAGER + '`'
|
|
425
441
|
|
|
426
442
|
const SHAPED: Readonly<Record<'cli', RegExp>> = {
|
|
427
443
|
cli: /^-{0,2}[A-Za-z][A-Za-z0-9-]*$/,
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { starts } from '../../integrations/cli.js';
|
|
2
|
+
import { calls } from '../../integrations/declared.js';
|
|
2
3
|
import { existsSync, readFileSync } from '../../integrations/disk.js';
|
|
3
4
|
import { valuesIn } from '../../core/rows.js';
|
|
4
5
|
import { join } from 'node:path';
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { starts } from '../../integrations/cli.ts'
|
|
2
|
+
import { calls } from '../../integrations/declared.ts'
|
|
2
3
|
import { existsSync, readFileSync } from '../../integrations/disk.ts'
|
|
3
4
|
import { valuesIn } from '../../core/rows.ts'
|
|
4
5
|
import { join } from 'node:path'
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ROOT } from '../../core/descend.js';
|
|
2
2
|
import { valuesIn } from '../../core/rows.js';
|
|
3
|
-
import { kind } from '../../integrations/disk.js';
|
|
3
|
+
import { kind, readFileSync } from '../../integrations/disk.js';
|
|
4
4
|
import { REACH, programOf, unlisted } from '../../integrations/target.js';
|
|
5
5
|
const TARGETS = 'TARGETS';
|
|
6
6
|
const there = (path) => kind(path) === 'file';
|
|
@@ -20,7 +20,7 @@ export const slopnator = {
|
|
|
20
20
|
if (typeof one.id !== 'string' || !one.id.startsWith(REACH.cli))
|
|
21
21
|
continue;
|
|
22
22
|
const name = one.id.slice(REACH.cli.length);
|
|
23
|
-
if (programOf(name, ROOT, there) === undefined)
|
|
23
|
+
if (programOf(name, ROOT, there, (path) => readFileSync(path, 'utf8')) === undefined)
|
|
24
24
|
found.push(unlisted(name));
|
|
25
25
|
}
|
|
26
26
|
return found.length === 0 ? false : found.join('. ');
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ROOT } from '../../core/descend.ts'
|
|
2
2
|
import { valuesIn } from '../../core/rows.ts'
|
|
3
|
-
import { kind } from '../../integrations/disk.ts'
|
|
3
|
+
import { kind, readFileSync } from '../../integrations/disk.ts'
|
|
4
4
|
import { REACH, programOf, unlisted } from '../../integrations/target.ts'
|
|
5
5
|
|
|
6
6
|
import type { Slopnator } from '../slopnator.ts'
|
|
@@ -22,7 +22,7 @@ export const slopnator: Slopnator = {
|
|
|
22
22
|
for (const one of held.TARGETS ?? []) {
|
|
23
23
|
if (typeof one.id !== 'string' || !one.id.startsWith(REACH.cli)) continue
|
|
24
24
|
const name = one.id.slice(REACH.cli.length)
|
|
25
|
-
if (programOf(name, ROOT, there) === undefined) found.push(unlisted(name))
|
|
25
|
+
if (programOf(name, ROOT, there, (path) => readFileSync(path, 'utf8')) === undefined) found.push(unlisted(name))
|
|
26
26
|
}
|
|
27
27
|
return found.length === 0 ? false : found.join('. ')
|
|
28
28
|
},
|
|
@@ -1,2 +1,23 @@
|
|
|
1
|
+
import { type Call, type Target } from '../../integrations/target.ts';
|
|
2
|
+
import type { Secret } from '../../integrations/secret.ts';
|
|
3
|
+
export declare function reached(targets: readonly Target[], calls: readonly Call[], secrets: readonly Secret[]): Readonly<Record<string, {
|
|
4
|
+
readonly reach: string;
|
|
5
|
+
readonly address?: string;
|
|
6
|
+
readonly headers: Readonly<Record<string, string>>;
|
|
7
|
+
readonly sends: readonly {
|
|
8
|
+
readonly secret: string;
|
|
9
|
+
readonly header: string;
|
|
10
|
+
readonly prefix: string;
|
|
11
|
+
}[];
|
|
12
|
+
readonly signs: readonly {
|
|
13
|
+
readonly secret: string;
|
|
14
|
+
readonly header: string;
|
|
15
|
+
readonly prefix: string;
|
|
16
|
+
}[];
|
|
17
|
+
readonly calls: Readonly<Record<string, {
|
|
18
|
+
readonly method: string | null;
|
|
19
|
+
readonly path: string;
|
|
20
|
+
}>>;
|
|
21
|
+
}>>;
|
|
1
22
|
export declare function parts(text: string): readonly string[];
|
|
2
23
|
export declare function compile(say?: boolean): Promise<number>;
|
|
@@ -27,7 +27,7 @@ async function reaching() {
|
|
|
27
27
|
const secrets = await held('secrets.ts', 'SECRETS');
|
|
28
28
|
return reached(targets, calls, secrets);
|
|
29
29
|
}
|
|
30
|
-
function reached(targets, calls, secrets) {
|
|
30
|
+
export function reached(targets, calls, secrets) {
|
|
31
31
|
const out = {};
|
|
32
32
|
for (const one of targets) {
|
|
33
33
|
const mine = calls.filter((each) => each.of === one.id && one.takes.includes(each.id));
|
|
@@ -32,7 +32,7 @@ async function reaching(): Promise<Readonly<Record<string, ReturnType<typeof rea
|
|
|
32
32
|
return reached(targets, calls, secrets)
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
-
function reached(
|
|
35
|
+
export function reached(
|
|
36
36
|
targets: readonly Target[],
|
|
37
37
|
calls: readonly Call[],
|
|
38
38
|
secrets: readonly Secret[],
|
|
@@ -1 +1,2 @@
|
|
|
1
|
-
|
|
1
|
+
import { type Grant } from '../access.ts';
|
|
2
|
+
export declare function permitting(grants: readonly Grant[]): string;
|