@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.
@@ -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 { CALL_PREFIX, LOCKFILES, ORDERS, PACKAGER, REACH, managerOf, programOf, unlisted } from '../integrations/target.js';
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, inTree } from '../core/tree.js';
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 = ((sheet(root).TARGETS ?? []).find((one) => one.id === PROGRAM)?.derives ?? []).find((one) => one.by === COPIES)?.at;
191
- if (copied === undefined || !existsSync(resolve(root, copied)))
190
+ const copied = resolve(root, COPY_AT);
191
+ if (!existsSync(copied))
192
192
  return UNCOPIED;
193
- const said = readFileSync(resolve(root, copied), 'utf8');
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.known.clear();
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
- function sheet(at) {
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 ran = starts(programFor(PACKAGER, at), ['install'], { at });
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 ' +
@@ -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 { CALL_PREFIX, LOCKFILES, ORDERS, PACKAGER, REACH, managerOf, programOf, unlisted, type Answer, type Call, type How, type Order, type Ran, type Recorded, type Running, type Target } from '../integrations/target.ts'
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, inTree } from '../core/tree.ts'
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 = ((sheet(root).TARGETS ?? []).find((one) => one.id === PROGRAM)?.derives ?? []).find((one) => one.by === COPIES)?.at
248
- if (copied === undefined || !existsSync(resolve(root, copied))) return UNCOPIED
249
- const said = readFileSync(resolve(root, copied), 'utf8')
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.known.clear()
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
- interface Sheet {
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 ran = starts(programFor(PACKAGER, at), ['install'], { at })
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
- return there(installed) ? installed : undefined;
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(name: string, at: string, there: (path: string) => boolean): string | undefined {
416
- if (NAMED.includes(name)) return name
417
- if (name === RUNTIME) return execPath
418
- if (name === PACKAGER) return managerOf(at, there)
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
- return there(installed) ? installed : undefined
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 { calls, starts } from '../../integrations/cli.js';
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 { calls, starts } from '../../integrations/cli.ts'
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
- export {};
1
+ import { type Grant } from '../access.ts';
2
+ export declare function permitting(grants: readonly Grant[]): string;