@prestack/thread 0.1.20 → 0.1.22

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (148) hide show
  1. package/CHANGELOG.md +193 -0
  2. package/dist/core/attach.js +21 -0
  3. package/dist/core/descend.d.ts +2 -0
  4. package/dist/core/descend.js +18 -3
  5. package/dist/core/generators/commit.js +55 -3
  6. package/dist/core/generators/dev.d.ts +1 -0
  7. package/dist/core/generators/dev.js +68 -0
  8. package/dist/core/rows.d.ts +5 -0
  9. package/dist/core/rows.js +45 -0
  10. package/dist/core/tree.d.ts +1 -0
  11. package/dist/core/tree.js +7 -1
  12. package/dist/derivation/generators/build.d.ts +3 -1
  13. package/dist/derivation/generators/build.js +103 -22
  14. package/dist/derivation/generators/release.js +74 -26
  15. package/dist/derivation/release.js +3 -3
  16. package/dist/integrations/cli.d.ts +3 -9
  17. package/dist/integrations/cli.js +96 -36
  18. package/dist/integrations/declared.d.ts +8 -0
  19. package/dist/integrations/declared.js +22 -0
  20. package/dist/integrations/pglite.js +13 -3
  21. package/dist/integrations/target.d.ts +4 -1
  22. package/dist/integrations/target.js +20 -6
  23. package/dist/integrity/attach.js +1 -5
  24. package/dist/integrity/generators/publish.js +2 -1
  25. package/dist/integrity/slopnators/sl_stale_path.js +10 -0
  26. package/dist/integrity/slopnators/sl_unlisted_program.js +2 -2
  27. package/dist/operations/access.js +3 -2
  28. package/dist/operations/capabilities.d.ts +2 -0
  29. package/dist/operations/capabilities.js +2 -0
  30. package/dist/operations/generators/capability.js +234 -24
  31. package/dist/operations/generators/compile.d.ts +21 -0
  32. package/dist/operations/generators/compile.js +151 -51
  33. package/dist/operations/generators/permits.d.ts +2 -1
  34. package/dist/operations/generators/permits.js +52 -47
  35. package/dist/operations/generators/seed.d.ts +3 -0
  36. package/dist/operations/generators/seed.js +25 -3
  37. package/dist/operations/nodes.d.ts +2 -2
  38. package/package.json +1 -1
  39. package/dist/core/actor.ts +0 -21
  40. package/dist/core/attach.ts +0 -613
  41. package/dist/core/chain.ts +0 -145
  42. package/dist/core/condition.ts +0 -227
  43. package/dist/core/descend.ts +0 -1173
  44. package/dist/core/errors/catalogue.ts +0 -314
  45. package/dist/core/errors/classes.ts +0 -51
  46. package/dist/core/errors/error.ts +0 -152
  47. package/dist/core/errors/namespaces.ts +0 -19
  48. package/dist/core/finding.ts +0 -37
  49. package/dist/core/generators/commit.ts +0 -157
  50. package/dist/core/init.ts +0 -222
  51. package/dist/core/invoked.ts +0 -52
  52. package/dist/core/round.ts +0 -74
  53. package/dist/core/rows.ts +0 -459
  54. package/dist/core/scope.ts +0 -20
  55. package/dist/core/thread.ts +0 -29
  56. package/dist/core/tree.ts +0 -90
  57. package/dist/core/types.ts +0 -351
  58. package/dist/derivation/attach.ts +0 -57
  59. package/dist/derivation/generators/build.ts +0 -371
  60. package/dist/derivation/generators/check.ts +0 -118
  61. package/dist/derivation/generators/determinism.ts +0 -145
  62. package/dist/derivation/generators/release.ts +0 -236
  63. package/dist/derivation/release.ts +0 -43
  64. package/dist/integrations/attach.ts +0 -187
  65. package/dist/integrations/cli.ts +0 -615
  66. package/dist/integrations/disk.ts +0 -466
  67. package/dist/integrations/generators/read.ts +0 -127
  68. package/dist/integrations/generators/vault.ts +0 -210
  69. package/dist/integrations/https.ts +0 -216
  70. package/dist/integrations/pglite.ts +0 -206
  71. package/dist/integrations/secret.ts +0 -54
  72. package/dist/integrations/target.ts +0 -656
  73. package/dist/integrations/vault.ts +0 -148
  74. package/dist/integrity/attach.ts +0 -150
  75. package/dist/integrity/check-threads.ts +0 -166
  76. package/dist/integrity/constraint.ts +0 -1246
  77. package/dist/integrity/generators/compiler.ts +0 -138
  78. package/dist/integrity/generators/gate.ts +0 -227
  79. package/dist/integrity/generators/issues.ts +0 -160
  80. package/dist/integrity/generators/publish.ts +0 -232
  81. package/dist/integrity/generators/trial.ts +0 -359
  82. package/dist/integrity/lint.ts +0 -214
  83. package/dist/integrity/lsp/bin.ts +0 -44
  84. package/dist/integrity/lsp/diagnostics.ts +0 -92
  85. package/dist/integrity/lsp/findings.ts +0 -63
  86. package/dist/integrity/lsp/lint.ts +0 -124
  87. package/dist/integrity/lsp/protocol.ts +0 -78
  88. package/dist/integrity/lsp/server.ts +0 -224
  89. package/dist/integrity/slopnator.ts +0 -324
  90. package/dist/integrity/slopnators/sl_broken_lineage.ts +0 -38
  91. package/dist/integrity/slopnators/sl_declared_elsewhere.ts +0 -60
  92. package/dist/integrity/slopnators/sl_derived_drift.ts +0 -101
  93. package/dist/integrity/slopnators/sl_door_in_a_node.ts +0 -250
  94. package/dist/integrity/slopnators/sl_hardcoded_style.ts +0 -55
  95. package/dist/integrity/slopnators/sl_inert_island.ts +0 -84
  96. package/dist/integrity/slopnators/sl_inert_slopnator.ts +0 -93
  97. package/dist/integrity/slopnators/sl_missing_row.ts +0 -140
  98. package/dist/integrity/slopnators/sl_no_barrel.ts +0 -48
  99. package/dist/integrity/slopnators/sl_no_child_process.ts +0 -100
  100. package/dist/integrity/slopnators/sl_no_comment.ts +0 -43
  101. package/dist/integrity/slopnators/sl_no_dispatch_bypass.ts +0 -276
  102. package/dist/integrity/slopnators/sl_no_import_cycle.ts +0 -66
  103. package/dist/integrity/slopnators/sl_no_such_command.ts +0 -81
  104. package/dist/integrity/slopnators/sl_only_takes_a_literal.ts +0 -63
  105. package/dist/integrity/slopnators/sl_outside_an_operation.ts +0 -25
  106. package/dist/integrity/slopnators/sl_screen_holds_only_declared.ts +0 -70
  107. package/dist/integrity/slopnators/sl_secret_literal.ts +0 -68
  108. package/dist/integrity/slopnators/sl_stale_path.ts +0 -178
  109. package/dist/integrity/slopnators/sl_twice_meant.ts +0 -57
  110. package/dist/integrity/slopnators/sl_twice_parented.ts +0 -82
  111. package/dist/integrity/slopnators/sl_undeclared_crossing.ts +0 -481
  112. package/dist/integrity/slopnators/sl_undeclared_destination.ts +0 -113
  113. package/dist/integrity/slopnators/sl_undeclared_element.ts +0 -44
  114. package/dist/integrity/slopnators/sl_undeclared_list.ts +0 -358
  115. package/dist/integrity/slopnators/sl_undeclared_node.ts +0 -75
  116. package/dist/integrity/slopnators/sl_undeclared_operation.ts +0 -257
  117. package/dist/integrity/slopnators/sl_undeclared_variant.ts +0 -152
  118. package/dist/integrity/slopnators/sl_undeclared_webhook.ts +0 -108
  119. package/dist/integrity/slopnators/sl_underived_route.ts +0 -72
  120. package/dist/integrity/slopnators/sl_undescribed.ts +0 -65
  121. package/dist/integrity/slopnators/sl_ungoverned_island.ts +0 -60
  122. package/dist/integrity/slopnators/sl_unlisted_program.ts +0 -46
  123. package/dist/integrity/slopnators/sl_unnamed_island.ts +0 -41
  124. package/dist/integrity/slopnators/sl_unpaired_member.ts +0 -139
  125. package/dist/integrity/slopnators/sl_unpaired_slopnator.ts +0 -71
  126. package/dist/integrity/slopnators/sl_unprefixed_member.ts +0 -110
  127. package/dist/integrity/slopnators/sl_unthreaded.ts +0 -421
  128. package/dist/integrity/slopnators/sl_untranslated_key.ts +0 -140
  129. package/dist/integrity/slopnators/sl_unvaulted_secret.ts +0 -132
  130. package/dist/integrity/slopnators/sl_unversioned.ts +0 -52
  131. package/dist/integrity/sweep.ts +0 -395
  132. package/dist/knowledge/attach.ts +0 -327
  133. package/dist/knowledge/generators/docsgen.ts +0 -186
  134. package/dist/knowledge/generators/glossary.ts +0 -73
  135. package/dist/knowledge/glossary.ts +0 -6
  136. package/dist/operations/access.ts +0 -131
  137. package/dist/operations/attach.ts +0 -408
  138. package/dist/operations/capabilities.ts +0 -88
  139. package/dist/operations/dispatch.ts +0 -294
  140. package/dist/operations/error-results.ts +0 -31
  141. package/dist/operations/generators/capability.ts +0 -1054
  142. package/dist/operations/generators/compile.ts +0 -701
  143. package/dist/operations/generators/permits.ts +0 -224
  144. package/dist/operations/generators/seed.ts +0 -359
  145. package/dist/operations/generators/trial.ts +0 -666
  146. package/dist/operations/nodes.ts +0 -53
  147. package/dist/operations/operations.ts +0 -91
  148. package/dist/operations/result.ts +0 -28
@@ -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;
@@ -246,6 +245,63 @@ const READS = new Set(Object.entries(ORDERS).filter(([order, answer]) => order =
246
245
  function kept(one, asked, held) {
247
246
  if (READS.has(asked.order) && held.answered !== 'refused')
248
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
+ }
265
+ }
266
+ const AHEAD = 32;
267
+ const upOf = (at) => (at.includes('/') ? at.slice(0, at.lastIndexOf('/')) : '');
268
+ const nameOf = (at) => at.slice(at.lastIndexOf('/') + 1);
269
+ const inside = (at, name) => (at === '' ? name : at + '/' + name);
270
+ function listedAt(one, at) {
271
+ const held = one.known.get(told({ order: 'list', at }))?.found;
272
+ return Array.isArray(held) ? held : [];
273
+ }
274
+ function realOf(one, at) {
275
+ const held = one.known.get(told({ order: 'real', at }));
276
+ if (held !== undefined || at === '')
277
+ return held;
278
+ const entry = listedAt(one, upOf(at)).find((each) => each.name === nameOf(at));
279
+ if (entry === undefined || entry.kind === 'other')
280
+ return undefined;
281
+ const above = realOf(one, upOf(at));
282
+ return typeof above?.at === 'string' ? { answered: 'real', at: above.at + '/' + nameOf(at) } : undefined;
283
+ }
284
+ function aheadOf(one, asked) {
285
+ if (asked.order === 'real')
286
+ return asked.at === '' || one.known.has(told({ order: 'real', at: '' })) ? [] : [{ order: 'real', at: '', ahead: true }];
287
+ const name = nameOf(asked.at);
288
+ const dot = name.indexOf('.');
289
+ const reading = asked.order === 'read' && dot > 0 && asked.at.split('/').includes(INSTALLED);
290
+ if (!reading && asked.order !== 'stat')
291
+ return [];
292
+ const suffix = reading ? name.slice(dot) : '';
293
+ const up = upOf(asked.at);
294
+ const found = [];
295
+ for (const each of listedAt(one, up)) {
296
+ if (found.length === AHEAD)
297
+ break;
298
+ if (each.kind !== 'file' || each.name === name || !each.name.endsWith(suffix))
299
+ continue;
300
+ const order = { order: asked.order, at: inside(up, each.name), ahead: true };
301
+ if (!one.known.has(told(order)))
302
+ found.push(order);
303
+ }
304
+ return found;
249
305
  }
250
306
  const plainly = (one) => one.order + ' ' + (one.at === '' ? '.' : one.at);
251
307
  const outside = (root, at) => {
@@ -299,11 +355,22 @@ function watching(one) {
299
355
  reply(one.dir, short, known);
300
356
  continue;
301
357
  }
358
+ const derived = asked.order === 'real' ? realOf(one, asked.at) : undefined;
359
+ if (derived !== undefined) {
360
+ reply(one.dir, short, derived);
361
+ kept(one, asked, derived);
362
+ continue;
363
+ }
302
364
  if (!READS.has(asked.order))
303
- one.known.clear();
365
+ forgets(one, asked);
304
366
  const due = one.replaying.shift();
305
367
  if (due !== undefined) {
306
368
  if (told(due.order) === told(asked)) {
369
+ while (one.replaying[0]?.order.ahead === true) {
370
+ const extra = one.replaying.shift();
371
+ if (extra !== undefined)
372
+ kept(one, extra.order, extra.answered);
373
+ }
307
374
  reply(one.dir, short, due.answered);
308
375
  kept(one, asked, due.answered);
309
376
  continue;
@@ -318,7 +385,8 @@ function watching(one) {
318
385
  }
319
386
  one.pending = short;
320
387
  one.asking = asked;
321
- hand(one, still(one, [asked]));
388
+ one.ahead = aheadOf(one, asked);
389
+ hand(one, still(one, [asked, ...one.ahead]));
322
390
  }
323
391
  }, TICK);
324
392
  }
@@ -346,11 +414,11 @@ function next(one, within) {
346
414
  }, Math.max(1_000, within - MARGIN));
347
415
  });
348
416
  }
349
- function begun(from, word, args, root, hears, called, recording = []) {
417
+ function begun(from, word, args, root, hears, called, recording = [], stash) {
350
418
  const of = called ?? randomBytes(6).toString('hex');
351
419
  const dir = join(tmpdir(), SERVED, randomBytes(6).toString('hex'));
352
420
  writeFileSync(join(dir, 'channel'), '', 'utf8');
353
- const one = { of, dir, root, out: '', err: '', why: '', pending: '', waiting: [], parked: [], replaying: [...recording], handed: recording.length, known: new Map() };
421
+ const one = { of, dir, root, out: '', err: '', why: '', pending: '', waiting: [], parked: [], replaying: [...recording], handed: recording.length, known: new Map(), ahead: [], ...(stash === undefined ? {} : { stash }) };
354
422
  const child = spawn(execPath, [...from.words, word, ...args], {
355
423
  cwd: from.own,
356
424
  shell: false,
@@ -381,6 +449,7 @@ function counted(one, held) {
381
449
  one.last = held;
382
450
  return held;
383
451
  }
452
+ const stashing = (one, held) => (one.stash === undefined ? held : { ...held, stashed: one.stash });
384
453
  async function served(word, args, within, how) {
385
454
  const of = how?.of;
386
455
  if (of !== undefined) {
@@ -395,20 +464,26 @@ async function served(word, args, within, how) {
395
464
  const from = startable();
396
465
  if (typeof from === 'string')
397
466
  return nowhere(from);
398
- const again = begun(from, word, args, root, how?.hears, of, recording);
399
- return counted(again, await next(again, within));
467
+ const again = begun(from, word, args, root, how?.hears, of, recording, how?.stash);
468
+ return stashing(again, counted(again, await next(again, within)));
400
469
  }
401
470
  const answering = how?.answering;
402
471
  if (answering !== undefined && answering < one.handed && one.last !== undefined)
403
- return one.last;
404
- const given = (how?.answered ?? [])[0];
472
+ return stashing(one, one.last);
473
+ const [given, ...rest] = how?.answered ?? [];
405
474
  if (one.pending !== '' && given !== undefined) {
475
+ one.ahead.forEach((order, index) => {
476
+ const held = rest[index];
477
+ if (held !== undefined)
478
+ kept(one, order, held);
479
+ });
480
+ one.ahead = [];
406
481
  reply(one.dir, one.pending, given);
407
482
  if (one.asking !== undefined)
408
483
  kept(one, one.asking, given);
409
484
  one.pending = '';
410
485
  }
411
- return counted(one, await next(one, within));
486
+ return stashing(one, counted(one, await next(one, within)));
412
487
  }
413
488
  const root = how?.at ?? '';
414
489
  if (root === '')
@@ -416,8 +491,8 @@ async function served(word, args, within, how) {
416
491
  const from = startable();
417
492
  if (typeof from === 'string')
418
493
  return nowhere(from);
419
- const fresh = begun(from, word, args, root, how?.hears);
420
- return counted(fresh, await next(fresh, within));
494
+ const fresh = begun(from, word, args, root, how?.hears, undefined, [], how?.stash);
495
+ return stashing(fresh, counted(fresh, await next(fresh, within)));
421
496
  }
422
497
  export function running(name, subcommands, at) {
423
498
  const out = {};
@@ -427,31 +502,15 @@ export function running(name, subcommands, at) {
427
502
  ? async (args, shows, within, how) => served(word, args, within, how)
428
503
  : async (args, shows, within, how) => {
429
504
  const where = how?.at ?? at;
430
- const program = programFor(name, where);
505
+ const [program = '', ...lead] = programFor(name, where);
431
506
  if ((shows ?? false) !== false)
432
- return handed(where, program, [word, ...args], shows ?? false, within, how?.hears);
433
- return await started(program, [word, ...args], { at: where, ...(within === undefined ? {} : { within }), ...(how?.hears === undefined ? {} : { hears: how.hears }) });
507
+ return handed(where, program, [...lead, word, ...args], shows ?? false, within, how?.hears);
508
+ return await started(program, [...lead, word, ...args], { at: where, ...(within === undefined ? {} : { within }), ...(how?.hears === undefined ? {} : { hears: how.hears }) });
434
509
  };
435
510
  }
436
511
  return out;
437
512
  }
438
- function sheet(at) {
439
- const path = inTree(findTree(at), 'targets.ts');
440
- return existsSync(path) ? valuesIn(readFileSync(path, 'utf8')) : {};
441
- }
442
- export function expansions(at) {
443
- const held = sheet(at);
444
- return (held.TARGETS ?? [])
445
- .filter((one) => one.id.startsWith(REACH.cli))
446
- .map((one) => ({ program: one.id.slice(REACH.cli.length), target: one, calls: (held.CALLS ?? []).filter((each) => each.of === one.id) }));
447
- }
448
- export function calls(target, at) {
449
- const named = {};
450
- for (const one of sheet(at).CALLS ?? [])
451
- if (one.of === target)
452
- named[one.id.replace(CALL_PREFIX, '')] = one.path;
453
- return running(target.replace(REACH.cli, ''), named, at);
454
- }
513
+ export { calls, expansions } from '../integrations/declared.js';
455
514
  export { LOCKFILES };
456
515
  export const manager = (into) => managerOf(into, existsSync);
457
516
  const ADDS = { pnpm: 'add', yarn: 'add', bun: 'add', npm: 'install' };
@@ -489,7 +548,8 @@ export function installing(at) {
489
548
  if (attached())
490
549
  return here().fetch(at);
491
550
  const tool = manager(at);
492
- const ran = starts(programFor(PACKAGER, at), ['install'], { at });
551
+ const [program = '', ...lead] = programFor(PACKAGER, at);
552
+ const ran = starts(program, [...lead, 'install'], { at });
493
553
  const said = (ran.out + ran.err).trim();
494
554
  if (!ran.there) {
495
555
  throw new Error(tool + ' is what the lockfile in ' + at + ' picks, and it is not on this machine, so nothing here can fetch ' +
@@ -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
+ }
@@ -52,14 +52,24 @@ export async function shapeOf(sql) {
52
52
  }
53
53
  }
54
54
  export function standing(held) {
55
- const store = storing({ ...held, at: 'memory://', migrations: [] });
55
+ const store = storing({ ...held, at: 'memory://' });
56
56
  const keeps = Object.keys(held.tables);
57
57
  return {
58
58
  keeps,
59
59
  store,
60
60
  async seed(actor, rows) {
61
- for (const one of rows)
61
+ for (const one of rows) {
62
+ const by = held.address[one.entity];
63
+ const key = by === undefined ? undefined : one.row[by];
64
+ if (by !== undefined && (typeof key === 'string' || typeof key === 'number')) {
65
+ const there = await store.list(actor, one.entity, { where: { [by]: key }, limit: 1 });
66
+ if (there.length > 0) {
67
+ await store.update(actor, one.entity, key, one.row);
68
+ continue;
69
+ }
70
+ }
62
71
  await store.insert(actor, one.entity, one.row);
72
+ }
63
73
  },
64
74
  async taken(actor) {
65
75
  const found = {};
@@ -83,7 +93,7 @@ export function storing(held) {
83
93
  const found = held.address[entity];
84
94
  if (found === undefined) {
85
95
  throw new Error(entity + ' declares nothing that identifies one of its rows, so a row of it cannot be addressed here. ' +
86
- 'Give it a value and say so with `thread create entity edit`.');
96
+ 'Name the value that does: give `thread edit ' + entity + ' --must` its invariants again with one more, unique over that value alone.');
87
97
  }
88
98
  return found;
89
99
  };
@@ -68,6 +68,7 @@ export interface Order {
68
68
  readonly said?: string;
69
69
  readonly mode?: number;
70
70
  readonly words?: readonly string[];
71
+ readonly ahead?: boolean;
71
72
  }
72
73
  export interface Answer {
73
74
  readonly answered: string;
@@ -87,6 +88,7 @@ export interface How {
87
88
  readonly answered?: readonly Answer[];
88
89
  readonly recording?: readonly Recorded[];
89
90
  readonly answering?: number;
91
+ readonly stash?: unknown;
90
92
  }
91
93
  export interface Ran {
92
94
  readonly there: boolean;
@@ -99,6 +101,7 @@ export interface Ran {
99
101
  readonly of?: string;
100
102
  readonly asks?: readonly Order[];
101
103
  readonly lost?: string;
104
+ readonly stashed?: unknown;
102
105
  }
103
106
  export type Running<C extends string = string> = Readonly<Record<C, (args: readonly string[], shows?: boolean | 'errors' | 'tee', within?: number, how?: How) => Promise<Ran>>>;
104
107
  export type Calling<C extends string = string> = {
@@ -178,7 +181,7 @@ export declare const unreachable: (targets: readonly Target[]) => readonly strin
178
181
  export declare const LOCKFILES: Readonly<Record<string, string>>;
179
182
  export declare const PACKAGER = "packager";
180
183
  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;
184
+ export declare function programOf(name: string, at: string, there: (path: string) => boolean, read: (path: string) => string): readonly string[] | undefined;
182
185
  export declare const unlisted: (name: string) => string;
183
186
  export declare function misnamed(target: string, name: string): string | undefined;
184
187
  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
  };
@@ -116,11 +116,7 @@ export const ATTACH = {
116
116
  { file: 'rules.ts', source: RULES_SOURCE },
117
117
  { file: 'gate.ts', source: GATE_SOURCE },
118
118
  ],
119
- ignores: ['.thread/', '.next/', 'node_modules/', 'tsconfig.tsbuildinfo'],
120
- scripts: {
121
- build: 'next build',
122
- start: 'next start',
123
- },
119
+ ignores: ['.thread/', 'node_modules/', 'tsconfig.tsbuildinfo'],
124
120
  needs: {
125
121
  '@types/node': '^24.0.0',
126
122
  typescript: '^6.0.0',
@@ -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';
@@ -76,6 +76,9 @@ export const slopnator = {
76
76
  return false;
77
77
  if (derived().has(path))
78
78
  return false;
79
+ const folder = path.slice(0, path.lastIndexOf('/'));
80
+ if (statSync(resolve(ROOT, folder), { throwIfNoEntry: false })?.isDirectory() !== true)
81
+ return false;
79
82
  return `\`${value}\` is not on disk`;
80
83
  },
81
84
  proves: {
@@ -152,6 +155,13 @@ export const slopnator = {
152
155
  ],
153
156
  at: 'probe.tsx',
154
157
  },
158
+ {
159
+ files: [
160
+ { at: 'docs/_probe_there.md', source: 'there\n' },
161
+ { at: 'probe.tsx', source: "const at = 'docs/elsewhere/entry.ts'\n" },
162
+ ],
163
+ at: 'probe.tsx',
164
+ },
155
165
  "const at = 'threads/'",
156
166
  "const said = 'a sentence about packages/core/integrity/src/constraint.ts and nothing else'",
157
167
  {
@@ -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('. ');
@@ -17,8 +17,9 @@ export function undeclared(answered, offers, which) {
17
17
  return (`this app declares no ${which === 'roles' ? 'role' : 'entitlement'} called ${stray.map((one) => `\`${one}\``).join(', ')} — ` +
18
18
  (offers.length === 0
19
19
  ? `it declares none at all. A ${which === 'roles' ? 'role' : 'entitlement'} is one of the words the value an ` +
20
- 'entity names as saying what a caller may do is held to. Declare them with `thread create entity edit` — ' +
21
- 'name that value, give it the `choice` format, and state its options'
20
+ 'entity names as saying what a caller may do is held to. Declare them on that entity: give ' +
21
+ '`thread edit <entity> --holds` its values again with that one in the `choice` format and its options stated, ' +
22
+ `and name it as \`${which}\` in \`thread edit <entity> --actor\``
22
23
  : `it declares ${offers.map((one) => `\`${one}\``).join(', ')}`));
23
24
  }
24
25
  export const keyIn = (one) => (one.must ?? []).flatMap((each) => (each.unique?.length === 1 ? [each.unique[0]] : []))[0];
@@ -9,6 +9,8 @@ export interface Defaults extends Threaded {
9
9
  readonly live?: readonly Surface[];
10
10
  }
11
11
  export declare const CAPABILITIES_AT = "operations/_capabilities.json";
12
+ export declare const SERVER_AT = "_generated/server/entry.ts";
13
+ export declare const SERVER_OUT = "_generated/server/server.mjs";
12
14
  export declare const CLIENT_AT = "operations/_client.ts";
13
15
  export declare const DISPATCHING_AT = "app/_generated/dispatching.ts";
14
16
  export declare const COMPILED = "app/_generated/compiled";
@@ -9,6 +9,8 @@ export const MEANS = {
9
9
  export const DEFAULTS_AT = '_threads/defaults.ts';
10
10
  export const UNSTATED = ['cli'];
11
11
  export const CAPABILITIES_AT = 'operations/_capabilities.json';
12
+ export const SERVER_AT = '_generated/server/entry.ts';
13
+ export const SERVER_OUT = '_generated/server/server.mjs';
12
14
  export const CLIENT_AT = 'operations/_client.ts';
13
15
  export const DISPATCHING_AT = 'app/_generated/dispatching.ts';
14
16
  export const COMPILED = 'app/_generated/compiled';