@graview/core 0.1.0 → 0.1.3

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 (296) hide show
  1. package/README.md +7 -0
  2. package/dist/app.d.ts +17 -0
  3. package/dist/app.d.ts.map +1 -1
  4. package/dist/app.js.map +1 -1
  5. package/dist/arrange.d.ts +3 -2
  6. package/dist/arrange.d.ts.map +1 -1
  7. package/dist/arrange.js +19 -2
  8. package/dist/arrange.js.map +1 -1
  9. package/dist/beginning.d.ts +5 -2
  10. package/dist/beginning.d.ts.map +1 -1
  11. package/dist/beginning.js +28 -0
  12. package/dist/beginning.js.map +1 -1
  13. package/dist/capabilities.d.ts +32 -0
  14. package/dist/capabilities.d.ts.map +1 -0
  15. package/dist/capabilities.js +31 -0
  16. package/dist/capabilities.js.map +1 -0
  17. package/dist/cli/check/index.d.ts +1 -0
  18. package/dist/cli/check/index.d.ts.map +1 -1
  19. package/dist/cli/check/index.js +1 -0
  20. package/dist/cli/check/index.js.map +1 -1
  21. package/dist/cli/check/lenses.d.ts.map +1 -1
  22. package/dist/cli/check/lenses.js +25 -0
  23. package/dist/cli/check/lenses.js.map +1 -1
  24. package/dist/cli/check/policy.d.ts.map +1 -1
  25. package/dist/cli/check/policy.js +35 -0
  26. package/dist/cli/check/policy.js.map +1 -1
  27. package/dist/cli/check/relations.d.ts.map +1 -1
  28. package/dist/cli/check/relations.js +31 -0
  29. package/dist/cli/check/relations.js.map +1 -1
  30. package/dist/cli/check/views.d.ts +14 -0
  31. package/dist/cli/check/views.d.ts.map +1 -0
  32. package/dist/cli/check/views.js +27 -0
  33. package/dist/cli/check/views.js.map +1 -0
  34. package/dist/cli/check.d.ts.map +1 -1
  35. package/dist/cli/check.js +2 -1
  36. package/dist/cli/check.js.map +1 -1
  37. package/dist/cli/create.d.ts +1 -1
  38. package/dist/cli/create.d.ts.map +1 -1
  39. package/dist/cli/create.js +63 -1
  40. package/dist/cli/create.js.map +1 -1
  41. package/dist/cli/index.d.ts +18 -1
  42. package/dist/cli/index.d.ts.map +1 -1
  43. package/dist/cli/index.js +66 -1
  44. package/dist/cli/index.js.map +1 -1
  45. package/dist/conformance/fixtures.d.ts +41 -0
  46. package/dist/conformance/fixtures.d.ts.map +1 -0
  47. package/dist/conformance/fixtures.js +950 -0
  48. package/dist/conformance/fixtures.js.map +1 -0
  49. package/dist/conformance/index.d.ts +54 -0
  50. package/dist/conformance/index.d.ts.map +1 -0
  51. package/dist/conformance/index.js +81 -0
  52. package/dist/conformance/index.js.map +1 -0
  53. package/dist/document/canonical.d.ts +4 -0
  54. package/dist/document/canonical.d.ts.map +1 -0
  55. package/dist/document/canonical.js +29 -0
  56. package/dist/document/canonical.js.map +1 -0
  57. package/dist/document/compile.d.ts +43 -0
  58. package/dist/document/compile.d.ts.map +1 -0
  59. package/dist/document/compile.js +807 -0
  60. package/dist/document/compile.js.map +1 -0
  61. package/dist/document/diff.d.ts +32 -0
  62. package/dist/document/diff.d.ts.map +1 -0
  63. package/dist/document/diff.js +148 -0
  64. package/dist/document/diff.js.map +1 -0
  65. package/dist/document/edit.d.ts +63 -0
  66. package/dist/document/edit.d.ts.map +1 -0
  67. package/dist/document/edit.js +1432 -0
  68. package/dist/document/edit.js.map +1 -0
  69. package/dist/document/expr/analyze.d.ts +14 -0
  70. package/dist/document/expr/analyze.d.ts.map +1 -0
  71. package/dist/document/expr/analyze.js +52 -0
  72. package/dist/document/expr/analyze.js.map +1 -0
  73. package/dist/document/expr/evaluate.d.ts +36 -0
  74. package/dist/document/expr/evaluate.d.ts.map +1 -0
  75. package/dist/document/expr/evaluate.js +344 -0
  76. package/dist/document/expr/evaluate.js.map +1 -0
  77. package/dist/document/expr/index.d.ts +8 -0
  78. package/dist/document/expr/index.d.ts.map +1 -0
  79. package/dist/document/expr/index.js +5 -0
  80. package/dist/document/expr/index.js.map +1 -0
  81. package/dist/document/expr/parse.d.ts +50 -0
  82. package/dist/document/expr/parse.d.ts.map +1 -0
  83. package/dist/document/expr/parse.js +240 -0
  84. package/dist/document/expr/parse.js.map +1 -0
  85. package/dist/document/expr/print.d.ts +4 -0
  86. package/dist/document/expr/print.d.ts.map +1 -0
  87. package/dist/document/expr/print.js +81 -0
  88. package/dist/document/expr/print.js.map +1 -0
  89. package/dist/document/findings.d.ts +15 -0
  90. package/dist/document/findings.d.ts.map +1 -0
  91. package/dist/document/findings.js +30 -0
  92. package/dist/document/findings.js.map +1 -0
  93. package/dist/document/graview-template.d.ts +148 -0
  94. package/dist/document/graview-template.d.ts.map +1 -0
  95. package/dist/document/graview-template.js +450 -0
  96. package/dist/document/graview-template.js.map +1 -0
  97. package/dist/document/index.d.ts +27 -0
  98. package/dist/document/index.d.ts.map +1 -0
  99. package/dist/document/index.js +16 -0
  100. package/dist/document/index.js.map +1 -0
  101. package/dist/document/migrate.d.ts +41 -0
  102. package/dist/document/migrate.d.ts.map +1 -0
  103. package/dist/document/migrate.js +252 -0
  104. package/dist/document/migrate.js.map +1 -0
  105. package/dist/document/patch.d.ts +16 -0
  106. package/dist/document/patch.d.ts.map +1 -0
  107. package/dist/document/patch.js +79 -0
  108. package/dist/document/patch.js.map +1 -0
  109. package/dist/document/rules.d.ts +40 -0
  110. package/dist/document/rules.d.ts.map +1 -0
  111. package/dist/document/rules.js +88 -0
  112. package/dist/document/rules.js.map +1 -0
  113. package/dist/document/schema.d.ts +461 -0
  114. package/dist/document/schema.d.ts.map +1 -0
  115. package/dist/document/schema.js +199 -0
  116. package/dist/document/schema.js.map +1 -0
  117. package/dist/document/template.d.ts +28 -0
  118. package/dist/document/template.d.ts.map +1 -0
  119. package/dist/document/template.js +117 -0
  120. package/dist/document/template.js.map +1 -0
  121. package/dist/document/to-document.d.ts +34 -0
  122. package/dist/document/to-document.d.ts.map +1 -0
  123. package/dist/document/to-document.js +186 -0
  124. package/dist/document/to-document.js.map +1 -0
  125. package/dist/document/upgrade.d.ts +9 -0
  126. package/dist/document/upgrade.d.ts.map +1 -0
  127. package/dist/document/upgrade.js +19 -0
  128. package/dist/document/upgrade.js.map +1 -0
  129. package/dist/document/views.d.ts +65 -0
  130. package/dist/document/views.d.ts.map +1 -0
  131. package/dist/document/views.js +264 -0
  132. package/dist/document/views.js.map +1 -0
  133. package/dist/formats.d.ts +50 -0
  134. package/dist/formats.d.ts.map +1 -0
  135. package/dist/formats.js +75 -0
  136. package/dist/formats.js.map +1 -0
  137. package/dist/graph/graph.d.ts +61 -3
  138. package/dist/graph/graph.d.ts.map +1 -1
  139. package/dist/graph/graph.js +187 -20
  140. package/dist/graph/graph.js.map +1 -1
  141. package/dist/index.d.ts +43 -18
  142. package/dist/index.d.ts.map +1 -1
  143. package/dist/index.js +31 -11
  144. package/dist/index.js.map +1 -1
  145. package/dist/integrity.d.ts +46 -0
  146. package/dist/integrity.d.ts.map +1 -0
  147. package/dist/integrity.js +179 -0
  148. package/dist/integrity.js.map +1 -0
  149. package/dist/invariants/engine.d.ts +8 -0
  150. package/dist/invariants/engine.d.ts.map +1 -1
  151. package/dist/invariants/engine.js +40 -2
  152. package/dist/invariants/engine.js.map +1 -1
  153. package/dist/invariants/types.d.ts +19 -0
  154. package/dist/invariants/types.d.ts.map +1 -1
  155. package/dist/labels.d.ts +65 -0
  156. package/dist/labels.d.ts.map +1 -0
  157. package/dist/labels.js +123 -0
  158. package/dist/labels.js.map +1 -0
  159. package/dist/mutations/define-mutation.d.ts.map +1 -1
  160. package/dist/mutations/define-mutation.js +5 -6
  161. package/dist/mutations/define-mutation.js.map +1 -1
  162. package/dist/mutations/derive-edits.d.ts.map +1 -1
  163. package/dist/mutations/derive-edits.js +20 -1
  164. package/dist/mutations/derive-edits.js.map +1 -1
  165. package/dist/mutations/form.d.ts +9 -0
  166. package/dist/mutations/form.d.ts.map +1 -1
  167. package/dist/mutations/form.js +20 -0
  168. package/dist/mutations/form.js.map +1 -1
  169. package/dist/mutations/node-ref.d.ts +6 -0
  170. package/dist/mutations/node-ref.d.ts.map +1 -1
  171. package/dist/mutations/node-ref.js +22 -8
  172. package/dist/mutations/node-ref.js.map +1 -1
  173. package/dist/mutations/types.d.ts +8 -0
  174. package/dist/mutations/types.d.ts.map +1 -1
  175. package/dist/mutations/words.d.ts +49 -0
  176. package/dist/mutations/words.d.ts.map +1 -0
  177. package/dist/mutations/words.js +61 -0
  178. package/dist/mutations/words.js.map +1 -0
  179. package/dist/ops/log.d.ts +156 -4
  180. package/dist/ops/log.d.ts.map +1 -1
  181. package/dist/ops/log.js +234 -51
  182. package/dist/ops/log.js.map +1 -1
  183. package/dist/ops/revisions.d.ts +66 -0
  184. package/dist/ops/revisions.d.ts.map +1 -0
  185. package/dist/ops/revisions.js +97 -0
  186. package/dist/ops/revisions.js.map +1 -0
  187. package/dist/ops/types.d.ts +49 -1
  188. package/dist/ops/types.d.ts.map +1 -1
  189. package/dist/ops/undo.d.ts +29 -2
  190. package/dist/ops/undo.d.ts.map +1 -1
  191. package/dist/ops/undo.js +110 -1
  192. package/dist/ops/undo.js.map +1 -1
  193. package/dist/ops/withheld.d.ts +37 -0
  194. package/dist/ops/withheld.d.ts.map +1 -0
  195. package/dist/ops/withheld.js +84 -0
  196. package/dist/ops/withheld.js.map +1 -0
  197. package/dist/permissions/policy.d.ts +15 -0
  198. package/dist/permissions/policy.d.ts.map +1 -1
  199. package/dist/permissions/policy.js +28 -1
  200. package/dist/permissions/policy.js.map +1 -1
  201. package/dist/permissions/sight.d.ts +66 -0
  202. package/dist/permissions/sight.d.ts.map +1 -0
  203. package/dist/permissions/sight.js +131 -0
  204. package/dist/permissions/sight.js.map +1 -0
  205. package/dist/permissions/types.d.ts +26 -0
  206. package/dist/permissions/types.d.ts.map +1 -1
  207. package/dist/permissions/types.js.map +1 -1
  208. package/dist/persistence/memory.d.ts.map +1 -1
  209. package/dist/persistence/memory.js +25 -0
  210. package/dist/persistence/memory.js.map +1 -1
  211. package/dist/persistence/sql.d.ts +43 -0
  212. package/dist/persistence/sql.d.ts.map +1 -0
  213. package/dist/persistence/sql.js +154 -0
  214. package/dist/persistence/sql.js.map +1 -0
  215. package/dist/persistence/sqlite.d.ts +17 -3
  216. package/dist/persistence/sqlite.d.ts.map +1 -1
  217. package/dist/persistence/sqlite.js +32 -96
  218. package/dist/persistence/sqlite.js.map +1 -1
  219. package/dist/persistence/types.d.ts +19 -0
  220. package/dist/persistence/types.d.ts.map +1 -1
  221. package/dist/presence.d.ts +56 -0
  222. package/dist/presence.d.ts.map +1 -1
  223. package/dist/presence.js +62 -2
  224. package/dist/presence.js.map +1 -1
  225. package/dist/refusal.d.ts +34 -0
  226. package/dist/refusal.d.ts.map +1 -0
  227. package/dist/refusal.js +40 -0
  228. package/dist/refusal.js.map +1 -0
  229. package/dist/scaffold/from-template.d.ts +16 -0
  230. package/dist/scaffold/from-template.d.ts.map +1 -0
  231. package/dist/scaffold/from-template.js +157 -0
  232. package/dist/scaffold/from-template.js.map +1 -0
  233. package/dist/scaffold/index.d.ts +10 -1
  234. package/dist/scaffold/index.d.ts.map +1 -1
  235. package/dist/scaffold/index.js +35 -7
  236. package/dist/scaffold/index.js.map +1 -1
  237. package/dist/scaffold/names.d.ts +4 -0
  238. package/dist/scaffold/names.d.ts.map +1 -1
  239. package/dist/scaffold/names.js.map +1 -1
  240. package/dist/scaffold/project.d.ts +1 -1
  241. package/dist/scaffold/project.d.ts.map +1 -1
  242. package/dist/scaffold/project.js +44 -13
  243. package/dist/scaffold/project.js.map +1 -1
  244. package/dist/scaffold/ui.d.ts.map +1 -1
  245. package/dist/scaffold/ui.js +19 -10
  246. package/dist/scaffold/ui.js.map +1 -1
  247. package/dist/schema/define-node.d.ts +20 -0
  248. package/dist/schema/define-node.d.ts.map +1 -1
  249. package/dist/schema/define-node.js +27 -2
  250. package/dist/schema/define-node.js.map +1 -1
  251. package/dist/schema/path.d.ts +19 -0
  252. package/dist/schema/path.d.ts.map +1 -0
  253. package/dist/schema/path.js +34 -0
  254. package/dist/schema/path.js.map +1 -0
  255. package/dist/schema/types.d.ts +17 -0
  256. package/dist/schema/types.d.ts.map +1 -1
  257. package/dist/seen.d.ts +64 -0
  258. package/dist/seen.d.ts.map +1 -0
  259. package/dist/seen.js +200 -0
  260. package/dist/seen.js.map +1 -0
  261. package/dist/store.d.ts +378 -8
  262. package/dist/store.d.ts.map +1 -1
  263. package/dist/store.js +801 -41
  264. package/dist/store.js.map +1 -1
  265. package/dist/testing.d.ts +6 -0
  266. package/dist/testing.d.ts.map +1 -1
  267. package/dist/testing.js +3 -1
  268. package/dist/testing.js.map +1 -1
  269. package/dist/theme/palettes.d.ts.map +1 -1
  270. package/dist/theme/palettes.js +4 -0
  271. package/dist/theme/palettes.js.map +1 -1
  272. package/dist/theme/types.d.ts +9 -0
  273. package/dist/theme/types.d.ts.map +1 -1
  274. package/dist/theme/types.js +4 -0
  275. package/dist/theme/types.js.map +1 -1
  276. package/dist/validate-graph.d.ts +84 -0
  277. package/dist/validate-graph.d.ts.map +1 -0
  278. package/dist/validate-graph.js +295 -0
  279. package/dist/validate-graph.js.map +1 -0
  280. package/dist/version.d.ts +3 -0
  281. package/dist/version.d.ts.map +1 -0
  282. package/dist/version.js +3 -0
  283. package/dist/version.js.map +1 -0
  284. package/dist/views/types.d.ts +8 -0
  285. package/dist/views/types.d.ts.map +1 -1
  286. package/dist/views/types.js +5 -0
  287. package/dist/views/types.js.map +1 -1
  288. package/dist/watched.d.ts +39 -0
  289. package/dist/watched.d.ts.map +1 -1
  290. package/dist/watched.js +94 -1
  291. package/dist/watched.js.map +1 -1
  292. package/dist/who.d.ts +24 -1
  293. package/dist/who.d.ts.map +1 -1
  294. package/dist/who.js +49 -2
  295. package/dist/who.js.map +1 -1
  296. package/package.json +9 -1
@@ -0,0 +1,66 @@
1
+ import type { Primitive } from "../graph/primitives.js";
2
+ import type { Operation } from "./types.js";
3
+ /**
4
+ * WHICH OP LAST WROTE EACH FIELD (FR-05) — read off the log, never stored.
5
+ *
6
+ * A stale write is one made against a value somebody has changed since:
7
+ * two people open one record, both change its status, and whoever sends
8
+ * second used to win without knowing the first had spoken. To say so, a
9
+ * call carries the revision of each field it changes as its sender last
10
+ * saw it, and the store compares that with the field's revision now.
11
+ *
12
+ * A field's revision is the `seq` of the op that last wrote it: an op that
13
+ * added its record, or one that patched it. It is DERIVED from the log
14
+ * rather than kept beside it, so no stored format changes and every store,
15
+ * every client and every host that folds the same log agrees on it without
16
+ * being told. A field no op in the log has written (a seeded one, or one
17
+ * written before the log's first op) is at `NEVER_WRITTEN`.
18
+ */
19
+ export declare const NEVER_WRITTEN = -1;
20
+ /** What a call says it saw: one field of one record, at the revision its sender last read. */
21
+ export interface FieldRevision {
22
+ readonly node: string;
23
+ readonly field: string;
24
+ readonly rev: number;
25
+ }
26
+ /**
27
+ * A FIELD THAT MOVED WHILE SOMEBODY WAS EDITING IT: the stale write, as
28
+ * the store refuses it. `theirs` is the value now, `yours` the one the call
29
+ * would have written, `by` who wrote theirs and `rev` the op that did.
30
+ * Named as Graview Cloud's room names it, so a host's interim reads the same.
31
+ */
32
+ export interface FieldConflict {
33
+ readonly node: string;
34
+ readonly field: string;
35
+ readonly theirs: unknown;
36
+ readonly yours: unknown;
37
+ /** Who wrote theirs, by name where the log says one: "Sam", "Someone". */
38
+ readonly by: string;
39
+ /** The revision the field is at now. */
40
+ readonly rev: number;
41
+ /** The revision the call said it saw. */
42
+ readonly saw: number;
43
+ }
44
+ /** The fields one primitive writes, by record. */
45
+ export declare function fieldsWritten(primitive: Primitive): readonly (readonly [string, string])[];
46
+ /**
47
+ * The revision of every field a log has written, kept current as ops land.
48
+ * A revision only moves forward: an op noted twice, or out of order, leaves
49
+ * a later one standing.
50
+ */
51
+ export declare class FieldRevisions {
52
+ private readonly revs;
53
+ /** Revisions read from a whole log (or the part of one a seat may see). */
54
+ static of(ops: readonly Operation[]): FieldRevisions;
55
+ /** Takes in ops as they land, by the seq each carries. */
56
+ note(ops: readonly Operation[]): void;
57
+ /** The seq of the op that last wrote this field, or `NEVER_WRITTEN`. */
58
+ of(node: string, field: string): number;
59
+ /** The base a call made of these ops would carry: each field they patch on a record they did not add, at its revision here. */
60
+ baseFor(ops: readonly Operation[], skip?: ReadonlySet<string>): FieldRevision[];
61
+ /** The base entries that no longer hold: each field written since its sender read it. */
62
+ stale(base: readonly FieldRevision[]): FieldRevision[];
63
+ }
64
+ /** Every record and every `record\0field` these ops write: what a later call need not claim to have seen. */
65
+ export declare function writtenBy(ops: readonly Operation[]): Set<string>;
66
+ //# sourceMappingURL=revisions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"revisions.d.ts","sourceRoot":"","sources":["../../src/ops/revisions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACxD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAE5C;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,aAAa,KAAK,CAAC;AAEhC,8FAA8F;AAC9F,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;CACtB;AAED;;;;;GAKG;AACH,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IACzB,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,0EAA0E;IAC1E,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,wCAAwC;IACxC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,yCAAyC;IACzC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;CACtB;AAID,kDAAkD;AAClD,wBAAgB,aAAa,CAAC,SAAS,EAAE,SAAS,GAAG,SAAS,CAAC,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,EAAE,CAQ1F;AAED;;;;GAIG;AACH,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAC,IAAI,CAA6B;IAElD,2EAA2E;IAC3E,MAAM,CAAC,EAAE,CAAC,GAAG,EAAE,SAAS,SAAS,EAAE,GAAG,cAAc;IAMpD,0DAA0D;IAC1D,IAAI,CAAC,GAAG,EAAE,SAAS,SAAS,EAAE,GAAG,IAAI;IAWrC,wEAAwE;IACxE,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM;IAIvC,+HAA+H;IAC/H,OAAO,CAAC,GAAG,EAAE,SAAS,SAAS,EAAE,EAAE,IAAI,GAAE,WAAW,CAAC,MAAM,CAAa,GAAG,aAAa,EAAE;IAiB1F,yFAAyF;IACzF,KAAK,CAAC,IAAI,EAAE,SAAS,aAAa,EAAE,GAAG,aAAa,EAAE;CAGvD;AAED,6GAA6G;AAC7G,wBAAgB,SAAS,CAAC,GAAG,EAAE,SAAS,SAAS,EAAE,GAAG,GAAG,CAAC,MAAM,CAAC,CAShE"}
@@ -0,0 +1,97 @@
1
+ /**
2
+ * WHICH OP LAST WROTE EACH FIELD (FR-05) — read off the log, never stored.
3
+ *
4
+ * A stale write is one made against a value somebody has changed since:
5
+ * two people open one record, both change its status, and whoever sends
6
+ * second used to win without knowing the first had spoken. To say so, a
7
+ * call carries the revision of each field it changes as its sender last
8
+ * saw it, and the store compares that with the field's revision now.
9
+ *
10
+ * A field's revision is the `seq` of the op that last wrote it: an op that
11
+ * added its record, or one that patched it. It is DERIVED from the log
12
+ * rather than kept beside it, so no stored format changes and every store,
13
+ * every client and every host that folds the same log agrees on it without
14
+ * being told. A field no op in the log has written (a seeded one, or one
15
+ * written before the log's first op) is at `NEVER_WRITTEN`.
16
+ */
17
+ export const NEVER_WRITTEN = -1;
18
+ const key = (node, field) => `${node}\u0000${field}`;
19
+ /** The fields one primitive writes, by record. */
20
+ export function fieldsWritten(primitive) {
21
+ if (primitive.op === "add-node") {
22
+ return Object.keys(primitive.node)
23
+ .filter((field) => field !== "id" && field !== "kind")
24
+ .map((field) => [primitive.node.id, field]);
25
+ }
26
+ if (primitive.op === "patch-node")
27
+ return Object.keys(primitive.after).map((field) => [primitive.id, field]);
28
+ return [];
29
+ }
30
+ /**
31
+ * The revision of every field a log has written, kept current as ops land.
32
+ * A revision only moves forward: an op noted twice, or out of order, leaves
33
+ * a later one standing.
34
+ */
35
+ export class FieldRevisions {
36
+ revs = new Map();
37
+ /** Revisions read from a whole log (or the part of one a seat may see). */
38
+ static of(ops) {
39
+ const revisions = new FieldRevisions();
40
+ revisions.note(ops);
41
+ return revisions;
42
+ }
43
+ /** Takes in ops as they land, by the seq each carries. */
44
+ note(ops) {
45
+ for (const op of ops) {
46
+ for (const primitive of op.primitives) {
47
+ for (const [node, field] of fieldsWritten(primitive)) {
48
+ const at = key(node, field);
49
+ if ((this.revs.get(at) ?? NEVER_WRITTEN) < op.seq)
50
+ this.revs.set(at, op.seq);
51
+ }
52
+ }
53
+ }
54
+ }
55
+ /** The seq of the op that last wrote this field, or `NEVER_WRITTEN`. */
56
+ of(node, field) {
57
+ return this.revs.get(key(node, field)) ?? NEVER_WRITTEN;
58
+ }
59
+ /** The base a call made of these ops would carry: each field they patch on a record they did not add, at its revision here. */
60
+ baseFor(ops, skip = new Set()) {
61
+ const created = new Set();
62
+ const base = new Map();
63
+ for (const op of ops) {
64
+ for (const primitive of op.primitives) {
65
+ if (primitive.op === "add-node")
66
+ created.add(primitive.node.id);
67
+ if (primitive.op !== "patch-node" || created.has(primitive.id) || skip.has(primitive.id))
68
+ continue;
69
+ for (const field of Object.keys(primitive.after)) {
70
+ const at = key(primitive.id, field);
71
+ if (skip.has(at) || base.has(at))
72
+ continue;
73
+ base.set(at, { node: primitive.id, field, rev: this.of(primitive.id, field) });
74
+ }
75
+ }
76
+ }
77
+ return [...base.values()];
78
+ }
79
+ /** The base entries that no longer hold: each field written since its sender read it. */
80
+ stale(base) {
81
+ return base.filter((entry) => this.of(entry.node, entry.field) !== entry.rev);
82
+ }
83
+ }
84
+ /** Every record and every `record\0field` these ops write: what a later call need not claim to have seen. */
85
+ export function writtenBy(ops) {
86
+ const out = new Set();
87
+ for (const op of ops) {
88
+ for (const primitive of op.primitives) {
89
+ if (primitive.op === "add-node")
90
+ out.add(primitive.node.id);
91
+ for (const [node, field] of fieldsWritten(primitive))
92
+ out.add(key(node, field));
93
+ }
94
+ }
95
+ return out;
96
+ }
97
+ //# sourceMappingURL=revisions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"revisions.js","sourceRoot":"","sources":["../../src/ops/revisions.ts"],"names":[],"mappings":"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"}
@@ -14,7 +14,25 @@ export interface Author {
14
14
  readonly kind: "human" | "agent" | "rule" | "system";
15
15
  readonly id?: string;
16
16
  readonly session?: string;
17
+ /**
18
+ * The author's own name, for a reader who has no record or seat to look
19
+ * it up in — another person in a hosted app, an agent from a chat. Said
20
+ * before any id is (FR-17).
21
+ */
22
+ readonly name?: string;
23
+ /**
24
+ * WHO THIS IS FOR. An agent acting for a person records both — "Claude,
25
+ * for Nick" — and is bounded by that person's roles as well as its own
26
+ * (FR-06).
27
+ */
28
+ readonly onBehalfOf?: Author;
17
29
  }
30
+ /**
31
+ * THE CHANNEL an op came through: `web` (a person at the interface),
32
+ * `mcp:<client>` (an agent's tool call), `view:<name>` (a view acting for
33
+ * its viewer), `api`, `cli`. A fact in the log, beside who and for whom.
34
+ */
35
+ export type Via = "web" | "api" | "cli" | `mcp:${string}` | `view:${string}` | (string & {});
18
36
  /**
19
37
  * One entry in the append-only log. The graph is a fold over these.
20
38
  *
@@ -29,8 +47,20 @@ export interface Operation {
29
47
  /** One user gesture, or one agent turn. */
30
48
  readonly batch: string;
31
49
  readonly author: Author;
32
- /** Human-readable statement of what was meant, not what changed. */
50
+ /**
51
+ * Human-readable statement of what was meant, not what changed: the act's
52
+ * own `describe()` sentence ("Add “Book the van”"), or "Undo: …".
53
+ */
33
54
  readonly intent: string;
55
+ /**
56
+ * What the whole gesture was for, when its caller said ("Plan the move"),
57
+ * on every op of the batch. Beside `intent`, never instead of it (FR-18):
58
+ * an op keeps its own sentence, so a blocked undo, the record's history
59
+ * and an audit still say what each op did, and the batch reads as what
60
+ * was meant. Absent when no intent was given, and the batch reads as its
61
+ * ops, as it always did.
62
+ */
63
+ readonly batchIntent?: string;
34
64
  readonly mutation: MutationCall | null;
35
65
  readonly primitives: readonly Primitive[];
36
66
  readonly inverse: readonly Primitive[];
@@ -39,6 +69,24 @@ export interface Operation {
39
69
  readonly at: string;
40
70
  /** Set when this op exists to undo another one. */
41
71
  readonly undoes?: string;
72
+ /** What the change came through, when the caller said (FR-06). */
73
+ readonly via?: Via;
74
+ /**
75
+ * SOMETHING YOU CANNOT SEE HAPPENED HERE (FR-16). Set on an op a store
76
+ * served to a seat that may not see what it touched: it keeps its id,
77
+ * seq, batch and time, so the log has no hole, and nothing else of its
78
+ * own — its author, intent, call and inverse are withheld, and its
79
+ * primitives, reads and writes keep only what the seat may see. Absent
80
+ * on every op a store makes; an op without it reads as before.
81
+ */
82
+ readonly withheld?: true;
83
+ /**
84
+ * THE MODULES THIS OP LEAVES ON (FR-12). Set only on an op authored
85
+ * `system · modules`, which turns modules off or on and touches no
86
+ * record: the store's enabled set is the one the last such op says. An
87
+ * op without it reads as before, and folds the same.
88
+ */
89
+ readonly enabledModules?: readonly string[];
42
90
  }
43
91
  export interface Batch {
44
92
  readonly id: string;
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/ops/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACxD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAE1D,MAAM,WAAW,MAAM;IACrB;;;;;;;;;OASG;IACH,QAAQ,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,GAAG,MAAM,GAAG,QAAQ,CAAC;IACrD,QAAQ,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED;;;;;;GAMG;AACH,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,qCAAqC;IACrC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,2CAA2C;IAC3C,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,oEAAoE;IACpE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,QAAQ,EAAE,YAAY,GAAG,IAAI,CAAC;IACvC,QAAQ,CAAC,UAAU,EAAE,SAAS,SAAS,EAAE,CAAC;IAC1C,QAAQ,CAAC,OAAO,EAAE,SAAS,SAAS,EAAE,CAAC;IACvC,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;IAClC,QAAQ,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC;IACnC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,mDAAmD;IACnD,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,KAAK;IACpB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,GAAG,EAAE,SAAS,SAAS,EAAE,CAAC;IACnC,uEAAuE;IACvE,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;CAC1B"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/ops/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACxD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAE1D,MAAM,WAAW,MAAM;IACrB;;;;;;;;;OASG;IACH,QAAQ,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,GAAG,MAAM,GAAG,QAAQ,CAAC;IACrD,QAAQ,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B;;;;OAIG;IACH,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB;;;;OAIG;IACH,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED;;;;GAIG;AACH,MAAM,MAAM,GAAG,GAAG,KAAK,GAAG,KAAK,GAAG,KAAK,GAAG,OAAO,MAAM,EAAE,GAAG,QAAQ,MAAM,EAAE,GAAG,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC;AAE7F;;;;;;GAMG;AACH,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,qCAAqC;IACrC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,2CAA2C;IAC3C,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB;;;OAGG;IACH,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB;;;;;;;OAOG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,QAAQ,EAAE,YAAY,GAAG,IAAI,CAAC;IACvC,QAAQ,CAAC,UAAU,EAAE,SAAS,SAAS,EAAE,CAAC;IAC1C,QAAQ,CAAC,OAAO,EAAE,SAAS,SAAS,EAAE,CAAC;IACvC,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;IAClC,QAAQ,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC;IACnC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,mDAAmD;IACnD,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,kEAAkE;IAClE,QAAQ,CAAC,GAAG,CAAC,EAAE,GAAG,CAAC;IACnB;;;;;;;OAOG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,IAAI,CAAC;IACzB;;;;;OAKG;IACH,QAAQ,CAAC,cAAc,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CAC7C;AAED,MAAM,WAAW,KAAK;IACpB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,GAAG,EAAE,SAAS,SAAS,EAAE,CAAC;IACnC,uEAAuE;IACvE,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;CAC1B"}
@@ -1,4 +1,5 @@
1
- import type { OperationLog } from "./log.js";
1
+ import { GraphError } from "../graph/graph.js";
2
+ import type { LogReading } from "./log.js";
2
3
  import type { Operation } from "./types.js";
3
4
  export interface UndoBlock {
4
5
  /** The op that read something the undo target wrote. */
@@ -16,14 +17,40 @@ export type UndoCheck = {
16
17
  /** Batches that would have to come along for the undo to be legal. */
17
18
  readonly includeBatches: readonly string[];
18
19
  readonly message: string;
20
+ /**
21
+ * Why, as a refusal's code (FR-46): `missing` when there is nothing
22
+ * live to take back, `forbidden` when it is a change the seat cannot
23
+ * see. Absent, the undo is blocked (`invalid`).
24
+ */
25
+ readonly reason?: "missing" | "forbidden";
19
26
  };
27
+ /** An undo check that said no. */
28
+ export type UndoRefused = Extract<UndoCheck, {
29
+ readonly ok: false;
30
+ }>;
31
+ /**
32
+ * AN UNDO THAT CANNOT RUN, AND WHY (FR-18): a later op read what it wrote
33
+ * (`blockedBy` names each, with what it read), it would reach back across a
34
+ * declaration change or behind the undo horizon (FR-23), or there was
35
+ * nothing live to undo. `check` is the `canUndo` answer it was refused on,
36
+ * so a host can offer to bring the blocking batches along
37
+ * (`check.includeBatches`) without asking again.
38
+ *
39
+ * Still a `GraphError`: a caller that caught those before catches this.
40
+ */
41
+ export declare class UndoBlockedError extends GraphError {
42
+ readonly check: UndoRefused;
43
+ constructor(check: UndoRefused);
44
+ /** The ops in the way, with what each read that the undo would take back. */
45
+ get blockedBy(): readonly UndoBlock[];
46
+ }
20
47
  /**
21
48
  * Undoing an op out of order is legal exactly when no later live op read
22
49
  * something it wrote. That is a checkable condition rather than a policy —
23
50
  * so when it fails the framework can name the blocking op and offer to bring
24
51
  * it along, instead of refusing or corrupting state.
25
52
  */
26
- export declare function checkUndo(log: OperationLog, batchIds: readonly string[]): UndoCheck;
53
+ export declare function checkUndo(log: LogReading, batchIds: readonly string[]): UndoCheck;
27
54
  /**
28
55
  * The primitives an undo would apply: each op's stored inverse, newest first,
29
56
  * so dependent writes come apart in the order they went together. (An op's
@@ -1 +1 @@
1
- {"version":3,"file":"undo.d.ts","sourceRoot":"","sources":["../../src/ops/undo.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAC7C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAE5C,MAAM,WAAW,SAAS;IACxB,wDAAwD;IACxD,QAAQ,CAAC,EAAE,EAAE,SAAS,CAAC;IACvB,kDAAkD;IAClD,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;CACrC;AAED,MAAM,MAAM,SAAS,GACjB;IAAE,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC;IAAC,QAAQ,CAAC,GAAG,EAAE,SAAS,SAAS,EAAE,CAAA;CAAE,GACzD;IACE,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC;IACnB,QAAQ,CAAC,GAAG,EAAE,SAAS,SAAS,EAAE,CAAC;IACnC,QAAQ,CAAC,SAAS,EAAE,SAAS,SAAS,EAAE,CAAC;IACzC,sEAAsE;IACtE,QAAQ,CAAC,cAAc,EAAE,SAAS,MAAM,EAAE,CAAC;IAC3C,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B,CAAC;AAEN;;;;;GAKG;AACH,wBAAgB,SAAS,CACvB,GAAG,EAAE,YAAY,EACjB,QAAQ,EAAE,SAAS,MAAM,EAAE,GAC1B,SAAS,CAgDX;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,SAAS,SAAS,EAAE,qCAEvD"}
1
+ {"version":3,"file":"undo.d.ts","sourceRoot":"","sources":["../../src/ops/undo.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAC3C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAG5C,MAAM,WAAW,SAAS;IACxB,wDAAwD;IACxD,QAAQ,CAAC,EAAE,EAAE,SAAS,CAAC;IACvB,kDAAkD;IAClD,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;CACrC;AAED,MAAM,MAAM,SAAS,GACjB;IAAE,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC;IAAC,QAAQ,CAAC,GAAG,EAAE,SAAS,SAAS,EAAE,CAAA;CAAE,GACzD;IACE,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC;IACnB,QAAQ,CAAC,GAAG,EAAE,SAAS,SAAS,EAAE,CAAC;IACnC,QAAQ,CAAC,SAAS,EAAE,SAAS,SAAS,EAAE,CAAC;IACzC,sEAAsE;IACtE,QAAQ,CAAC,cAAc,EAAE,SAAS,MAAM,EAAE,CAAC;IAC3C,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB;;;;OAIG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,GAAG,WAAW,CAAC;CAC3C,CAAC;AAEN,kCAAkC;AAClC,MAAM,MAAM,WAAW,GAAG,OAAO,CAAC,SAAS,EAAE;IAAE,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAA;CAAE,CAAC,CAAC;AAErE;;;;;;;;;GASG;AACH,qBAAa,gBAAiB,SAAQ,UAAU;IAClC,QAAQ,CAAC,KAAK,EAAE,WAAW;gBAAlB,KAAK,EAAE,WAAW;IAKvC,6EAA6E;IAC7E,IAAI,SAAS,IAAI,SAAS,SAAS,EAAE,CAEpC;CACF;AAED;;;;;GAKG;AACH,wBAAgB,SAAS,CACvB,GAAG,EAAE,UAAU,EACf,QAAQ,EAAE,SAAS,MAAM,EAAE,GAC1B,SAAS,CAyIX;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,SAAS,SAAS,EAAE,qCAEvD"}
package/dist/ops/undo.js CHANGED
@@ -1,3 +1,27 @@
1
+ import { GraphError } from "../graph/graph.js";
2
+ import { isWithheld } from "./withheld.js";
3
+ /**
4
+ * AN UNDO THAT CANNOT RUN, AND WHY (FR-18): a later op read what it wrote
5
+ * (`blockedBy` names each, with what it read), it would reach back across a
6
+ * declaration change or behind the undo horizon (FR-23), or there was
7
+ * nothing live to undo. `check` is the `canUndo` answer it was refused on,
8
+ * so a host can offer to bring the blocking batches along
9
+ * (`check.includeBatches`) without asking again.
10
+ *
11
+ * Still a `GraphError`: a caller that caught those before catches this.
12
+ */
13
+ export class UndoBlockedError extends GraphError {
14
+ check;
15
+ constructor(check) {
16
+ super(check.message);
17
+ this.check = check;
18
+ this.name = "UndoBlockedError";
19
+ }
20
+ /** The ops in the way, with what each read that the undo would take back. */
21
+ get blockedBy() {
22
+ return this.check.blockedBy;
23
+ }
24
+ }
1
25
  /**
2
26
  * Undoing an op out of order is legal exactly when no later live op read
3
27
  * something it wrote. That is a checkable condition rather than a policy —
@@ -10,22 +34,92 @@ export function checkUndo(log, batchIds) {
10
34
  const ops = log
11
35
  .all()
12
36
  .filter((op) => targets.has(op.batch) && !undone.has(op.id));
37
+ /*
38
+ * NOT BEHIND THE UNDO HORIZON (FR-23). A compacted log holds nothing
39
+ * before its checkpoint, so a batch it holds no op of is behind the
40
+ * horizon, or was never made: either way there is nothing here to put
41
+ * back, and the sentence names where history stops.
42
+ */
43
+ const horizon = [...log.epochs()].reverse().find((epoch) => epoch.horizon);
44
+ if (horizon) {
45
+ const held = new Set(log.all().map((op) => op.batch));
46
+ const behind = batchIds.filter((id) => !held.has(id));
47
+ if (behind.length > 0) {
48
+ return {
49
+ ok: false,
50
+ ops,
51
+ blockedBy: [],
52
+ includeBatches: [],
53
+ reason: "missing",
54
+ message: `Cannot undo ${behind.map((id) => `batch ${id}`).join(", ")}: it is not after the undo horizon at op ${horizon.seq}${horizon.at ? ` (${horizon.at})` : ""}. What was done before the horizon is archived, and undo does not reach behind it.`,
55
+ };
56
+ }
57
+ }
13
58
  if (ops.length === 0) {
14
59
  return {
15
60
  ok: false,
16
61
  ops,
17
62
  blockedBy: [],
18
63
  includeBatches: [],
64
+ reason: "missing",
19
65
  message: batchIds.length === 0
20
66
  ? "No batch given to undo"
21
67
  : `Nothing live to undo in ${batchIds.join(", ")} — already undone, or never applied`,
22
68
  };
23
69
  }
70
+ /*
71
+ * NOT A CHANGE YOU CANNOT SEE (FR-16). A withheld op carries no inverse
72
+ * to put back, and taking it back is for somebody who can see it; the
73
+ * sentence says so without saying what it was.
74
+ */
75
+ if (ops.some(isWithheld)) {
76
+ return {
77
+ ok: false,
78
+ ops,
79
+ blockedBy: [],
80
+ includeBatches: [],
81
+ reason: "forbidden",
82
+ message: ops.every(isWithheld)
83
+ ? "Cannot undo a change you cannot see: only somebody who can see it can take it back."
84
+ : "Cannot undo this here: part of it is a change you cannot see, and only somebody who can see it can take it back.",
85
+ };
86
+ }
87
+ /*
88
+ * NOT A MODULE TURNED OFF OR ON (FR-12). It touches no record, so there
89
+ * is nothing to put back, and the workspace's set is changed by turning
90
+ * the module the other way — which is the host's to do.
91
+ */
92
+ if (ops.some((op) => op.enabledModules !== undefined)) {
93
+ return {
94
+ ok: false,
95
+ ops,
96
+ blockedBy: [],
97
+ includeBatches: [],
98
+ message: "Cannot undo turning a module off or on: it is changed by turning the module the other way.",
99
+ };
100
+ }
24
101
  const writes = new Set();
25
102
  for (const op of ops)
26
103
  for (const id of op.writes)
27
104
  writes.add(id);
28
105
  const earliest = Math.min(...ops.map((op) => op.seq));
106
+ /*
107
+ * NOT ACROSS A DECLARATION CHANGE (FR-27). An op from before the change
108
+ * carries an inverse written in the old declaration's words: putting it
109
+ * back would write the old shape into the new graph. The change is named,
110
+ * since it is what stands in the way, not anything a person did since.
111
+ */
112
+ const crossed = log.epochs().find((epoch) => epoch.change !== undefined && epoch.seq > earliest);
113
+ if (crossed) {
114
+ const before = ops.find((op) => op.seq < crossed.seq);
115
+ return {
116
+ ok: false,
117
+ ops,
118
+ blockedBy: [],
119
+ includeBatches: [],
120
+ message: `Cannot undo "${before.intent}": it was done before the declaration changed (${crossed.change}), and undo does not reach back across that change.`,
121
+ };
122
+ }
29
123
  const blockedBy = [];
30
124
  for (const op of log.all()) {
31
125
  if (op.seq <= earliest)
@@ -40,10 +134,25 @@ export function checkUndo(log, batchIds) {
40
134
  }
41
135
  if (blockedBy.length === 0)
42
136
  return { ok: true, ops };
43
- const includeBatches = [...new Set(blockedBy.map((b) => b.op.batch))];
137
+ /*
138
+ * A LATER CHANGE YOU CANNOT SEE is said to be one, and nothing more: not
139
+ * its sentence, its id or what it read (FR-16). It cannot come along
140
+ * either, so no batch is offered while one stands in the way.
141
+ */
44
142
  const named = blockedBy
143
+ .filter((b) => !isWithheld(b.op))
45
144
  .map((b) => `"${b.op.intent}" (op ${b.op.id}, read ${b.overlap.join(", ")})`)
46
145
  .join("; ");
146
+ if (blockedBy.some((b) => isWithheld(b.op))) {
147
+ return {
148
+ ok: false,
149
+ ops,
150
+ blockedBy,
151
+ includeBatches: [],
152
+ message: `Cannot undo on its own — ${named ? `a later operation depends on it: ${named}; and ` : ""}a later change you cannot see depends on it, which only somebody who can see it can take back.`,
153
+ };
154
+ }
155
+ const includeBatches = [...new Set(blockedBy.map((b) => b.op.batch))];
47
156
  return {
48
157
  ok: false,
49
158
  ops,
@@ -1 +1 @@
1
- {"version":3,"file":"undo.js","sourceRoot":"","sources":["../../src/ops/undo.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"undo.js","sourceRoot":"","sources":["../../src/ops/undo.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,37 @@
1
+ import type { Primitive } from "../graph/primitives.js";
2
+ import type { Author, Operation } from "./types.js";
3
+ /**
4
+ * A LOG A SEAT MAY NOT FULLY SEE IS REDACTED, NOT GAPPED (FR-16).
5
+ *
6
+ * A store that keeps some records from a seat cannot simply drop the ops
7
+ * that touched them: a log is contiguous by construction (undo, replay and
8
+ * `OperationLog.from` all count on it), and a hole would also say less than
9
+ * the truth — something did happen there. So the op stays, as a WITHHELD
10
+ * op: its place in the log and nothing that says what it was.
11
+ */
12
+ /** What a withheld op says it was, wherever it is read out. */
13
+ export declare const WITHHELD_INTENT = "A change you cannot see";
14
+ /** Who a withheld op says made it: nobody this seat may be told about. */
15
+ export declare const WITHHELD_AUTHOR: Author;
16
+ /** Whether an op is one a store withheld from the seat reading it. */
17
+ export declare function isWithheld(op: Pick<Operation, "withheld">): boolean;
18
+ /** The record ids a primitive touches. */
19
+ export declare function touchedBy(primitive: Primitive): readonly string[];
20
+ /**
21
+ * Whether an op touched, read or named anything `sees` says no to: then it
22
+ * is withheld whole, because its sentence ("Book a test drive for Freya")
23
+ * names what it touched as surely as its primitives do.
24
+ */
25
+ export declare function touchesUnseen(op: Operation, sees: (id: string) => boolean): boolean;
26
+ /**
27
+ * THE OP AS A SEAT THAT MAY NOT SEE IT RECEIVES IT. Its id, seq, batch and
28
+ * time stand; who made it, what it meant, the call and the inverse go. Its
29
+ * primitives keep the ones that touch only what the seat sees — a change to
30
+ * a record the seat can see still reaches it, or its copy of the graph
31
+ * would go stale — and its reads and writes keep only seen ids, so an undo
32
+ * the seat asks for is still blocked by it where it should be.
33
+ */
34
+ export declare function withhold(op: Operation, sees: (id: string) => boolean): Operation;
35
+ /** Every op of a log as a seat receives it: what it may see as it is, the rest withheld in place. */
36
+ export declare function redact(ops: readonly Operation[], sees: (id: string) => boolean): Operation[];
37
+ //# sourceMappingURL=withheld.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"withheld.d.ts","sourceRoot":"","sources":["../../src/ops/withheld.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACxD,OAAO,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAEpD;;;;;;;;GAQG;AAEH,+DAA+D;AAC/D,eAAO,MAAM,eAAe,4BAA4B,CAAC;AAEzD,0EAA0E;AAC1E,eAAO,MAAM,eAAe,EAAE,MAA4D,CAAC;AAE3F,sEAAsE;AACtE,wBAAgB,UAAU,CAAC,EAAE,EAAE,IAAI,CAAC,SAAS,EAAE,UAAU,CAAC,GAAG,OAAO,CAEnE;AAED,0CAA0C;AAC1C,wBAAgB,SAAS,CAAC,SAAS,EAAE,SAAS,GAAG,SAAS,MAAM,EAAE,CAWjE;AAUD;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,GAAG,OAAO,CAInF;AAED;;;;;;;GAOG;AACH,wBAAgB,QAAQ,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,GAAG,SAAS,CAgBhF;AAED,qGAAqG;AACrG,wBAAgB,MAAM,CAAC,GAAG,EAAE,SAAS,SAAS,EAAE,EAAE,IAAI,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,GAAG,SAAS,EAAE,CAE5F"}
@@ -0,0 +1,84 @@
1
+ /**
2
+ * A LOG A SEAT MAY NOT FULLY SEE IS REDACTED, NOT GAPPED (FR-16).
3
+ *
4
+ * A store that keeps some records from a seat cannot simply drop the ops
5
+ * that touched them: a log is contiguous by construction (undo, replay and
6
+ * `OperationLog.from` all count on it), and a hole would also say less than
7
+ * the truth — something did happen there. So the op stays, as a WITHHELD
8
+ * op: its place in the log and nothing that says what it was.
9
+ */
10
+ /** What a withheld op says it was, wherever it is read out. */
11
+ export const WITHHELD_INTENT = "A change you cannot see";
12
+ /** Who a withheld op says made it: nobody this seat may be told about. */
13
+ export const WITHHELD_AUTHOR = { kind: "system", id: "withheld", name: "Someone" };
14
+ /** Whether an op is one a store withheld from the seat reading it. */
15
+ export function isWithheld(op) {
16
+ return op.withheld === true;
17
+ }
18
+ /** The record ids a primitive touches. */
19
+ export function touchedBy(primitive) {
20
+ switch (primitive.op) {
21
+ case "add-node":
22
+ case "remove-node":
23
+ return [primitive.node.id];
24
+ case "patch-node":
25
+ return [primitive.id];
26
+ case "add-edge":
27
+ case "remove-edge":
28
+ return [primitive.edge.from, primitive.edge.to];
29
+ }
30
+ }
31
+ /** Every string a call's arguments hold, at any depth: where a record's id would be. */
32
+ function strings(value, into = []) {
33
+ if (typeof value === "string")
34
+ into.push(value);
35
+ else if (Array.isArray(value))
36
+ for (const inner of value)
37
+ strings(inner, into);
38
+ else if (value !== null && typeof value === "object")
39
+ for (const inner of Object.values(value))
40
+ strings(inner, into);
41
+ return into;
42
+ }
43
+ /**
44
+ * Whether an op touched, read or named anything `sees` says no to: then it
45
+ * is withheld whole, because its sentence ("Book a test drive for Freya")
46
+ * names what it touched as surely as its primitives do.
47
+ */
48
+ export function touchesUnseen(op, sees) {
49
+ if (op.primitives.some((primitive) => touchedBy(primitive).some((id) => !sees(id))))
50
+ return true;
51
+ if (op.writes.some((id) => !sees(id)) || op.reads.some((id) => !sees(id)))
52
+ return true;
53
+ return op.mutation !== null && strings(op.mutation.args).some((value) => !sees(value));
54
+ }
55
+ /**
56
+ * THE OP AS A SEAT THAT MAY NOT SEE IT RECEIVES IT. Its id, seq, batch and
57
+ * time stand; who made it, what it meant, the call and the inverse go. Its
58
+ * primitives keep the ones that touch only what the seat sees — a change to
59
+ * a record the seat can see still reaches it, or its copy of the graph
60
+ * would go stale — and its reads and writes keep only seen ids, so an undo
61
+ * the seat asks for is still blocked by it where it should be.
62
+ */
63
+ export function withhold(op, sees) {
64
+ return {
65
+ id: op.id,
66
+ seq: op.seq,
67
+ batch: op.batch,
68
+ author: WITHHELD_AUTHOR,
69
+ intent: WITHHELD_INTENT,
70
+ mutation: null,
71
+ primitives: op.primitives.filter((primitive) => touchedBy(primitive).every(sees)),
72
+ inverse: [],
73
+ reads: op.reads.filter(sees),
74
+ writes: op.writes.filter(sees),
75
+ at: op.at,
76
+ ...(op.undoes !== undefined ? { undoes: op.undoes } : {}),
77
+ withheld: true,
78
+ };
79
+ }
80
+ /** Every op of a log as a seat receives it: what it may see as it is, the rest withheld in place. */
81
+ export function redact(ops, sees) {
82
+ return ops.map((op) => (op.withheld || !touchesUnseen(op, sees) ? op : withhold(op, sees)));
83
+ }
84
+ //# sourceMappingURL=withheld.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"withheld.js","sourceRoot":"","sources":["../../src/ops/withheld.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,21 @@
1
1
  import type { AnyMutationDefinition } from "../mutations/types.js";
2
2
  import type { AnySchema } from "../schema/schema.js";
3
3
  import type { Policy, Principal, Refusal } from "./types.js";
4
+ /**
5
+ * THE SEAT A POLICY JUDGES, delegation read through (FR-06). An agent acting
6
+ * for a person may do what BOTH may: its roles are the intersection of its
7
+ * own and theirs (an agent that declared none takes the person's), and a
8
+ * `self` grant — "you, on yours" — is about the person, not the agent. A
9
+ * principal acting for nobody is judged as itself.
10
+ */
11
+ export declare function actingAs(principal: Principal): Principal;
12
+ /**
13
+ * THE HOST'S OWN WORK HAS A SEAT (FR-17). A template's setup, a seed, a
14
+ * migration applied by the system is not a person a policy was written
15
+ * about, and `roles: ["*"]` matched no grant — so a host gave its system
16
+ * every role the policy named. `system` is a kind, not a role: it passes.
17
+ */
18
+ export declare const isSystem: (principal: Principal) => boolean;
4
19
  /**
5
20
  * WHAT A REFUSAL CALLS THINGS. The policy knows acts and roles by their
6
21
  * names; a person reads "Not permitted: close-deal on a deal — sales-manager