@graview/core 0.1.49 → 0.1.50

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 (233) hide show
  1. package/dist/answer-seen.js +2 -2
  2. package/dist/answer-seen.js.map +1 -1
  3. package/dist/app.d.ts +11 -0
  4. package/dist/app.d.ts.map +1 -1
  5. package/dist/app.js.map +1 -1
  6. package/dist/capabilities.d.ts +7 -1
  7. package/dist/capabilities.d.ts.map +1 -1
  8. package/dist/capabilities.js.map +1 -1
  9. package/dist/cli/check/index.d.ts +1 -0
  10. package/dist/cli/check/index.d.ts.map +1 -1
  11. package/dist/cli/check/index.js +1 -0
  12. package/dist/cli/check/index.js.map +1 -1
  13. package/dist/cli/check/publishing.d.ts +13 -0
  14. package/dist/cli/check/publishing.d.ts.map +1 -0
  15. package/dist/cli/check/publishing.js +36 -0
  16. package/dist/cli/check/publishing.js.map +1 -0
  17. package/dist/cli/check.d.ts.map +1 -1
  18. package/dist/cli/check.js +2 -1
  19. package/dist/cli/check.js.map +1 -1
  20. package/dist/cli/describe.d.ts.map +1 -1
  21. package/dist/cli/describe.js +15 -0
  22. package/dist/cli/describe.js.map +1 -1
  23. package/dist/cli/index.d.ts +1 -0
  24. package/dist/cli/index.d.ts.map +1 -1
  25. package/dist/cli/index.js +3 -1
  26. package/dist/cli/index.js.map +1 -1
  27. package/dist/document/compiled.d.ts.map +1 -1
  28. package/dist/document/compiled.js +5 -0
  29. package/dist/document/compiled.js.map +1 -1
  30. package/dist/document/field-spec.d.ts.map +1 -1
  31. package/dist/document/field-spec.js +8 -2
  32. package/dist/document/field-spec.js.map +1 -1
  33. package/dist/document/format-findings.d.ts.map +1 -1
  34. package/dist/document/format-findings.js +5 -0
  35. package/dist/document/format-findings.js.map +1 -1
  36. package/dist/document/schema.d.ts +13 -0
  37. package/dist/document/schema.d.ts.map +1 -1
  38. package/dist/document/schema.js +16 -0
  39. package/dist/document/schema.js.map +1 -1
  40. package/dist/document/to-document.d.ts.map +1 -1
  41. package/dist/document/to-document.js +1 -0
  42. package/dist/document/to-document.js.map +1 -1
  43. package/dist/document.schema.json +42 -2
  44. package/dist/field-sight.js +5 -4
  45. package/dist/field-sight.js.map +1 -1
  46. package/dist/format-error.d.ts +54 -0
  47. package/dist/format-error.d.ts.map +1 -0
  48. package/dist/format-error.js +73 -0
  49. package/dist/format-error.js.map +1 -0
  50. package/dist/formats.d.ts +16 -13
  51. package/dist/formats.d.ts.map +1 -1
  52. package/dist/formats.js +18 -17
  53. package/dist/formats.js.map +1 -1
  54. package/dist/graph/diff.js +1 -1
  55. package/dist/graph/diff.js.map +1 -1
  56. package/dist/graph/edge-fields.d.ts +20 -0
  57. package/dist/graph/edge-fields.d.ts.map +1 -0
  58. package/dist/graph/edge-fields.js +21 -0
  59. package/dist/graph/edge-fields.js.map +1 -0
  60. package/dist/graph/graph.d.ts +3 -0
  61. package/dist/graph/graph.d.ts.map +1 -1
  62. package/dist/graph/graph.js +57 -4
  63. package/dist/graph/graph.js.map +1 -1
  64. package/dist/graph/primitives.d.ts +22 -2
  65. package/dist/graph/primitives.d.ts.map +1 -1
  66. package/dist/graph/primitives.js +20 -0
  67. package/dist/graph/primitives.js.map +1 -1
  68. package/dist/graph/types.d.ts +15 -1
  69. package/dist/graph/types.d.ts.map +1 -1
  70. package/dist/graph/types.js +0 -0
  71. package/dist/graph/types.js.map +1 -1
  72. package/dist/history.d.ts +23 -2
  73. package/dist/history.d.ts.map +1 -1
  74. package/dist/history.js +23 -3
  75. package/dist/history.js.map +1 -1
  76. package/dist/index.d.ts +17 -6
  77. package/dist/index.d.ts.map +1 -1
  78. package/dist/index.js +12 -2
  79. package/dist/index.js.map +1 -1
  80. package/dist/integrity.d.ts.map +1 -1
  81. package/dist/integrity.js +2 -1
  82. package/dist/integrity.js.map +1 -1
  83. package/dist/linked.d.ts +1 -0
  84. package/dist/linked.d.ts.map +1 -1
  85. package/dist/linked.js +1 -0
  86. package/dist/linked.js.map +1 -1
  87. package/dist/mutations/define-mutation.d.ts +4 -0
  88. package/dist/mutations/define-mutation.d.ts.map +1 -1
  89. package/dist/mutations/define-mutation.js +32 -8
  90. package/dist/mutations/define-mutation.js.map +1 -1
  91. package/dist/mutations/keep-link.d.ts.map +1 -1
  92. package/dist/mutations/keep-link.js +4 -2
  93. package/dist/mutations/keep-link.js.map +1 -1
  94. package/dist/mutations/type-text.d.ts +49 -0
  95. package/dist/mutations/type-text.d.ts.map +1 -0
  96. package/dist/mutations/type-text.js +106 -0
  97. package/dist/mutations/type-text.js.map +1 -0
  98. package/dist/mutations/types.d.ts +8 -0
  99. package/dist/mutations/types.d.ts.map +1 -1
  100. package/dist/mutations/visitor-form.js +1 -1
  101. package/dist/mutations/visitor-form.js.map +1 -1
  102. package/dist/ops/digest.d.ts +17 -6
  103. package/dist/ops/digest.d.ts.map +1 -1
  104. package/dist/ops/digest.js +36 -15
  105. package/dist/ops/digest.js.map +1 -1
  106. package/dist/ops/kept-fields.d.ts +1 -1
  107. package/dist/ops/kept-fields.d.ts.map +1 -1
  108. package/dist/ops/kept-fields.js +4 -1
  109. package/dist/ops/kept-fields.js.map +1 -1
  110. package/dist/ops/log.d.ts.map +1 -1
  111. package/dist/ops/log.js +3 -0
  112. package/dist/ops/log.js.map +1 -1
  113. package/dist/ops/provenance.d.ts.map +1 -1
  114. package/dist/ops/provenance.js +3 -1
  115. package/dist/ops/provenance.js.map +1 -1
  116. package/dist/ops/revisions.d.ts +8 -2
  117. package/dist/ops/revisions.d.ts.map +1 -1
  118. package/dist/ops/revisions.js +12 -3
  119. package/dist/ops/revisions.js.map +1 -1
  120. package/dist/ops/served.d.ts.map +1 -1
  121. package/dist/ops/served.js +30 -4
  122. package/dist/ops/served.js.map +1 -1
  123. package/dist/ops/suggestions.d.ts +14 -0
  124. package/dist/ops/suggestions.d.ts.map +1 -1
  125. package/dist/ops/suggestions.js.map +1 -1
  126. package/dist/ops/types.d.ts +43 -3
  127. package/dist/ops/types.d.ts.map +1 -1
  128. package/dist/ops/types.js +16 -1
  129. package/dist/ops/types.js.map +1 -1
  130. package/dist/ops/undo.d.ts +15 -2
  131. package/dist/ops/undo.d.ts.map +1 -1
  132. package/dist/ops/undo.js +22 -3
  133. package/dist/ops/undo.js.map +1 -1
  134. package/dist/ops/withheld.d.ts.map +1 -1
  135. package/dist/ops/withheld.js +4 -0
  136. package/dist/ops/withheld.js.map +1 -1
  137. package/dist/ops/writers.d.ts.map +1 -1
  138. package/dist/ops/writers.js +3 -0
  139. package/dist/ops/writers.js.map +1 -1
  140. package/dist/past.d.ts +1 -0
  141. package/dist/past.d.ts.map +1 -1
  142. package/dist/past.js +9 -2
  143. package/dist/past.js.map +1 -1
  144. package/dist/persistence/memory.d.ts +1 -1
  145. package/dist/persistence/memory.d.ts.map +1 -1
  146. package/dist/persistence/memory.js +5 -1
  147. package/dist/persistence/memory.js.map +1 -1
  148. package/dist/persistence/sql.d.ts +1 -1
  149. package/dist/persistence/sql.d.ts.map +1 -1
  150. package/dist/persistence/sql.js +45 -5
  151. package/dist/persistence/sql.js.map +1 -1
  152. package/dist/persistence/sqlite.d.ts +1 -0
  153. package/dist/persistence/sqlite.d.ts.map +1 -1
  154. package/dist/persistence/sqlite.js +1 -0
  155. package/dist/persistence/sqlite.js.map +1 -1
  156. package/dist/persistence/types.d.ts +34 -1
  157. package/dist/persistence/types.d.ts.map +1 -1
  158. package/dist/persistence/types.js +35 -1
  159. package/dist/persistence/types.js.map +1 -1
  160. package/dist/presence.d.ts +2 -1
  161. package/dist/presence.d.ts.map +1 -1
  162. package/dist/presence.js +2 -1
  163. package/dist/presence.js.map +1 -1
  164. package/dist/publish.d.ts +78 -0
  165. package/dist/publish.d.ts.map +1 -0
  166. package/dist/publish.js +23 -0
  167. package/dist/publish.js.map +1 -0
  168. package/dist/publishing.d.ts +10 -0
  169. package/dist/publishing.d.ts.map +1 -0
  170. package/dist/publishing.js +168 -0
  171. package/dist/publishing.js.map +1 -0
  172. package/dist/refused.d.ts +13 -6
  173. package/dist/refused.d.ts.map +1 -1
  174. package/dist/refused.js +13 -6
  175. package/dist/refused.js.map +1 -1
  176. package/dist/schema/collaborative.d.ts +14 -0
  177. package/dist/schema/collaborative.d.ts.map +1 -0
  178. package/dist/schema/collaborative.js +65 -0
  179. package/dist/schema/collaborative.js.map +1 -0
  180. package/dist/seen.d.ts +44 -1
  181. package/dist/seen.d.ts.map +1 -1
  182. package/dist/seen.js +59 -14
  183. package/dist/seen.js.map +1 -1
  184. package/dist/store.d.ts +78 -4
  185. package/dist/store.d.ts.map +1 -1
  186. package/dist/store.js +118 -24
  187. package/dist/store.js.map +1 -1
  188. package/dist/subscriptions.d.ts.map +1 -1
  189. package/dist/subscriptions.js +13 -1
  190. package/dist/subscriptions.js.map +1 -1
  191. package/dist/sync/engine.d.ts.map +1 -1
  192. package/dist/sync/engine.js +43 -15
  193. package/dist/sync/engine.js.map +1 -1
  194. package/dist/sync/index.d.ts +1 -1
  195. package/dist/sync/index.d.ts.map +1 -1
  196. package/dist/sync/index.js +1 -1
  197. package/dist/sync/index.js.map +1 -1
  198. package/dist/sync/types.d.ts +10 -0
  199. package/dist/sync/types.d.ts.map +1 -1
  200. package/dist/sync/types.js +8 -0
  201. package/dist/sync/types.js.map +1 -1
  202. package/dist/testing.d.ts.map +1 -1
  203. package/dist/testing.js +1 -0
  204. package/dist/testing.js.map +1 -1
  205. package/dist/text/diff.d.ts +10 -0
  206. package/dist/text/diff.d.ts.map +1 -0
  207. package/dist/text/diff.js +26 -0
  208. package/dist/text/diff.js.map +1 -0
  209. package/dist/text/since.d.ts +29 -0
  210. package/dist/text/since.d.ts.map +1 -0
  211. package/dist/text/since.js +66 -0
  212. package/dist/text/since.js.map +1 -0
  213. package/dist/text/splice.d.ts +51 -0
  214. package/dist/text/splice.d.ts.map +1 -0
  215. package/dist/text/splice.js +71 -0
  216. package/dist/text/splice.js.map +1 -0
  217. package/dist/text/typing-hook.d.ts +37 -0
  218. package/dist/text/typing-hook.d.ts.map +1 -0
  219. package/dist/text/typing-hook.js +16 -0
  220. package/dist/text/typing-hook.js.map +1 -0
  221. package/dist/tidying.d.ts +1 -1
  222. package/dist/tidying.d.ts.map +1 -1
  223. package/dist/typing.d.ts +2 -0
  224. package/dist/typing.d.ts.map +1 -0
  225. package/dist/typing.js +109 -0
  226. package/dist/typing.js.map +1 -0
  227. package/dist/version.d.ts +1 -1
  228. package/dist/version.js +1 -1
  229. package/dist/who.d.ts +1 -1
  230. package/dist/who.d.ts.map +1 -1
  231. package/dist/who.js +5 -2
  232. package/dist/who.js.map +1 -1
  233. package/package.json +12 -2
package/dist/store.js CHANGED
@@ -17,6 +17,7 @@ import { namesMade, resolveMade } from "./mutations/made.js";
17
17
  import { OperationLog } from "./ops/log.js";
18
18
  import { lineageOf } from "./ops/lineage.js";
19
19
  import { KEEP_LINK, keepLinkMutation, keepLinkVia } from "./mutations/keep-link.js";
20
+ import { TYPE_TEXT, TYPING } from "./text/typing-hook.js";
20
21
  import { declinedIn, isAutomated, tidyingRefusal } from "./ops/tidying.js";
21
22
  import { nameOfAuthor } from "./who.js";
22
23
  import { actingAs, grantsTo, isSystem, permits, permittedMutations, subjectKinds } from "./permissions/policy.js";
@@ -25,13 +26,14 @@ import { writersBehind } from "./ops/writers.js";
25
26
  import { nounOf } from "./schema/define-node.js";
26
27
  import { behind, fieldsJudged, partialSight, recordsOf } from "./permissions/sight.js";
27
28
  import { PermissionDeniedError } from "./permissions/types.js";
28
- import { checkUndo, UndoBlockedError, undoPrimitives } from "./ops/undo.js";
29
+ import { checkUndo, UndoBlockedError, undoneInverses, undoPrimitives } from "./ops/undo.js";
29
30
  import { validateGraph } from "./validate-graph.js";
30
31
  import { tellTheWatchOfAStore, tellTheWatchOfAnAuthor, tellTheWatchOfARefusal, tellTheWatchOfAnInputRefusal } from "./watched.js";
31
32
  import { capitalize } from "./capital.js";
32
33
  import { dayOf } from "./clock.js";
33
34
  import { pluralOf } from "./schema/define-node.js";
34
35
  import { linkedReaderOf } from "./schema/linked.js";
36
+ import { onPublish, PUBLISH, PUBLISHING } from "./publish.js";
35
37
  /** A change asked of a store of the past (Roadmap f-playback): the past is read, never changed. */
36
38
  export class PastError extends GraphError {
37
39
  constructor(at) {
@@ -174,9 +176,13 @@ export class Store {
174
176
  mutations = new Map();
175
177
  /** The store's own "Keep this link" (FR-172), when the app declared none by that name. */
176
178
  keeper;
179
+ /** "Type text", when the store answers to its own (Roadmap f-collab-text). */
180
+ typer;
177
181
  invariants;
178
182
  invariantOptions;
179
183
  policy;
184
+ /** When edits reach readers (`StoreOptions.publishing`). */
185
+ publishing;
180
186
  /**
181
187
  * The intelligence the app declared, so a surface can read the DOORS.
182
188
  *
@@ -238,6 +244,7 @@ export class Store {
238
244
  if (this.timeZone)
239
245
  this.ticked = { day: isoToday(this.timeZone), on: isoToday(this.timeZone) };
240
246
  this.policy = options.policy;
247
+ this.publishing = options.publishing;
241
248
  this.by = options.principal ?? HUMAN;
242
249
  this.intelligence = options.intelligence ?? [];
243
250
  for (const provider of options.intelligence ?? []) {
@@ -336,6 +343,35 @@ export class Store {
336
343
  * between reads and re-read only past what it has read — a log only ever
337
344
  * grows at its end, and a rebase that cut it is read again from the start.
338
345
  */
346
+ /**
347
+ * WHERE WHAT THIS STORE SERVES ENDS: the ops before this seq are in what
348
+ * it reads, none after. A store's own is its head (`log.length`), a past
349
+ * (`pastOf(store, seq)`) is `seq`, and a seat's view (`seenBy`) is the
350
+ * seq that seat is served through — the head today. A seat's served state
351
+ * may end before the head, chosen by policy (docs/stability.md, "Served
352
+ * state and the head"), as narrowing; a write is always judged at the
353
+ * head, whatever this says. Read-only: nothing sets it.
354
+ */
355
+ get servedSeq() {
356
+ return this.log.length;
357
+ }
358
+ /**
359
+ * HOW FAR READERS ARE SERVED (Roadmap f-publishing-lifecycle): the
360
+ * `publishedThrough` of the last publish that stands, or `undefined`
361
+ * when nothing was ever published. Read from the log, as the modules
362
+ * are, and kept between reads while the log is the one it was read from.
363
+ */
364
+ get publishedSeq() {
365
+ return PUBLISHING.seq?.(this);
366
+ }
367
+ /**
368
+ * "PUBLISH" (Roadmap f-publishing-lifecycle): answered by every store of
369
+ * an app whose readers are served what was published, and that declared
370
+ * no act by the name, once `@graview/core/publishing` is loaded.
371
+ */
372
+ get publisher() {
373
+ return PUBLISHING.act?.(this, this.mutations);
374
+ }
339
375
  get modules() {
340
376
  const all = this.log.all();
341
377
  const last = all.at(-1)?.id;
@@ -405,7 +441,7 @@ export class Store {
405
441
  return op;
406
442
  }
407
443
  mutation(name) {
408
- const found = this.mutations.get(this.spelled(name));
444
+ const found = this.mutations.get(this.spelled(name)) ?? (name === PUBLISH ? this.publisher : undefined);
409
445
  if (!found) {
410
446
  throw new UnknownMutationError(name, [...this.mutations.keys()]);
411
447
  }
@@ -422,6 +458,9 @@ export class Store {
422
458
  * as it was given, to be refused by that name.
423
459
  */
424
460
  spelled(name) {
461
+ // "Type text" (Roadmap f-collab-text): answered by every store once `@graview/core/typing` is loaded, and offered by no list.
462
+ if (name === TYPE_TEXT && !this.typer && TYPING.typeText && !this.past && !this.mutations.has(name))
463
+ this.mutations.set(name, (this.typer = TYPING.typeText(this)));
425
464
  return this.mutations.has(name) ? name : (actSpelledAs(name, this.mutations.keys()) ?? name);
426
465
  }
427
466
  /** A call with its act's name as the store spells it, so the log and every check read the one name. */
@@ -436,10 +475,10 @@ export class Store {
436
475
  return mutation.derived !== undefined && this.modules.disabledKinds.has(mutation.derived.kind);
437
476
  }
438
477
  allMutations() {
439
- return [...this.mutations.values()].filter((mutation) => mutation !== this.keeper && !this.mutationDisabled(mutation));
478
+ return [...this.mutations.values(), ...(this.publisher ? [this.publisher] : [])].filter((mutation) => mutation !== this.keeper && mutation !== this.typer && !this.mutationDisabled(mutation));
440
479
  }
441
480
  /** The declared acts a derived act resolves its permission through; keeping a link, those that make or break its relation. */
442
- viaOf(mutation, call) {
481
+ viaOf(mutation, call, principal) {
443
482
  if (mutation !== undefined && mutation === this.keeper) {
444
483
  const declared = keepLinkVia(this.mutations.values(), call?.args["kind"]);
445
484
  if (declared.length > 0)
@@ -448,6 +487,9 @@ export class Store {
448
487
  const all = [...this.mutations.values()];
449
488
  return [...new Set(all.filter((one) => one.derived?.relation === call?.args["kind"]).flatMap((one) => derivedVia(this.schema, all, one) ?? []))];
450
489
  }
490
+ // Typing into a field: whoever may run an act that writes that field of that record.
491
+ if (mutation !== undefined && mutation === this.typer)
492
+ return TYPING.via(this.schema, [...this.mutations.values()], this.subjectKindOf(call, principal), call?.args["field"]);
451
493
  if (!mutation?.derived)
452
494
  return undefined;
453
495
  return derivedVia(this.schema, [...this.mutations.values()], mutation);
@@ -496,7 +538,10 @@ export class Store {
496
538
  const narrowed = this.refusesAgent(call, principal) ?? this.namesTurnedOff(call, principal);
497
539
  if (narrowed)
498
540
  return { ok: false, refusal: narrowed };
499
- const mutation = this.mutations.get(call.name);
541
+ const mutation = this.mutations.get(call.name) ?? (call.name === PUBLISH ? this.publisher : undefined);
542
+ // Publishing is the declaration's `publishers`, not a grant's.
543
+ if (mutation && mutation === this.publisher)
544
+ return PUBLISHING.verdict(this, principal);
500
545
  const id = mutation?.subject && call.args[mutation.subject.arg];
501
546
  /*
502
547
  * An own-end grant holds the records the call names at the far end of
@@ -505,7 +550,7 @@ export class Store {
505
550
  * names. A call that cannot be compiled is refused by `apply`, in its
506
551
  * own words.
507
552
  */
508
- return permits(this.policy, principal, call.name, this.subjectKindOf(call, principal), this.viaOf(mutation, call), typeof id === "string" ? id : undefined, this.words, () => {
553
+ return permits(this.policy, principal, call.name, this.subjectKindOf(call, principal), this.viaOf(mutation, call, principal), typeof id === "string" ? id : undefined, this.words, () => {
509
554
  const me = actingAs(principal).id;
510
555
  const far = this.namedIn(call, false);
511
556
  try {
@@ -519,7 +564,7 @@ export class Store {
519
564
  return far;
520
565
  },
521
566
  // Who made the record a derived act is about: riding only everybody's grant, it reaches what this principal made (W-201).
522
- mutation?.derived && (() => recordsOf(this.log).creatorOf(id)),
567
+ mutation !== undefined && (mutation.derived !== undefined || mutation === this.typer) ? () => recordsOf(this.log).creatorOf(id) : undefined,
523
568
  // Taking a record out, or folding it away, rides the making of the kind only over what this principal made (W-201).
524
569
  mutation?.derived !== undefined && UNMAKING.has(mutation.derived.act));
525
570
  }
@@ -608,6 +653,18 @@ export class Store {
608
653
  const hidden = seesId(this, author);
609
654
  return { ...me, seen: seenBy(this, author).graph, keepUnchanged: (value) => namesUnseen(value, hidden), seesInFull: seesEveryRecord(this, author) };
610
655
  }
656
+ /**
657
+ * WHETHER ONE FIELD OF A RECORD IS TYPED TOGETHER (Roadmap f-collab-text),
658
+ * read off the declaration of the record's kind, never off its value:
659
+ * `isCollaborativeField`. False for a record that is not here.
660
+ */
661
+ collaborative(node, field) {
662
+ return TYPING.collaborative?.(this.schema, this.graph.getNode(node)?.kind, field) ?? false;
663
+ }
664
+ /** A whole value written over text typed together, as a splice, where `@graview/core/typing` is loaded; as written otherwise. */
665
+ asSplices(primitives, base) {
666
+ return TYPING.asSplices ? TYPING.asSplices(this, primitives, base) : primitives;
667
+ }
611
668
  /**
612
669
  * A CALL THAT NAMES A RECORD THAT IS NOT THERE FOR ITS CALLER IS REFUSED
613
670
  * AS MISSING (FR-02, FR-55) — one that does not exist, and one the caller
@@ -652,12 +709,17 @@ export class Store {
652
709
  });
653
710
  }
654
711
  /**
655
- * The store as one principal may see it: its graph, log, history and
656
- * problems hold only what the policy's `sees` lets them see, and every
657
- * act still comes here. With no `sees`, this store itself (see `seen.ts`).
712
+ * The app as this seat is served it: its graph, log, history and problems
713
+ * hold only what the policy's `sees` lets the seat see, through the seq it
714
+ * is served (`servedSeq`), and every act still comes here, judged at the
715
+ * head. A reader of an `on-publish` app is served what was published; a
716
+ * collaborator the head, or — `{ as: "published" }` — what readers are
717
+ * served, to preview it. With no `sees` and nothing held back, this store
718
+ * itself (see `seen.ts`). Nothing promises it is the head, nor that one
719
+ * principal has one view.
658
720
  */
659
- seenBy(principal) {
660
- return seenBy(this, principal);
721
+ seenBy(principal, options) {
722
+ return seenBy(this, principal, options);
661
723
  }
662
724
  /** Whether a principal may see one record (the policy's `sees`). */
663
725
  sees(principal, id) {
@@ -734,11 +796,13 @@ export class Store {
734
796
  */
735
797
  permittedMutations(principal = HUMAN) {
736
798
  const all = this.allMutations();
737
- const declared = permittedMutations(this.policy, principal, all.filter((mutation) => !mutation.derived));
799
+ const declared = permittedMutations(this.policy, principal, all.filter((mutation) => !mutation.derived && mutation !== this.publisher));
800
+ // Publishing is offered to the declaration's publishers (Roadmap f-publishing-lifecycle).
801
+ const publishing = this.publisher && PUBLISHING.verdict(this, principal).ok ? [this.publisher] : [];
738
802
  // A derived act is offered when the kind's own acts it rides are.
739
803
  const derived = all.filter((mutation) => mutation.derived !== undefined &&
740
804
  permits(this.policy, principal, mutation.name, mutation.derived.kind, this.viaOf(mutation), actingAs(principal).id).ok);
741
- return [...declared, ...derived];
805
+ return [...declared, ...publishing, ...derived];
742
806
  }
743
807
  /**
744
808
  * The kind a call acts on, read off the mutation's own subject binding.
@@ -918,7 +982,7 @@ export class Store {
918
982
  call = this.spelledCall(call);
919
983
  const definition = this.mutation(call.name);
920
984
  const compiled = compileMutation(this.graph, definition, call.args, this.sighted(options.author));
921
- return this.previewPrimitives(compiled.primitives, {
985
+ return this.previewPrimitives(this.asSplices(compiled.primitives), {
922
986
  reads: compiled.reads,
923
987
  writes: compiled.writes,
924
988
  intent: compiled.intent,
@@ -971,6 +1035,9 @@ export class Store {
971
1035
  mintBatch: () => PREVIEW_BATCH,
972
1036
  rehearsing: true,
973
1037
  });
1038
+ // Its own acts, so "Type text" reads the text back off the rehearsal's log, as the apply would off this one's.
1039
+ const typer = this.typer && TYPING.typeText(twin);
1040
+ Object.assign(twin, { typer, mutations: typer ? new Map(this.mutations).set(TYPE_TEXT, typer) : new Map(this.mutations) });
974
1041
  return twin;
975
1042
  }
976
1043
  previewPrimitives(primitives, meta) {
@@ -1036,7 +1103,7 @@ export class Store {
1036
1103
  * was brought in for, and the narrower of the two wins.
1037
1104
  */
1038
1105
  const permitted = (call) => {
1039
- const verdict = this.policy ? this.permits(call, author) : undefined;
1106
+ const verdict = this.policy || (this.publisher && call.name === PUBLISH) ? this.permits(call, author) : undefined;
1040
1107
  if (verdict && !verdict.ok)
1041
1108
  this.refuse(verdict.refusal, author.id);
1042
1109
  };
@@ -1063,18 +1130,35 @@ export class Store {
1063
1130
  tellTheWatchOfAnAuthor(author.id);
1064
1131
  /* The first record each call made, for a later call that names it (FR-193). */
1065
1132
  const madeIds = [];
1133
+ const minted = [];
1134
+ /*
1135
+ * A CLIENT'S OWN ID, A HINT (Roadmap f-1-0-client-ids). A call it applied
1136
+ * first says the id it minted there (`mintedHint`); the record is made
1137
+ * under it when it is free, and minted as ever when somebody holds it —
1138
+ * and a later call of this change naming the hint names the record made.
1139
+ */
1140
+ const renamed = new Map();
1141
+ const rename = (value) => (typeof value === "string" ? (renamed.get(value) ?? value) : Array.isArray(value) ? value.map(rename) : value);
1066
1142
  try {
1067
- for (const [at, asked] of calls.entries()) {
1143
+ for (const [at, sent] of calls.entries()) {
1144
+ const { mintedHint: hint, ...plain } = sent;
1145
+ const asked = renamed.size ? { ...plain, args: Object.fromEntries(Object.entries(plain.args).map(([key, value]) => [key, rename(value)])) } : plain;
1068
1146
  const call = naming[at] ? resolveMade(asked, madeIds, at) : asked;
1069
1147
  if (naming[at])
1070
1148
  (permitted(call), declared(call));
1071
1149
  const definition = this.mutation(call.name);
1150
+ if (definition === this.publisher) {
1151
+ const op = PUBLISHING.op(this, { id: this.nextId(), at: this.now() }, call, batch, author, options);
1152
+ ops.push(op);
1153
+ intents.push(op.intent);
1154
+ continue;
1155
+ }
1072
1156
  const missing = this.missingFor(call, author);
1073
1157
  if (missing)
1074
1158
  throw missing;
1075
1159
  let compiled;
1076
1160
  try {
1077
- compiled = compileMutation(this.graph, definition, call.args, this.sighted(author));
1161
+ compiled = compileMutation(this.graph, definition, call.args, { ...this.sighted(author), hint });
1078
1162
  }
1079
1163
  catch (error) {
1080
1164
  // A press the act's own input refused: the watch tells a form filled wrongly from an act offered whole.
@@ -1082,6 +1166,8 @@ export class Store {
1082
1166
  tellTheWatchOfAnInputRefusal(error, author.id);
1083
1167
  throw error;
1084
1168
  }
1169
+ if (hint !== undefined && compiled.firstMinted !== undefined && compiled.firstMinted !== hint)
1170
+ renamed.set(hint, compiled.firstMinted);
1085
1171
  if (automated) {
1086
1172
  const refused = tidyingRefusal(this.schema, this.graph, compiled.primitives, this.authorName(author), { declaration: !this.suggesting });
1087
1173
  if (refused)
@@ -1100,6 +1186,7 @@ export class Store {
1100
1186
  */
1101
1187
  if (compiled.primitives.length === 0)
1102
1188
  continue;
1189
+ minted[at] = compiled.firstMinted;
1103
1190
  madeIds[at] = compiled.primitives.find((primitive) => primitive.op === "add-node")?.node.id;
1104
1191
  const op = {
1105
1192
  id: this.nextId(),
@@ -1121,7 +1208,7 @@ export class Store {
1121
1208
  * instruction is lost the moment the op is written to JSON and the
1122
1209
  * inverse quietly does nothing. See `normalize` in primitives.ts.
1123
1210
  */
1124
- primitives: compiled.primitives.map(normalize),
1211
+ primitives: this.asSplices(compiled.primitives, options.base).map(normalize),
1125
1212
  reads: compiled.reads,
1126
1213
  writes: compiled.writes,
1127
1214
  // Which records its sentence read, so a reader who may not see one is not handed the sentence (FR-55).
@@ -1155,6 +1242,7 @@ export class Store {
1155
1242
  return {
1156
1243
  batch,
1157
1244
  ops,
1245
+ ...(minted.length > 0 ? { minted } : {}),
1158
1246
  diff,
1159
1247
  primitives: allPrimitives,
1160
1248
  reads: [...reads],
@@ -1317,7 +1405,7 @@ export class Store {
1317
1405
  const rollback = this.graph.snapshot();
1318
1406
  const intent = options.intent ?? "Apply a change";
1319
1407
  const lineage = lineageOf(options.lineage);
1320
- const recorded = primitives.map(normalize);
1408
+ const recorded = this.asSplices(primitives, options.base).map(normalize);
1321
1409
  const ops = [];
1322
1410
  if (recorded.length > 0) {
1323
1411
  const op = {
@@ -1379,7 +1467,7 @@ export class Store {
1379
1467
  const check = this.canUndo(batchIds);
1380
1468
  if (!check.ok)
1381
1469
  return { ok: false, check };
1382
- const primitives = undoPrimitives(check.ops);
1470
+ const primitives = undoPrimitives(check.ops, this.log.all());
1383
1471
  const writes = new Set();
1384
1472
  for (const op of check.ops)
1385
1473
  for (const id of op.writes)
@@ -1440,7 +1528,7 @@ export class Store {
1440
1528
  * what you may have done — the mutation each operation ran, judged
1441
1529
  * again now.
1442
1530
  */
1443
- if (this.policy) {
1531
+ if (this.policy || this.publisher) {
1444
1532
  for (const target of check.ops) {
1445
1533
  if (!target.mutation)
1446
1534
  continue;
@@ -1463,7 +1551,7 @@ export class Store {
1463
1551
  const before = this.violations();
1464
1552
  const ops = [];
1465
1553
  const kept = this.log.length;
1466
- for (const target of [...check.ops].sort((a, b) => b.seq - a.seq)) {
1554
+ for (const { op: target, primitives } of undoneInverses(this.log.all(), check.ops)) {
1467
1555
  const op = {
1468
1556
  id: this.nextId(),
1469
1557
  seq: this.log.length,
@@ -1472,7 +1560,7 @@ export class Store {
1472
1560
  intent: `Undo: ${target.intent}`,
1473
1561
  ...given("batchIntent", options.intent),
1474
1562
  mutation: null,
1475
- primitives: inverseOf(target),
1563
+ primitives,
1476
1564
  reads: target.reads,
1477
1565
  writes: target.writes,
1478
1566
  at: this.now(),
@@ -1814,6 +1902,12 @@ export class Store {
1814
1902
  const byDays = kept ? kept.seq : this.log.length;
1815
1903
  seq = Math.min(byOps, byDays);
1816
1904
  }
1905
+ // 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.
1906
+ // And readers' own (Roadmap f-publishing-lifecycle): what was published — or, before the first publish, where the log begins.
1907
+ const readers = onPublish(this.publishing) ? [this.publishedSeq ?? 0] : [];
1908
+ const pinned = [...(options.pins ?? []), ...readers].filter((pin) => Number.isInteger(pin));
1909
+ if (pinned.length > 0)
1910
+ seq = Math.min(seq, ...pinned);
1817
1911
  seq = Math.min(Math.max(seq, this.log.horizon), this.log.length);
1818
1912
  // Never inside a gesture: back to where the batch it would split began.
1819
1913
  while (seq > this.log.horizon && seq < this.log.length && ops[seq - this.log.horizon].batch === ops[seq - 1 - this.log.horizon].batch)