@graview/core 0.1.58 → 0.1.60

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/dist/archived-seen.d.ts.map +1 -1
  2. package/dist/archived-seen.js +7 -3
  3. package/dist/archived-seen.js.map +1 -1
  4. package/dist/capabilities.js +1 -1
  5. package/dist/capabilities.js.map +1 -1
  6. package/dist/catalog.js +1 -1
  7. package/dist/catalog.js.map +1 -1
  8. package/dist/cli/check/field-sight.d.ts.map +1 -1
  9. package/dist/cli/check/field-sight.js +3 -1
  10. package/dist/cli/check/field-sight.js.map +1 -1
  11. package/dist/cli/check/index.d.ts +1 -0
  12. package/dist/cli/check/index.d.ts.map +1 -1
  13. package/dist/cli/check/index.js +1 -0
  14. package/dist/cli/check/index.js.map +1 -1
  15. package/dist/cli/check/typing.d.ts +4 -0
  16. package/dist/cli/check/typing.d.ts.map +1 -0
  17. package/dist/cli/check/typing.js +56 -0
  18. package/dist/cli/check/typing.js.map +1 -0
  19. package/dist/cli/check.d.ts.map +1 -1
  20. package/dist/cli/check.js +2 -1
  21. package/dist/cli/check.js.map +1 -1
  22. package/dist/document/brand.js +3 -3
  23. package/dist/document/brand.js.map +1 -1
  24. package/dist/document/compiled.d.ts.map +1 -1
  25. package/dist/document/compiled.js +22 -1
  26. package/dist/document/compiled.js.map +1 -1
  27. package/dist/document/schema.d.ts.map +1 -1
  28. package/dist/document/schema.js +2 -1
  29. package/dist/document/schema.js.map +1 -1
  30. package/dist/document.schema.json +42 -4
  31. package/dist/field-sight.js +4 -2
  32. package/dist/field-sight.js.map +1 -1
  33. package/dist/format-error.d.ts +1 -1
  34. package/dist/format-error.js +1 -1
  35. package/dist/formats.d.ts +22 -4
  36. package/dist/formats.d.ts.map +1 -1
  37. package/dist/formats.js +28 -9
  38. package/dist/formats.js.map +1 -1
  39. package/dist/fresh.d.ts +26 -0
  40. package/dist/fresh.d.ts.map +1 -0
  41. package/dist/fresh.js +33 -0
  42. package/dist/fresh.js.map +1 -0
  43. package/dist/friction.d.ts +7 -0
  44. package/dist/friction.d.ts.map +1 -1
  45. package/dist/friction.js +2 -1
  46. package/dist/friction.js.map +1 -1
  47. package/dist/import/rows.d.ts.map +1 -1
  48. package/dist/import/rows.js +20 -10
  49. package/dist/import/rows.js.map +1 -1
  50. package/dist/index.d.ts +3 -0
  51. package/dist/index.d.ts.map +1 -1
  52. package/dist/index.js +2 -0
  53. package/dist/index.js.map +1 -1
  54. package/dist/linked.d.ts.map +1 -1
  55. package/dist/linked.js +7 -1
  56. package/dist/linked.js.map +1 -1
  57. package/dist/mutations/define-mutation.d.ts +14 -0
  58. package/dist/mutations/define-mutation.d.ts.map +1 -1
  59. package/dist/mutations/define-mutation.js +11 -5
  60. package/dist/mutations/define-mutation.js.map +1 -1
  61. package/dist/mutations/derive-edits.d.ts.map +1 -1
  62. package/dist/mutations/derive-edits.js +4 -1
  63. package/dist/mutations/derive-edits.js.map +1 -1
  64. package/dist/mutations/derive-repeats.d.ts +8 -0
  65. package/dist/mutations/derive-repeats.d.ts.map +1 -1
  66. package/dist/mutations/derive-repeats.js +31 -5
  67. package/dist/mutations/derive-repeats.js.map +1 -1
  68. package/dist/mutations/type-text.d.ts +6 -1
  69. package/dist/mutations/type-text.d.ts.map +1 -1
  70. package/dist/mutations/type-text.js +8 -1
  71. package/dist/mutations/type-text.js.map +1 -1
  72. package/dist/mutations/types.d.ts +21 -2
  73. package/dist/mutations/types.d.ts.map +1 -1
  74. package/dist/mutations/would-run.d.ts +26 -0
  75. package/dist/mutations/would-run.d.ts.map +1 -0
  76. package/dist/mutations/would-run.js +58 -0
  77. package/dist/mutations/would-run.js.map +1 -0
  78. package/dist/ops/lineage.d.ts +12 -1
  79. package/dist/ops/lineage.d.ts.map +1 -1
  80. package/dist/ops/lineage.js +9 -2
  81. package/dist/ops/lineage.js.map +1 -1
  82. package/dist/ops/log.d.ts +38 -0
  83. package/dist/ops/log.d.ts.map +1 -1
  84. package/dist/ops/log.js +7 -0
  85. package/dist/ops/log.js.map +1 -1
  86. package/dist/ops/undo.d.ts.map +1 -1
  87. package/dist/ops/undo.js +9 -0
  88. package/dist/ops/undo.js.map +1 -1
  89. package/dist/past.d.ts +3 -1
  90. package/dist/past.d.ts.map +1 -1
  91. package/dist/past.js +9 -1
  92. package/dist/past.js.map +1 -1
  93. package/dist/permissions/sight.d.ts +16 -0
  94. package/dist/permissions/sight.d.ts.map +1 -1
  95. package/dist/permissions/sight.js +16 -0
  96. package/dist/permissions/sight.js.map +1 -1
  97. package/dist/publish.d.ts +7 -2
  98. package/dist/publish.d.ts.map +1 -1
  99. package/dist/publish.js.map +1 -1
  100. package/dist/publishing.d.ts +4 -1
  101. package/dist/publishing.d.ts.map +1 -1
  102. package/dist/publishing.js +75 -15
  103. package/dist/publishing.js.map +1 -1
  104. package/dist/scaffold/agents.js +1 -1
  105. package/dist/scaffold/from-template.d.ts.map +1 -1
  106. package/dist/scaffold/from-template.js +2 -2
  107. package/dist/scaffold/from-template.js.map +1 -1
  108. package/dist/scaffold/names.d.ts +10 -0
  109. package/dist/scaffold/names.d.ts.map +1 -1
  110. package/dist/scaffold/names.js +20 -1
  111. package/dist/scaffold/names.js.map +1 -1
  112. package/dist/scaffold/ui.d.ts.map +1 -1
  113. package/dist/scaffold/ui.js +9 -8
  114. package/dist/scaffold/ui.js.map +1 -1
  115. package/dist/schema/figures.d.ts +9 -2
  116. package/dist/schema/figures.d.ts.map +1 -1
  117. package/dist/schema/figures.js +38 -9
  118. package/dist/schema/figures.js.map +1 -1
  119. package/dist/schema/linked.d.ts +7 -1
  120. package/dist/schema/linked.d.ts.map +1 -1
  121. package/dist/schema/linked.js.map +1 -1
  122. package/dist/seen.d.ts.map +1 -1
  123. package/dist/seen.js +25 -3
  124. package/dist/seen.js.map +1 -1
  125. package/dist/store.d.ts +31 -1
  126. package/dist/store.d.ts.map +1 -1
  127. package/dist/store.js +171 -33
  128. package/dist/store.js.map +1 -1
  129. package/dist/subscriptions.d.ts +12 -0
  130. package/dist/subscriptions.d.ts.map +1 -1
  131. package/dist/subscriptions.js +10 -1
  132. package/dist/subscriptions.js.map +1 -1
  133. package/dist/sync/mcp.d.ts +8 -0
  134. package/dist/sync/mcp.d.ts.map +1 -1
  135. package/dist/sync/mcp.js +2 -0
  136. package/dist/sync/mcp.js.map +1 -1
  137. package/dist/text/typing-hook.d.ts +2 -2
  138. package/dist/text/typing-hook.d.ts.map +1 -1
  139. package/dist/theme/fonts.d.ts +10 -0
  140. package/dist/theme/fonts.d.ts.map +1 -1
  141. package/dist/theme/fonts.js +25 -1
  142. package/dist/theme/fonts.js.map +1 -1
  143. package/dist/version.d.ts +1 -1
  144. package/dist/version.js +1 -1
  145. package/dist/views/kept.d.ts.map +1 -1
  146. package/dist/views/kept.js +3 -0
  147. package/dist/views/kept.js.map +1 -1
  148. package/package.json +1 -1
package/dist/store.js CHANGED
@@ -10,9 +10,11 @@ import { nodeRefArgs } from "./mutations/node-ref.js";
10
10
  import { verifyFold } from "./integrity.js";
11
11
  import { evaluate } from "./invariants/engine.js";
12
12
  import { compileMutation } from "./mutations/define-mutation.js";
13
+ import { wouldRun } from "./mutations/would-run.js";
13
14
  import { actSpelledAs, nearestAct } from "./mutations/act-spelling.js";
14
15
  import { InvalidArguments } from "./mutations/invalid-arguments.js";
15
- import { deriveMutations, derivedVia } from "./mutations/derive-edits.js";
16
+ import { deriveMutations, derivedVia, removeVia } from "./mutations/derive-edits.js";
17
+ import { repeatingFields, repeatMakes } from "./mutations/derive-repeats.js";
16
18
  import { namesMade, resolveMade } from "./mutations/made.js";
17
19
  import { OperationLog } from "./ops/log.js";
18
20
  import { lineageOf } from "./ops/lineage.js";
@@ -24,7 +26,7 @@ import { actingAs, grantsTo, isSystem, permits, permittedMutations, subjectKinds
24
26
  import { namesUnseen, redact } from "./ops/withheld.js";
25
27
  import { writersBehind } from "./ops/writers.js";
26
28
  import { nounOf } from "./schema/define-node.js";
27
- import { behind, fieldsJudged, partialSight, recordsOf } from "./permissions/sight.js";
29
+ import { behind, fieldsJudged, mayKeepField, partialSight, recordsOf } from "./permissions/sight.js";
28
30
  import { PermissionDeniedError } from "./permissions/types.js";
29
31
  import { checkUndo, UndoBlockedError, undoneInverses, undoPrimitives } from "./ops/undo.js";
30
32
  import { validateGraph } from "./validate-graph.js";
@@ -406,6 +408,8 @@ export class Store {
406
408
  break;
407
409
  }
408
410
  }
411
+ // No op in the tail says a set: the one the checkpoint kept stands, since the op that said it was archived with it (FR-12, FR-23).
412
+ said ??= this.log.horizonEpoch()?.enabledModules;
409
413
  const projection = held && said === held.said ? held.projection : resolveModules(this.declaredModules, said ?? this.startingModules);
410
414
  this.projected = { projection, length: all.length, last, said };
411
415
  return projection;
@@ -503,8 +507,12 @@ export class Store {
503
507
  return [...new Set(all.filter((one) => one.derived?.relation === call?.args["kind"]).flatMap((one) => derivedVia(this.schema, all, one) ?? []))];
504
508
  }
505
509
  // Typing into a field: whoever may run an act that writes that field of that record.
506
- if (mutation !== undefined && mutation === this.typer)
507
- return TYPING.via(this.schema, [...this.mutations.values()], this.subjectKindOf(call, principal), call?.args["field"]);
510
+ if (mutation !== undefined && mutation === this.typer) {
511
+ // Through an act that could run on this record now: its module on, its own rule holding for the record as this principal is served it (`wouldRun`).
512
+ const served = principal === undefined || isSystem(principal) ? this.graph : seenBy(this, principal).graph;
513
+ const allows = (writer) => !this.mutationDisabled(writer) && wouldRun(writer, this.schema, served, call?.args["node"], call?.args["field"], principal && actingAs(principal).id);
514
+ return TYPING.via(this.schema, [...this.mutations.values()], this.subjectKindOf(call, principal), call?.args["field"], allows);
515
+ }
508
516
  if (!mutation?.derived)
509
517
  return undefined;
510
518
  return derivedVia(this.schema, [...this.mutations.values()], mutation);
@@ -557,7 +565,14 @@ export class Store {
557
565
  // Publishing is the declaration's `publishers`, not a grant's.
558
566
  if (mutation && mutation === this.publisher)
559
567
  return PUBLISHING.verdict(this, principal);
560
- const id = mutation?.subject && call.args[mutation.subject.arg];
568
+ /*
569
+ * KEEPING A LINK ONLY THE DERIVED ACTS MAKE is judged as they are: on
570
+ * the record it leaves, so riding everybody's grant it reaches only the
571
+ * links of records this principal made (W-201). A link a declared act
572
+ * makes or breaks is that act's, which carries no such rule.
573
+ */
574
+ const keeping = mutation !== undefined && mutation === this.keeper && keepLinkVia(this.mutations.values(), call.args["kind"]).length === 0;
575
+ const id = keeping ? call.args["from"] : mutation?.subject && call.args[mutation.subject.arg];
561
576
  /*
562
577
  * An own-end grant holds the records the call names at the far end of
563
578
  * what it links, and every link the act would make or break: a connect
@@ -565,7 +580,8 @@ export class Store {
565
580
  * names. A call that cannot be compiled is refused by `apply`, in its
566
581
  * own words.
567
582
  */
568
- return permits(this.policy, principal, call.name, this.subjectKindOf(call, principal), this.viaOf(mutation, call, principal), typeof id === "string" ? id : undefined, this.words, () => {
583
+ const kind = this.subjectKindOf(call, principal);
584
+ const verdict = permits(this.policy, principal, call.name, kind, this.viaOf(mutation, call, principal), typeof id === "string" ? id : undefined, this.words, () => {
569
585
  const me = actingAs(principal).id;
570
586
  const far = this.namedIn(call, false);
571
587
  try {
@@ -579,9 +595,43 @@ export class Store {
579
595
  return far;
580
596
  },
581
597
  // Who made the record a derived act is about: riding only everybody's grant, it reaches what this principal made (W-201).
582
- mutation !== undefined && (mutation.derived !== undefined || mutation === this.typer) ? () => recordsOf(this.log).creatorOf(id) : undefined,
598
+ mutation !== undefined && (mutation.derived !== undefined || mutation === this.typer || keeping) ? () => this.madeBy(mutation, call, id) : undefined,
583
599
  // Taking a record out, or folding it away, rides the making of the kind only over what this principal made (W-201).
584
600
  mutation?.derived !== undefined && UNMAKING.has(mutation.derived.act));
601
+ if (!verdict.ok || !mutation?.derived || !this.repeatMakes(mutation, call, id, principal))
602
+ return verdict;
603
+ /*
604
+ * AND A CHANGE TO ONE DAY THAT MAKES A RECORD ("change just this one",
605
+ * "change from here" past the first day) rides the kind's edit for the
606
+ * series, and the making of the kind for the record it makes: who may
607
+ * retitle a shift has not been trusted with making one.
608
+ */
609
+ return permits(this.policy, principal, call.name, kind, removeVia([...this.mutations.values()], mutation.derived.kind), typeof id === "string" ? id : undefined, this.words);
610
+ }
611
+ /** Whether a call of a repeat act makes a record (`repeatMakes`), read off the series as this principal is served it. */
612
+ repeatMakes(mutation, call, id, principal) {
613
+ const act = mutation.derived.act;
614
+ if (act !== "detach" && act !== "split")
615
+ return false;
616
+ const definition = this.schema.tryDefinition(mutation.derived.kind);
617
+ if (!definition)
618
+ return false;
619
+ const node = typeof id === "string" ? (isSystem(principal) ? this.graph : seenBy(this, principal).graph).getNode(id) : undefined;
620
+ return repeatMakes(act, call.args, node, repeatingFields(definition));
621
+ }
622
+ /**
623
+ * WHO MADE WHAT A DERIVED ACT CHANGES (W-201): its subject — and, for a
624
+ * merge, the record it folds into too, which takes every tie of the one
625
+ * folded away. Both are to be one principal's for the merge to be theirs;
626
+ * asked before that one is chosen, the subject alone.
627
+ */
628
+ madeBy(mutation, call, id) {
629
+ const records = recordsOf(this.log);
630
+ const by = records.creatorOf(id);
631
+ const into = call.args["into"];
632
+ if (mutation.derived?.act === "merge" && typeof into === "string" && records.creatorOf(into) !== by)
633
+ return undefined;
634
+ return by;
585
635
  }
586
636
  /** The ids a call names in its record arguments: its subject's too, unless `subject` is false (the far ends of what it links). */
587
637
  namedIn(call, subject = true) {
@@ -661,7 +711,11 @@ export class Store {
661
711
  */
662
712
  sighted(author) {
663
713
  // Who `$me` is: the person, for an agent acting for one (Roadmap f-act-as-me).
664
- const me = { me: author && actingAs(author).id };
714
+ const me = {
715
+ me: author && actingAs(author).id,
716
+ everHeld: (id) => recordsOf(this.log).kindOf(id) !== undefined,
717
+ keeps: (kind, field) => mayKeepField(this.policy?.sees, kind, field),
718
+ };
665
719
  if (author === undefined || !hidesFrom(this, author))
666
720
  return me;
667
721
  // And a seat that writes a value naming what it may not see has written its own words, though the store held them already.
@@ -841,8 +895,15 @@ export class Store {
841
895
  */
842
896
  if (typeof id !== "string")
843
897
  return subject.kinds !== "*" && subject.kinds.length === 1 ? subject.kinds[0] : undefined;
844
- // A record the principal may not see is judged as one that is not there: its kind is not the policy's to read aloud (FR-55).
845
- if (!isSystem(principal) && !seesId(this, principal)(id))
898
+ /*
899
+ * A record the principal is not served is judged as one that is not
900
+ * there: its kind is not the policy's to read aloud (FR-55). Served,
901
+ * not only in sight, as `missingFor` reads it: a record withheld whole
902
+ * for a field it cannot do without answered a grant drawn by kind as
903
+ * its kind, and an absent id as no kind, so the refusal told the two
904
+ * apart.
905
+ */
906
+ if (!isSystem(principal) && !seenBy(this, principal).graph.has(id))
846
907
  return undefined;
847
908
  return this.graph.getNode(id)?.kind;
848
909
  }
@@ -1546,25 +1607,9 @@ export class Store {
1546
1607
  * what you may have done — the mutation each operation ran, judged
1547
1608
  * again now.
1548
1609
  */
1549
- if (this.policy || this.publisher) {
1550
- for (const target of check.ops) {
1551
- if (!target.mutation)
1552
- continue;
1553
- /*
1554
- * AND AN ACT DONE AS ITS AUTHOR (`$me`, `ctx.me`) IS THEIRS TO TAKE
1555
- * BACK: it wrote their own record where its call never named it, so
1556
- * the same call by another would have done something else. "Vote for
1557
- * it" is everybody's to run, and undoing Ann's was taking back her vote.
1558
- */
1559
- const by = actingAs(target.author).id;
1560
- const names = (value) => JSON.stringify(value).includes(JSON.stringify(by));
1561
- const verdict = by !== undefined && by !== actingAs(author).id && !isSystem(author) && !names(target.mutation.args) && names(target.primitives)
1562
- ? { ok: false, refusal: { mutation: target.mutation.name, wouldNeed: [], message: "it was done as somebody else." } }
1563
- : this.permits(target.mutation, author);
1564
- if (!verdict.ok)
1565
- this.refuse({ ...verdict.refusal, message: `Not permitted to undo "${target.intent}": ${verdict.refusal.message}` }, author.id, true);
1566
- }
1567
- }
1610
+ const refused = this.undoRefusal(check.ops, author);
1611
+ if (refused)
1612
+ this.refuse(refused, author.id, true);
1568
1613
  const rollback = this.graph.snapshot();
1569
1614
  const before = this.violations();
1570
1615
  const ops = [];
@@ -1608,6 +1653,75 @@ export class Store {
1608
1653
  violationsAfter: after,
1609
1654
  };
1610
1655
  }
1656
+ /**
1657
+ * Whether this principal may take these batches back: the log's answer
1658
+ * (`canUndo`) and the policy's, the same judgment `undo` makes. What a
1659
+ * page offers to take back is asked here, so an undo it shows is never
1660
+ * one the store refuses.
1661
+ */
1662
+ mayUndo(batchIds, principal = this.by) {
1663
+ const check = this.canUndo(batchIds);
1664
+ if (!check.ok)
1665
+ return { ok: false, check };
1666
+ const refusal = this.undoRefusal(check.ops, principal);
1667
+ return refusal ? { ok: false, refusal } : { ok: true };
1668
+ }
1669
+ /** What refuses `author` taking these ops back, or nothing: what you may undo is what you may have done. */
1670
+ undoRefusal(ops, author) {
1671
+ if (this.policy || this.publisher) {
1672
+ for (const target of ops) {
1673
+ /*
1674
+ * AN UNDO OF AN UNDO PUTS BACK THE CHANGE IT TOOK BACK, and is judged
1675
+ * as that change — redo is one, and let through unasked it put back
1676
+ * an act its seat was refused. Undoing that again takes the change
1677
+ * back once more, judged as it again: however long the chain, the
1678
+ * change at its root.
1679
+ */
1680
+ const judged = this.rootOf(target);
1681
+ const by = actingAs(judged.author).id;
1682
+ if (!judged.mutation) {
1683
+ // A change that changed nothing (a suggestion, an announcement) is anybody's who sees it to take back: declining a suggestion is one.
1684
+ if (judged.primitives.length === 0)
1685
+ continue;
1686
+ /*
1687
+ * A CHANGE NO ACT MADE — an import, a sync, a connection, a host's
1688
+ * repair (`applyPrimitives`) — has no act to judge it as, and its
1689
+ * author was its authority. It is taken back by the system, by
1690
+ * whoever made it, or by a seat the policy grants every act.
1691
+ */
1692
+ if (this.policy && !isSystem(author) && by !== actingAs(author).id && !this.grantsEveryAct(author)) {
1693
+ return { mutation: "undo", wouldNeed: [], message: `Not permitted to undo "${target.intent}": no act made it, so only whoever did may take it back.` };
1694
+ }
1695
+ continue;
1696
+ }
1697
+ /*
1698
+ * AND AN ACT DONE AS ITS AUTHOR (`$me`, `ctx.me`) IS THEIRS TO TAKE
1699
+ * BACK: it wrote their own record where its call never named it, so
1700
+ * the same call by another would have done something else. "Vote for
1701
+ * it" is everybody's to run, and undoing Ann's was taking back her vote.
1702
+ */
1703
+ const names = (value) => JSON.stringify(value).includes(JSON.stringify(by));
1704
+ const verdict = by !== undefined && by !== actingAs(author).id && !isSystem(author) && !names(judged.mutation.args) && names(judged.primitives)
1705
+ ? { ok: false, refusal: { mutation: judged.mutation.name, wouldNeed: [], message: "it was done as somebody else." } }
1706
+ : this.permits(judged.mutation, author);
1707
+ if (!verdict.ok)
1708
+ return { ...verdict.refusal, message: `Not permitted to undo "${target.intent}": ${verdict.refusal.message}` };
1709
+ }
1710
+ }
1711
+ return undefined;
1712
+ }
1713
+ /** The change an undo of an undo puts back: the op at the root of its chain, or the last undo the log still holds of it. */
1714
+ rootOf(op) {
1715
+ let at = op;
1716
+ for (let undone = at.undoes === undefined ? undefined : this.log.get(at.undoes); undone !== undefined; undone = at.undoes === undefined ? undefined : this.log.get(at.undoes))
1717
+ at = undone;
1718
+ return at;
1719
+ }
1720
+ /** Whether the policy grants this principal every act, on every record: what taking back a change no act made asks of anybody but its author. */
1721
+ grantsEveryAct(principal) {
1722
+ const acting = actingAs(principal);
1723
+ return (this.policy?.grants ?? []).some((grant) => grantsTo(grant, acting) && grant.mutations === "*" && (grant.kinds === undefined || grant.kinds === "*") && !grant.self && !grant.ownEnd);
1724
+ }
1611
1725
  /** An author named as Activity names one, for a sentence that refuses them. */
1612
1726
  authorName(author) {
1613
1727
  return nameOfAuthor(author, { graph: this.graph, schema: this.schema });
@@ -1933,8 +2047,9 @@ export class Store {
1933
2047
  seq = Math.min(byOps, byDays);
1934
2048
  }
1935
2049
  // Never past a seq somebody is served at: a reader's past (`pastOf`) folds from the log, so the lowest pin is the furthest it goes.
1936
- // And readers' own (Roadmap f-publishing-lifecycle): what was published — or, before the first publish, where the log begins.
1937
- const readers = onPublish(this.publishing) ? [this.publishedSeq ?? 0] : [];
2050
+ // Readers of an app that publishes are served the app as each publish behind it left it, which it keeps (`published`, Roadmap
2051
+ // f-publishing-lifecycle); before the first publish — or with nothing loaded to say one — they are served where the log begins.
2052
+ const readers = onPublish(this.publishing) && this.publishedSeq === undefined ? [0] : [];
1938
2053
  const pinned = [...(options.pins ?? []), ...readers].filter((pin) => Number.isInteger(pin));
1939
2054
  if (pinned.length > 0)
1940
2055
  seq = Math.min(seq, ...pinned);
@@ -1947,8 +2062,19 @@ export class Store {
1947
2062
  const verified = this.verify();
1948
2063
  if (!verified.ok)
1949
2064
  throw new GraphError(`Cannot compact a store that does not verify: ${verified.reason}`);
2065
+ /*
2066
+ * And the app as readers were served it at each published seq behind it they may be served again: folded here where the
2067
+ * log still holds it, carried from the checkpoint before where it does not, so a horizon past old publishes loses none.
2068
+ */
2069
+ const carried = this.log.horizonEpoch()?.published ?? [];
2070
+ const published = (PUBLISHING.behind?.(this, seq) ?? []).flatMap((at) => (at >= this.log.horizon ? [this.epochAt(at)] : carried.filter((one) => one.seq === at)));
2071
+ return { ...this.epochAt(seq), ...(published.length > 0 && { published }) };
2072
+ }
2073
+ /** The checkpoint at `seq`, which the log holds, and what it keeps of the ops behind it — but for the published seqs behind it. */
2074
+ epochAt(seq) {
2075
+ const ops = this.log.all();
1950
2076
  // What the store keeps of who made what and what was removed is written here, from the log, never carried over from an epoch.
1951
- const { creators: _creators, removed: _removed, writers: _writers, ...checkpoint } = this.log.checkpointAt(this.schema, seq, { validate: this.validate });
2077
+ const { creators: _creators, removed: _removed, writers: _writers, published: _published, ...checkpoint } = this.log.checkpointAt(this.schema, seq, { validate: this.validate });
1952
2078
  /*
1953
2079
  * Who made each record behind it, so a store opened on it still knows (an `own` sight reads the log for it);
1954
2080
  * what each record removed behind it was, so a field naming one is still judged as naming it; and who wrote
@@ -1959,7 +2085,19 @@ export class Store {
1959
2085
  const records = recordsOf(this.log);
1960
2086
  const writers = writersBehind(this.log, seq, checkpoint.base.nodes, (value) => namesUnseen(value, (id) => !base.has(id) && records.kindOf(id) === undefined));
1961
2087
  const kept = (map) => map && Object.keys(map).length > 0;
1962
- return { ...checkpoint, ...(kept(creators) && { creators }), ...(removed && { removed }), ...(kept(writers) && { writers }) };
2088
+ /*
2089
+ * The modules on at its seq and how far readers were served there, which the ops before it said and which go to the
2090
+ * archive with them: so a module off stays off (FR-12) and a store whose publish ops were archived still knows what
2091
+ * stood published (Roadmap f-publishing-lifecycle).
2092
+ */
2093
+ return {
2094
+ ...checkpoint,
2095
+ ...(kept(creators) && { creators }),
2096
+ ...(removed && { removed }),
2097
+ ...(kept(writers) && { writers }),
2098
+ ...given("enabledModules", ops.slice(0, seq - this.log.horizon).reverse().find((op) => op.enabledModules)?.enabledModules ?? this.log.horizonEpoch()?.enabledModules),
2099
+ ...given("publishedThrough", PUBLISHING.seq?.(this, seq)),
2100
+ };
1963
2101
  }
1964
2102
  /**
1965
2103
  * MOVES THE UNDO HORIZON TO `checkpoint` (FR-23): the ops and epochs