@graview/core 0.1.3 → 0.1.4

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 (97) hide show
  1. package/dist/app.d.ts +9 -1
  2. package/dist/app.d.ts.map +1 -1
  3. package/dist/app.js.map +1 -1
  4. package/dist/capabilities.js +1 -1
  5. package/dist/capabilities.js.map +1 -1
  6. package/dist/cli/check/policy.d.ts.map +1 -1
  7. package/dist/cli/check/policy.js +40 -0
  8. package/dist/cli/check/policy.js.map +1 -1
  9. package/dist/cli/index.d.ts +2 -1
  10. package/dist/cli/index.d.ts.map +1 -1
  11. package/dist/cli/index.js.map +1 -1
  12. package/dist/document/canonical.d.ts +2 -1
  13. package/dist/document/canonical.d.ts.map +1 -1
  14. package/dist/document/canonical.js +2 -1
  15. package/dist/document/canonical.js.map +1 -1
  16. package/dist/document/diff.d.ts.map +1 -1
  17. package/dist/document/diff.js +3 -1
  18. package/dist/document/diff.js.map +1 -1
  19. package/dist/document/edit.d.ts +7 -1
  20. package/dist/document/edit.d.ts.map +1 -1
  21. package/dist/document/edit.js +46 -2
  22. package/dist/document/edit.js.map +1 -1
  23. package/dist/document/index.d.ts +1 -1
  24. package/dist/document/index.d.ts.map +1 -1
  25. package/dist/document/migrate.d.ts.map +1 -1
  26. package/dist/document/migrate.js +3 -2
  27. package/dist/document/migrate.js.map +1 -1
  28. package/dist/document/patch.d.ts.map +1 -1
  29. package/dist/document/patch.js +2 -5
  30. package/dist/document/patch.js.map +1 -1
  31. package/dist/document/schema.d.ts +10 -10
  32. package/dist/index.d.ts +7 -6
  33. package/dist/index.d.ts.map +1 -1
  34. package/dist/index.js +5 -4
  35. package/dist/index.js.map +1 -1
  36. package/dist/integrity.d.ts.map +1 -1
  37. package/dist/integrity.js +35 -5
  38. package/dist/integrity.js.map +1 -1
  39. package/dist/invariants/types.d.ts +1 -1
  40. package/dist/invariants/types.d.ts.map +1 -1
  41. package/dist/mutations/define-mutation.d.ts +22 -2
  42. package/dist/mutations/define-mutation.d.ts.map +1 -1
  43. package/dist/mutations/define-mutation.js +18 -5
  44. package/dist/mutations/define-mutation.js.map +1 -1
  45. package/dist/mutations/node-ref.d.ts +5 -2
  46. package/dist/mutations/node-ref.d.ts.map +1 -1
  47. package/dist/mutations/node-ref.js +28 -7
  48. package/dist/mutations/node-ref.js.map +1 -1
  49. package/dist/mutations/types.d.ts +2 -2
  50. package/dist/mutations/types.d.ts.map +1 -1
  51. package/dist/ops/log.d.ts +11 -0
  52. package/dist/ops/log.d.ts.map +1 -1
  53. package/dist/ops/log.js.map +1 -1
  54. package/dist/ops/served.d.ts +80 -0
  55. package/dist/ops/served.d.ts.map +1 -0
  56. package/dist/ops/served.js +316 -0
  57. package/dist/ops/served.js.map +1 -0
  58. package/dist/ops/types.d.ts +8 -0
  59. package/dist/ops/types.d.ts.map +1 -1
  60. package/dist/ops/withheld.d.ts +63 -8
  61. package/dist/ops/withheld.d.ts.map +1 -1
  62. package/dist/ops/withheld.js +198 -22
  63. package/dist/ops/withheld.js.map +1 -1
  64. package/dist/ops/writers.d.ts +29 -0
  65. package/dist/ops/writers.d.ts.map +1 -0
  66. package/dist/ops/writers.js +98 -0
  67. package/dist/ops/writers.js.map +1 -0
  68. package/dist/permissions/sight.d.ts +15 -2
  69. package/dist/permissions/sight.d.ts.map +1 -1
  70. package/dist/permissions/sight.js +34 -5
  71. package/dist/permissions/sight.js.map +1 -1
  72. package/dist/presence.d.ts +24 -2
  73. package/dist/presence.d.ts.map +1 -1
  74. package/dist/presence.js +26 -1
  75. package/dist/presence.js.map +1 -1
  76. package/dist/refusal.d.ts +9 -5
  77. package/dist/refusal.d.ts.map +1 -1
  78. package/dist/refusal.js +9 -5
  79. package/dist/refusal.js.map +1 -1
  80. package/dist/schema/json-schema.d.ts.map +1 -1
  81. package/dist/schema/json-schema.js +27 -1
  82. package/dist/schema/json-schema.js.map +1 -1
  83. package/dist/schema/schema.d.ts +19 -4
  84. package/dist/schema/schema.d.ts.map +1 -1
  85. package/dist/schema/schema.js.map +1 -1
  86. package/dist/seen.d.ts +54 -1
  87. package/dist/seen.d.ts.map +1 -1
  88. package/dist/seen.js +457 -25
  89. package/dist/seen.js.map +1 -1
  90. package/dist/store.d.ts +90 -10
  91. package/dist/store.d.ts.map +1 -1
  92. package/dist/store.js +146 -35
  93. package/dist/store.js.map +1 -1
  94. package/dist/testing.js.map +1 -1
  95. package/dist/version.d.ts +1 -1
  96. package/dist/version.js +1 -1
  97. package/package.json +2 -2
@@ -1,4 +1,5 @@
1
- import type { Primitive } from "../graph/primitives.js";
1
+ import { type Primitive } from "../graph/primitives.js";
2
+ import { type FieldWriter, type Timeline } from "./served.js";
2
3
  import type { Author, Operation } from "./types.js";
3
4
  /**
4
5
  * A LOG A SEAT MAY NOT FULLY SEE IS REDACTED, NOT GAPPED (FR-16).
@@ -17,6 +18,52 @@ export declare const WITHHELD_AUTHOR: Author;
17
18
  export declare function isWithheld(op: Pick<Operation, "withheld">): boolean;
18
19
  /** The record ids a primitive touches. */
19
20
  export declare function touchedBy(primitive: Primitive): readonly string[];
21
+ /**
22
+ * WHAT ONE SEAT IS SERVED (FR-55): the judgement a log is redacted by.
23
+ * `sees` is the seat's sight — the ids it may be told. `shows` is what it
24
+ * is served: a record it sees, unless a field the record cannot do without
25
+ * names one it may not see. `served` is a record as the seat receives it,
26
+ * and `optional` says whether a kind's field may be left off one. A bare
27
+ * `(id) => boolean` is a lens that clears nothing: a record whose field
28
+ * names an unseen id is not served at all.
29
+ */
30
+ export interface SeatLens {
31
+ readonly sees: (id: string) => boolean;
32
+ readonly shows: (id: string) => boolean;
33
+ readonly served: <N extends {
34
+ readonly id: string;
35
+ readonly kind: string;
36
+ }>(node: N) => N | undefined;
37
+ /** A record as the seat is served it, given who wrote each field's value: the seat's own words are served as written (FR-55). */
38
+ readonly servedWith: <N extends {
39
+ readonly id: string;
40
+ readonly kind: string;
41
+ }>(node: N, writer: FieldWriter) => N | undefined;
42
+ /** The seat whose words these are — the person, for an agent acting for one. */
43
+ readonly actor?: string;
44
+ readonly optional: (kind: string, field: string) => boolean;
45
+ /** The kind of a record, by id, when it is known: for a patch, whose primitive does not say. */
46
+ readonly kindOf: (id: string) => string | undefined;
47
+ /** The store's graph now and the log that led to it, so an op is judged where it stood (`serveAlong`). */
48
+ readonly timeline?: Timeline;
49
+ /**
50
+ * THIS LENS AS OF NOW, ITS JUDGEMENTS KEPT: each id judged once, each
51
+ * record served once, for one reading of a store that does not change
52
+ * while it is read — a log redacted, a snapshot served. A lens follows
53
+ * the store as it is asked (FR-51); a pinned one is thrown away after the
54
+ * reading it was made for, so it never answers for a store that moved.
55
+ */
56
+ readonly pin?: () => SeatLens;
57
+ }
58
+ /** A lens over a bare sight: it serves a record whole or not at all. */
59
+ export declare function lensOf(seeing: SeatLens | ((id: string) => boolean)): SeatLens;
60
+ /**
61
+ * WHETHER A VALUE NAMES ANYTHING THE SEAT MAY NOT SEE (FR-55): any string
62
+ * in it, at any depth, a value or a key, that is an unseen record's id. An
63
+ * id is minted from a label, so an id a seat is served is the name of what
64
+ * it was kept from.
65
+ */
66
+ export declare function namesUnseen(value: unknown, sees: (id: string) => boolean): boolean;
20
67
  /**
21
68
  * Whether an op touched, read or named anything `sees` says no to: then it
22
69
  * is withheld whole, because its sentence ("Book a test drive for Freya")
@@ -26,12 +73,20 @@ export declare function touchesUnseen(op: Operation, sees: (id: string) => boole
26
73
  /**
27
74
  * THE OP AS A SEAT THAT MAY NOT SEE IT RECEIVES IT. Its id, seq, batch and
28
75
  * 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.
76
+ * primitives keep the ones that touch only what the seat is served, as it
77
+ * is served them (FR-55) — a change to a record the seat can see still
78
+ * reaches it, or its copy of the graph would go stale, and no id of what it
79
+ * may not see rides along in a field — and its reads and writes keep only
80
+ * served ids, so an undo the seat asks for is still blocked by it where it
81
+ * should be.
82
+ */
83
+ export declare function withhold(op: Operation, seeing: SeatLens | ((id: string) => boolean), served?: readonly Primitive[]): Operation;
84
+ /**
85
+ * Every op of a log as a seat receives it: what it may see as it is, the
86
+ * rest withheld in place. An op is withheld when it touches a record the
87
+ * seat is not served, or names one it may not see ANYWHERE — its call, its
88
+ * sentence, its author, a field value in a primitive or its inverse
89
+ * (FR-55). One withheld already is withheld again under this seat's lens.
33
90
  */
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[];
91
+ export declare function redact(ops: readonly Operation[], seeing: SeatLens | ((id: string) => boolean)): Operation[];
37
92
  //# sourceMappingURL=withheld.d.ts.map
@@ -1 +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"}
1
+ {"version":3,"file":"withheld.d.ts","sourceRoot":"","sources":["../../src/ops/withheld.ts"],"names":[],"mappings":"AAAA,OAAO,EAAS,KAAK,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAI/D,OAAO,EAAc,KAAK,WAAW,EAAE,KAAK,QAAQ,EAAE,MAAM,aAAa,CAAC;AAC1E,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;AAED;;;;;;;;GAQG;AACH,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC;IACvC,QAAQ,CAAC,KAAK,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC;IACxC,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC,SAAS;QAAE,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;KAAE,EAAE,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,SAAS,CAAC;IACtG,iIAAiI;IACjI,QAAQ,CAAC,UAAU,EAAE,CAAC,CAAC,SAAS;QAAE,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;KAAE,EAAE,IAAI,EAAE,CAAC,EAAE,MAAM,EAAE,WAAW,KAAK,CAAC,GAAG,SAAS,CAAC;IAC/H,gFAAgF;IAChF,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC;IAC5D,gGAAgG;IAChG,QAAQ,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAC;IACpD,0GAA0G;IAC1G,QAAQ,CAAC,QAAQ,CAAC,EAAE,QAAQ,CAAC;IAC7B;;;;;;OAMG;IACH,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,QAAQ,CAAC;CAC/B;AAMD,wEAAwE;AACxE,wBAAgB,MAAM,CAAC,MAAM,EAAE,QAAQ,GAAG,CAAC,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,GAAG,QAAQ,CAW7E;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,GAAG,OAAO,CAalF;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,GAAG,OAAO,CAInF;AAwCD;;;;;;;;;GASG;AACH,wBAAgB,QAAQ,CAAC,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,GAAG,CAAC,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,EAAE,MAAM,CAAC,EAAE,SAAS,SAAS,EAAE,GAAG,SAAS,CAsB9H;AAED;;;;;;GAMG;AACH,wBAAgB,MAAM,CAAC,GAAG,EAAE,SAAS,SAAS,EAAE,EAAE,MAAM,EAAE,QAAQ,GAAG,CAAC,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,GAAG,SAAS,EAAE,CAmB3G"}
@@ -1,3 +1,6 @@
1
+ import { UNSET } from "../graph/primitives.js";
2
+ import { actingAs } from "../permissions/policy.js";
3
+ import { serveAlong } from "./served.js";
1
4
  /**
2
5
  * A LOG A SEAT MAY NOT FULLY SEE IS REDACTED, NOT GAPPED (FR-16).
3
6
  *
@@ -28,17 +31,47 @@ export function touchedBy(primitive) {
28
31
  return [primitive.edge.from, primitive.edge.to];
29
32
  }
30
33
  }
31
- /** Every string a call's arguments hold, at any depth: where a record's id would be. */
32
- function strings(value, into = []) {
34
+ /** Whether any field of a record — not its id or kind — names what `sees` says no to. */
35
+ const fieldsNameUnseen = (node, sees) => Object.entries(node).some(([key, value]) => key !== "id" && key !== "kind" && namesUnseen(value, sees));
36
+ /** A lens over a bare sight: it serves a record whole or not at all. */
37
+ export function lensOf(seeing) {
38
+ if (typeof seeing !== "function")
39
+ return seeing;
40
+ const sees = seeing;
41
+ return {
42
+ sees,
43
+ shows: sees,
44
+ served: (node) => (sees(node.id) && !fieldsNameUnseen(node, sees) ? node : undefined),
45
+ servedWith: (node) => (sees(node.id) && !fieldsNameUnseen(node, sees) ? node : undefined),
46
+ optional: () => false,
47
+ kindOf: () => undefined,
48
+ };
49
+ }
50
+ /**
51
+ * WHETHER A VALUE NAMES ANYTHING THE SEAT MAY NOT SEE (FR-55): any string
52
+ * in it, at any depth, a value or a key, that is an unseen record's id. An
53
+ * id is minted from a label, so an id a seat is served is the name of what
54
+ * it was kept from.
55
+ */
56
+ export function namesUnseen(value, sees) {
33
57
  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;
58
+ return !sees(value);
59
+ if (value === null || typeof value !== "object")
60
+ return false;
61
+ // Walked without copying: a served log asks this of every op in it.
62
+ if (Array.isArray(value)) {
63
+ for (let at = 0; at < value.length; at++)
64
+ if (namesUnseen(value[at], sees))
65
+ return true;
66
+ return false;
67
+ }
68
+ for (const key in value) {
69
+ if (!Object.hasOwn(value, key))
70
+ continue;
71
+ if (!sees(key) || namesUnseen(value[key], sees))
72
+ return true;
73
+ }
74
+ return false;
42
75
  }
43
76
  /**
44
77
  * Whether an op touched, read or named anything `sees` says no to: then it
@@ -50,17 +83,62 @@ export function touchesUnseen(op, sees) {
50
83
  return true;
51
84
  if (op.writes.some((id) => !sees(id)) || op.reads.some((id) => !sees(id)))
52
85
  return true;
53
- return op.mutation !== null && strings(op.mutation.args).some((value) => !sees(value));
86
+ return op.mutation !== null && namesUnseen(op.mutation.args, sees);
87
+ }
88
+ /**
89
+ * A PRIMITIVE AS A SEAT RECEIVES IT, or nothing (FR-55). One that touches a
90
+ * record the seat is not served goes. A record it adds is served as the
91
+ * seat's view serves it — a field naming an unseen record cleared, or the
92
+ * primitive dropped when that field is one the record cannot do without,
93
+ * so a client never adds a record that fails its own declaration. A patch
94
+ * that wrote such a field says the field was cleared, as the view shows
95
+ * it; one it cannot clear goes. A removal needs only the record's id.
96
+ */
97
+ function servedPrimitive(primitive, lens) {
98
+ if (!touchedBy(primitive).every(lens.shows))
99
+ return undefined;
100
+ switch (primitive.op) {
101
+ case "add-node": {
102
+ const node = lens.served(primitive.node);
103
+ return node ? (node === primitive.node ? primitive : { op: "add-node", node }) : undefined;
104
+ }
105
+ case "remove-node": {
106
+ const node = primitive.node;
107
+ if (!fieldsNameUnseen(node, lens.sees))
108
+ return primitive;
109
+ return { op: "remove-node", node: Object.fromEntries(Object.entries(node).filter(([key, value]) => key === "id" || key === "kind" || !namesUnseen(value, lens.sees))) };
110
+ }
111
+ case "patch-node": {
112
+ const kind = lens.kindOf(primitive.id);
113
+ const after = {};
114
+ for (const [field, value] of Object.entries(primitive.after)) {
115
+ if (!namesUnseen(value, lens.sees))
116
+ after[field] = value;
117
+ else if (kind !== undefined && lens.optional(kind, field))
118
+ after[field] = UNSET;
119
+ else
120
+ return undefined;
121
+ }
122
+ const before = Object.fromEntries(Object.entries(primitive.before).filter(([, value]) => !namesUnseen(value, lens.sees)));
123
+ return { op: "patch-node", id: primitive.id, before, after };
124
+ }
125
+ case "add-edge":
126
+ case "remove-edge":
127
+ return namesUnseen(primitive.edge, lens.sees) ? undefined : primitive;
128
+ }
54
129
  }
55
130
  /**
56
131
  * THE OP AS A SEAT THAT MAY NOT SEE IT RECEIVES IT. Its id, seq, batch and
57
132
  * 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.
133
+ * primitives keep the ones that touch only what the seat is served, as it
134
+ * is served them (FR-55) — a change to a record the seat can see still
135
+ * reaches it, or its copy of the graph would go stale, and no id of what it
136
+ * may not see rides along in a field — and its reads and writes keep only
137
+ * served ids, so an undo the seat asks for is still blocked by it where it
138
+ * should be.
62
139
  */
63
- export function withhold(op, sees) {
140
+ export function withhold(op, seeing, served) {
141
+ const lens = lensOf(seeing);
64
142
  return {
65
143
  id: op.id,
66
144
  seq: op.seq,
@@ -68,17 +146,115 @@ export function withhold(op, sees) {
68
146
  author: WITHHELD_AUTHOR,
69
147
  intent: WITHHELD_INTENT,
70
148
  mutation: null,
71
- primitives: op.primitives.filter((primitive) => touchedBy(primitive).every(sees)),
149
+ primitives: served ??
150
+ op.primitives.flatMap((primitive) => {
151
+ const one = servedPrimitive(primitive, lens);
152
+ return one ? [one] : [];
153
+ }),
72
154
  inverse: [],
73
- reads: op.reads.filter(sees),
74
- writes: op.writes.filter(sees),
155
+ reads: op.reads.filter((id) => lens.shows(id) && isRecord(lens, id)),
156
+ writes: op.writes.filter((id) => lens.shows(id) && isRecord(lens, id)),
75
157
  at: op.at,
76
158
  ...(op.undoes !== undefined ? { undoes: op.undoes } : {}),
77
159
  withheld: true,
78
160
  };
79
161
  }
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)));
162
+ /**
163
+ * Every op of a log as a seat receives it: what it may see as it is, the
164
+ * rest withheld in place. An op is withheld when it touches a record the
165
+ * seat is not served, or names one it may not see ANYWHERE — its call, its
166
+ * sentence, its author, a field value in a primitive or its inverse
167
+ * (FR-55). One withheld already is withheld again under this seat's lens.
168
+ */
169
+ export function redact(ops, seeing) {
170
+ // One reading of the store, whatever its length: every id and record judged once in it.
171
+ const given = lensOf(seeing);
172
+ const lens = given.pin?.() ?? given;
173
+ /*
174
+ * ALONG THE STORE'S HISTORY, when the lens knows it: each primitive judged
175
+ * by whether its record was served just before it and just after, so the
176
+ * ops a seat is served fold to the snapshot it is served (`serveAlong`).
177
+ */
178
+ if (lens.timeline) {
179
+ const judge = {
180
+ served: (node, writer) => lens.servedWith(node, writer),
181
+ edgeClean: (edge) => !namesUnseen(edge, lens.sees),
182
+ sees: lens.sees,
183
+ names: (value) => namesUnseen(value, lens.sees),
184
+ };
185
+ return serveAlong(ops, lens.timeline, judge).map(({ op, primitives, faithful }) => (!op.withheld && faithful ? whole(op, lens, primitives) : undefined) ?? withhold(op, lens, primitives));
186
+ }
187
+ return ops.map((op) => (!op.withheld && !touchesUnseen(op, lens.shows) ? whole(op, lens) : undefined) ?? withhold(op, lens));
188
+ }
189
+ /**
190
+ * AN OP SERVED WHOLE, when what it did is all the seat's to know: as it is
191
+ * when it names nothing hidden, and with its reads and writes trimmed of
192
+ * what is hidden when that is the only place it does — what it looked at
193
+ * is not the seat's to be told, what it did is. A string its primitives are
194
+ * served with, as the seat is served them, is the seat's to read anywhere
195
+ * in it — a field value the seat wrote, in a patch and in the inverse that
196
+ * would put it back — and so is its call, when the call was the seat's own
197
+ * (FR-55). Otherwise its call, sentence, author and inverse are not
198
+ * trimmed: an act's sentence is made from its call ("Compare with Freya
199
+ * Davies"), so one that names a hidden record anywhere withholds the op.
200
+ */
201
+ function whole(op, lens, served) {
202
+ /*
203
+ * WHAT IT READ, AS RECORDS THE SEAT SEES (FR-55). An act that looked for a
204
+ * record that is not there read its absence, and one that looked for a
205
+ * hidden one read nothing the seat may be told: both are left out alike,
206
+ * or the reads of a seat's own act would say which guess was real.
207
+ */
208
+ const kept = (id) => lens.sees(id) && isRecord(lens, id);
209
+ const reads = op.reads.filter(kept);
210
+ const writes = op.writes.filter(kept);
211
+ const tidy = reads.length === op.reads.length && writes.length === op.writes.length ? op : { ...op, reads, writes };
212
+ /*
213
+ * ANOTHER AUTHOR'S SENTENCE, ONLY IF ITS READER SEES WHAT IT READ. A
214
+ * sentence names records by their labels, which no id check catches;
215
+ * the op says which records its `describe` read, and a seat that may not
216
+ * see one of them is served the op withheld. The seat's own sentences
217
+ * were worded from its own view.
218
+ */
219
+ const own = lens.actor !== undefined && actingAs(op.author).id === lens.actor;
220
+ if (!own && op.described?.some((id) => !lens.sees(id)))
221
+ return undefined;
222
+ if (!namesUnseen(op, lens.sees))
223
+ return tidy;
224
+ const said = new Set();
225
+ const collect = (value) => {
226
+ if (typeof value === "string")
227
+ said.add(value);
228
+ else if (Array.isArray(value))
229
+ for (const inner of value)
230
+ collect(inner);
231
+ else if (value !== null && typeof value === "object")
232
+ for (const [key, inner] of Object.entries(value)) {
233
+ said.add(key);
234
+ collect(inner);
235
+ }
236
+ };
237
+ /*
238
+ * THE SEAT'S OWN WORDS EXCUSE ONLY WHAT IT WROTE. A value the primitives
239
+ * are served with (the seat wrote it, or it would have been cleared) may
240
+ * stand in the inverse that puts it back; the call may name what the
241
+ * seat said only when the call is the seat's own. Another author's call,
242
+ * sentence and name are judged by sight alone, whatever the seat wrote.
243
+ */
244
+ if (served)
245
+ collect(served);
246
+ const told = (id) => lens.sees(id) || said.has(id);
247
+ if (namesUnseen(tidy.inverse, told))
248
+ return undefined;
249
+ if (own) {
250
+ if (op.mutation)
251
+ collect(op.mutation.args);
252
+ return namesUnseen({ ...tidy, primitives: [], inverse: [] }, told) ? undefined : tidy;
253
+ }
254
+ return namesUnseen({ ...tidy, primitives: [], inverse: [] }, lens.sees) ? undefined : tidy;
255
+ }
256
+ /** Whether an id names a record the store knows — in the graph or its log — when the lens can say; a bare sight cannot, and keeps every id. */
257
+ function isRecord(lens, id) {
258
+ return lens.timeline === undefined || lens.kindOf(id) !== undefined;
83
259
  }
84
260
  //# sourceMappingURL=withheld.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"withheld.js","sourceRoot":"","sources":["../../src/ops/withheld.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"withheld.js","sourceRoot":"","sources":["../../src/ops/withheld.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,29 @@
1
+ import type { Operation } from "./types.js";
2
+ /**
3
+ * WHO WROTE EACH FIELD'S VALUE, AND WHEN (FR-55).
4
+ *
5
+ * A seat is served its own words: a field whose value the seat wrote is
6
+ * served as written even when it names a record the seat may not see,
7
+ * because saying back what a seat said tells it nothing — and judging it
8
+ * like anybody else's would tell a seat that guessed an id whether the
9
+ * guess was real. So the seat view has to know who wrote a field's value,
10
+ * at the moment it is served and at every moment of the log it serves.
11
+ *
12
+ * Each op that writes a field is an entry: the seq, and the seat that wrote
13
+ * it — the person, for an agent acting for one. An undo is never the
14
+ * author of what it puts back: the value it restores is the words of
15
+ * whoever wrote it before the op it takes back. Kept as the log goes, like
16
+ * `recordsOf`: a call reads only the ops appended since the last, a log cut
17
+ * back takes out what its cut ops wrote, and a log compacted behind its
18
+ * horizon keeps what was read before it.
19
+ */
20
+ export interface Writers {
21
+ /** Who wrote the value a field held just before op `seq` ran; `undefined` when nobody this log knows. */
22
+ writerAt(id: string, field: string, seq: number): string | undefined;
23
+ }
24
+ /** Who wrote each field's value in a log, kept as the log goes (see `Writers`). */
25
+ export declare function writersOf(log: {
26
+ all(): readonly Operation[];
27
+ readonly length?: number;
28
+ }): Writers;
29
+ //# sourceMappingURL=writers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"writers.d.ts","sourceRoot":"","sources":["../../src/ops/writers.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAE5C;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,WAAW,OAAO;IACtB,yGAAyG;IACzG,QAAQ,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;CACtE;AAiFD,mFAAmF;AACnF,wBAAgB,SAAS,CAAC,GAAG,EAAE;IAAE,GAAG,IAAI,SAAS,SAAS,EAAE,CAAC;IAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAoBjG"}
@@ -0,0 +1,98 @@
1
+ import { isUnset } from "../graph/primitives.js";
2
+ import { actingAs } from "../permissions/policy.js";
3
+ const keyOf = (id, field) => `${id}\u0000${field}`;
4
+ const INDEXES = new WeakMap();
5
+ function indexFrom(base) {
6
+ const fields = new Map();
7
+ const writerAt = (id, field, seq) => {
8
+ const entries = fields.get(keyOf(id, field));
9
+ if (!entries)
10
+ return undefined;
11
+ let low = 0;
12
+ let high = entries.length;
13
+ while (low < high) {
14
+ const middle = (low + high) >> 1;
15
+ if (entries[middle].seq < seq)
16
+ low = middle + 1;
17
+ else
18
+ high = middle;
19
+ }
20
+ return low === 0 ? undefined : entries[low - 1].by;
21
+ };
22
+ return { base, end: base, read: [], fields, written: [], seqOf: new Map(), writers: { writerAt } };
23
+ }
24
+ /** The fields one primitive writes, by record. */
25
+ function writtenBy(primitive) {
26
+ if (primitive.op === "add-node")
27
+ return [primitive.node.id, Object.keys(primitive.node).filter((field) => field !== "id" && field !== "kind")];
28
+ if (primitive.op === "patch-node")
29
+ return [primitive.id, Object.keys(primitive.after).filter((field) => !isUnset(primitive.after[field]))];
30
+ return undefined;
31
+ }
32
+ function readInto(index, op) {
33
+ const seq = index.end;
34
+ const author = actingAs(op.author).id;
35
+ // What an undo puts back is the words of whoever wrote them before the op it takes back.
36
+ const undone = op.undoes !== undefined ? index.seqOf.get(op.undoes) : undefined;
37
+ const keys = [];
38
+ for (const primitive of op.primitives) {
39
+ const written = writtenBy(primitive);
40
+ if (!written)
41
+ continue;
42
+ const [id, fields] = written;
43
+ for (const field of fields) {
44
+ const by = op.undoes === undefined ? author : undone !== undefined ? index.writers.writerAt(id, field, undone) : undefined;
45
+ const key = keyOf(id, field);
46
+ let entries = index.fields.get(key);
47
+ if (!entries)
48
+ index.fields.set(key, (entries = []));
49
+ entries.push({ seq, by });
50
+ keys.push(key);
51
+ }
52
+ }
53
+ index.written.push({ seq, keys });
54
+ index.seqOf.set(op.id, seq);
55
+ index.read.push(op);
56
+ index.end++;
57
+ }
58
+ function cutBack(index, seq) {
59
+ while (index.written.length > 0 && index.written.at(-1).seq >= seq) {
60
+ const { keys } = index.written.pop();
61
+ for (const key of keys)
62
+ index.fields.get(key)?.pop();
63
+ }
64
+ for (const op of index.read.slice(seq - index.base))
65
+ index.seqOf.delete(op.id);
66
+ index.read.length = seq - index.base;
67
+ index.end = seq;
68
+ }
69
+ /** Who wrote each field's value in a log, kept as the log goes (see `Writers`). */
70
+ export function writersOf(log) {
71
+ const ops = log.all();
72
+ const end = typeof log.length === "number" ? log.length : ops.length;
73
+ const first = end - ops.length;
74
+ let index = INDEXES.get(log);
75
+ if (index && index.base <= first && first <= index.end) {
76
+ let agreed = Math.min(index.end, end);
77
+ while (agreed > first && ops[agreed - 1 - first] !== index.read[agreed - 1 - index.base])
78
+ agreed--;
79
+ if (agreed === first && first > index.base && agreed < index.end)
80
+ index = undefined;
81
+ else {
82
+ if (agreed < index.end)
83
+ cutBack(index, agreed);
84
+ if (first > index.base) {
85
+ index.read = index.read.slice(first - index.base);
86
+ index.base = first;
87
+ }
88
+ }
89
+ }
90
+ else
91
+ index = undefined;
92
+ if (!index)
93
+ INDEXES.set(log, (index = indexFrom(first)));
94
+ for (let seq = index.end; seq < end; seq++)
95
+ readInto(index, ops[seq - first]);
96
+ return index.writers;
97
+ }
98
+ //# sourceMappingURL=writers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"writers.js","sourceRoot":"","sources":["../../src/ops/writers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAkB,MAAM,wBAAwB,CAAC;AACjE,OAAO,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AA2CpD,MAAM,KAAK,GAAG,CAAC,EAAU,EAAE,KAAa,EAAU,EAAE,CAAC,GAAG,EAAE,SAAS,KAAK,EAAE,CAAC;AAE3E,MAAM,OAAO,GAAG,IAAI,OAAO,EAAiB,CAAC;AAE7C,SAAS,SAAS,CAAC,IAAY;IAC7B,MAAM,MAAM,GAAG,IAAI,GAAG,EAAmB,CAAC;IAC1C,MAAM,QAAQ,GAAG,CAAC,EAAU,EAAE,KAAa,EAAE,GAAW,EAAsB,EAAE;QAC9E,MAAM,OAAO,GAAG,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC,CAAC;QAC7C,IAAI,CAAC,OAAO;YAAE,OAAO,SAAS,CAAC;QAC/B,IAAI,GAAG,GAAG,CAAC,CAAC;QACZ,IAAI,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC;QAC1B,OAAO,GAAG,GAAG,IAAI,EAAE,CAAC;YAClB,MAAM,MAAM,GAAG,CAAC,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC;YACjC,IAAI,OAAO,CAAC,MAAM,CAAE,CAAC,GAAG,GAAG,GAAG;gBAAE,GAAG,GAAG,MAAM,GAAG,CAAC,CAAC;;gBAC5C,IAAI,GAAG,MAAM,CAAC;QACrB,CAAC;QACD,OAAO,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,GAAG,CAAC,CAAE,CAAC,EAAE,CAAC;IACtD,CAAC,CAAC;IACF,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,GAAG,EAAE,EAAE,OAAO,EAAE,EAAE,QAAQ,EAAE,EAAE,CAAC;AACrG,CAAC;AAED,kDAAkD;AAClD,SAAS,SAAS,CAAC,SAAoB;IACrC,IAAI,SAAS,CAAC,EAAE,KAAK,UAAU;QAAE,OAAO,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,EAAE,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,MAAM,CAAC,CAAC,CAAC;IAC/I,IAAI,SAAS,CAAC,EAAE,KAAK,YAAY;QAAE,OAAO,CAAC,SAAS,CAAC,EAAE,EAAE,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IAC3I,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,SAAS,QAAQ,CAAC,KAAY,EAAE,EAAa;IAC3C,MAAM,GAAG,GAAG,KAAK,CAAC,GAAG,CAAC;IACtB,MAAM,MAAM,GAAG,QAAQ,CAAC,EAAE,CAAC,MAAmB,CAAC,CAAC,EAAE,CAAC;IACnD,yFAAyF;IACzF,MAAM,MAAM,GAAG,EAAE,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IAChF,MAAM,IAAI,GAAa,EAAE,CAAC;IAC1B,KAAK,MAAM,SAAS,IAAI,EAAE,CAAC,UAAU,EAAE,CAAC;QACtC,MAAM,OAAO,GAAG,SAAS,CAAC,SAAS,CAAC,CAAC;QACrC,IAAI,CAAC,OAAO;YAAE,SAAS;QACvB,MAAM,CAAC,EAAE,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC;QAC7B,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;YAC3B,MAAM,EAAE,GAAG,EAAE,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;YAC3H,MAAM,GAAG,GAAG,KAAK,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;YAC7B,IAAI,OAAO,GAAG,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YACpC,IAAI,CAAC,OAAO;gBAAE,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,OAAO,GAAG,EAAE,CAAC,CAAC,CAAC;YACpD,OAAO,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,EAAE,EAAE,CAAC,CAAC;YAC1B,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACjB,CAAC;IACH,CAAC;IACD,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC;IAClC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC;IAC5B,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACpB,KAAK,CAAC,GAAG,EAAE,CAAC;AACd,CAAC;AAED,SAAS,OAAO,CAAC,KAAY,EAAE,GAAW;IACxC,OAAO,KAAK,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,IAAI,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC,CAAE,CAAC,GAAG,IAAI,GAAG,EAAE,CAAC;QACpE,MAAM,EAAE,IAAI,EAAE,GAAG,KAAK,CAAC,OAAO,CAAC,GAAG,EAAG,CAAC;QACtC,KAAK,MAAM,GAAG,IAAI,IAAI;YAAE,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,GAAG,EAAE,CAAC;IACvD,CAAC;IACD,KAAK,MAAM,EAAE,IAAI,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,GAAG,KAAK,CAAC,IAAI,CAAC;QAAE,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;IAC/E,KAAK,CAAC,IAAI,CAAC,MAAM,GAAG,GAAG,GAAG,KAAK,CAAC,IAAI,CAAC;IACrC,KAAK,CAAC,GAAG,GAAG,GAAG,CAAC;AAClB,CAAC;AAED,mFAAmF;AACnF,MAAM,UAAU,SAAS,CAAC,GAA8D;IACtF,MAAM,GAAG,GAAG,GAAG,CAAC,GAAG,EAAE,CAAC;IACtB,MAAM,GAAG,GAAG,OAAO,GAAG,CAAC,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC;IACrE,MAAM,KAAK,GAAG,GAAG,GAAG,GAAG,CAAC,MAAM,CAAC;IAC/B,IAAI,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAC7B,IAAI,KAAK,IAAI,KAAK,CAAC,IAAI,IAAI,KAAK,IAAI,KAAK,IAAI,KAAK,CAAC,GAAG,EAAE,CAAC;QACvD,IAAI,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;QACtC,OAAO,MAAM,GAAG,KAAK,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,GAAG,KAAK,CAAC,KAAK,KAAK,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,GAAG,KAAK,CAAC,IAAI,CAAC;YAAE,MAAM,EAAE,CAAC;QACnG,IAAI,MAAM,KAAK,KAAK,IAAI,KAAK,GAAG,KAAK,CAAC,IAAI,IAAI,MAAM,GAAG,KAAK,CAAC,GAAG;YAAE,KAAK,GAAG,SAAS,CAAC;aAC/E,CAAC;YACJ,IAAI,MAAM,GAAG,KAAK,CAAC,GAAG;gBAAE,OAAO,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;YAC/C,IAAI,KAAK,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;gBACvB,KAAK,CAAC,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC;gBAClD,KAAK,CAAC,IAAI,GAAG,KAAK,CAAC;YACrB,CAAC;QACH,CAAC;IACH,CAAC;;QAAM,KAAK,GAAG,SAAS,CAAC;IACzB,IAAI,CAAC,KAAK;QAAE,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,KAAK,GAAG,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IACzD,KAAK,IAAI,GAAG,GAAG,KAAK,CAAC,GAAG,EAAE,GAAG,GAAG,GAAG,EAAE,GAAG,EAAE;QAAE,QAAQ,CAAC,KAAK,EAAE,GAAG,CAAC,GAAG,GAAG,KAAK,CAAE,CAAC,CAAC;IAC/E,OAAO,KAAK,CAAC,OAAO,CAAC;AACvB,CAAC"}
@@ -1,3 +1,4 @@
1
+ import type { Epoch } from "../ops/log.js";
1
2
  import type { Operation } from "../ops/types.js";
2
3
  import type { Policy, Principal } from "./types.js";
3
4
  /** The edges a graph can be asked about, to say what is joined to whom. */
@@ -25,6 +26,8 @@ export interface RecordedLog {
25
26
  all(): readonly Operation[];
26
27
  /** The seq the next op takes, counting what a compaction archived; without it, the ops it holds. */
27
28
  readonly length?: number;
29
+ /** Its epochs, for a log compacted behind a checkpoint that names who made what behind it (`Epoch.creators`). */
30
+ epochs?(): readonly Epoch[];
28
31
  }
29
32
  /**
30
33
  * The records a log names, read from its add- and remove-node primitives.
@@ -38,12 +41,22 @@ export interface RecordedLog {
38
41
  * is taken out, back to where the log and the index still agree. A log
39
42
  * compacted behind its horizon (FR-23) keeps what the index had already
40
43
  * read of the archived ops, so a record made behind the horizon is still
41
- * known by its maker for as long as this log is open; a log OPENED
42
- * compacted never read them, and knows its makers from the horizon on.
44
+ * known by its maker for as long as this log is open. A log OPENED
45
+ * compacted never read them: it knows them from its checkpoint, which keeps
46
+ * who made each record behind it (`Epoch.creators`, written by
47
+ * `store.checkpoint`), and a checkpoint without that (one written before it
48
+ * existed) leaves it knowing its makers from the horizon on.
43
49
  * The `Records` returned is the same object every time, and answers as of
44
50
  * the last call.
45
51
  */
46
52
  export declare function recordsOf(log: RecordedLog): Records;
53
+ /**
54
+ * WHO MADE EACH RECORD BEFORE `seq`, as a checkpoint at `seq` keeps it
55
+ * (`Epoch.creators`): record id to the seat that first added it, in the
56
+ * order they were made — what the log's checkpoint already kept, then what
57
+ * the ops it holds before `seq` add.
58
+ */
59
+ export declare function creatorsBefore(log: RecordedLog, seq: number): Record<string, string>;
47
60
  /**
48
61
  * Whether a principal may see one record.
49
62
  *
@@ -1 +1 @@
1
- {"version":3,"file":"sight.d.ts","sourceRoot":"","sources":["../../src/permissions/sight.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAEjD,OAAO,KAAK,EAAE,MAAM,EAAE,SAAS,EAAS,MAAM,YAAY,CAAC;AAE3D,2EAA2E;AAC3E,UAAU,MAAM;IACd,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,SAAS;QAAE,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IACpD,EAAE,CAAC,EAAE,EAAE,MAAM,GAAG,SAAS;QAAE,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CACpD;AAKD,yDAAyD;AACzD,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,WAAW,CAAC,MAAM,CAAC,CAE5E;AAED;;;GAGG;AACH,MAAM,WAAW,OAAO;IACtB,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;IACvC,wFAAwF;IACxF,SAAS,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;CAC3C;AAED,0GAA0G;AAC1G,MAAM,WAAW,WAAW;IAC1B,GAAG,IAAI,SAAS,SAAS,EAAE,CAAC;IAC5B,oGAAoG;IACpG,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;CAC1B;AA0DD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,SAAS,CAAC,GAAG,EAAE,WAAW,GAAG,OAAO,CAwBnD;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,IAAI,CAClB,MAAM,EAAE,MAAM,GAAG,SAAS,EAC1B,SAAS,EAAE,SAAS,EACpB,IAAI,EAAE;IAAE,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,EACpD,KAAK,EAAE,MAAM;AACb,gFAAgF;AAChF,OAAO,CAAC,EAAE,IAAI,CAAC,OAAO,EAAE,WAAW,CAAC,GACnC,OAAO,CAmBT"}
1
+ {"version":3,"file":"sight.d.ts","sourceRoot":"","sources":["../../src/permissions/sight.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AAC3C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAEjD,OAAO,KAAK,EAAE,MAAM,EAAE,SAAS,EAAS,MAAM,YAAY,CAAC;AAE3D,2EAA2E;AAC3E,UAAU,MAAM;IACd,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,SAAS;QAAE,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IACpD,EAAE,CAAC,EAAE,EAAE,MAAM,GAAG,SAAS;QAAE,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CACpD;AAKD,yDAAyD;AACzD,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,WAAW,CAAC,MAAM,CAAC,CAE5E;AAED;;;GAGG;AACH,MAAM,WAAW,OAAO;IACtB,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;IACvC,wFAAwF;IACxF,SAAS,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;CAC3C;AAED,0GAA0G;AAC1G,MAAM,WAAW,WAAW;IAC1B,GAAG,IAAI,SAAS,SAAS,EAAE,CAAC;IAC5B,oGAAoG;IACpG,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,iHAAiH;IACjH,MAAM,CAAC,IAAI,SAAS,KAAK,EAAE,CAAC;CAC7B;AA2DD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,SAAS,CAAC,GAAG,EAAE,WAAW,GAAG,OAAO,CAwBnD;AAQD;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,WAAW,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAUpF;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,IAAI,CAClB,MAAM,EAAE,MAAM,GAAG,SAAS,EAC1B,SAAS,EAAE,SAAS,EACpB,IAAI,EAAE;IAAE,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,EACpD,KAAK,EAAE,MAAM;AACb,gFAAgF;AAChF,OAAO,CAAC,EAAE,IAAI,CAAC,OAAO,EAAE,WAAW,CAAC,GACnC,OAAO,CAmBT"}
@@ -5,9 +5,10 @@ export function sightedKinds(policy) {
5
5
  return new Set((policy?.sees ?? []).flatMap((sight) => sight.kinds));
6
6
  }
7
7
  const INDEXES = new WeakMap();
8
- function indexFrom(base) {
8
+ /** A fresh index for a log that begins at seq `base`, knowing the makers its checkpoint kept behind it. */
9
+ function indexFrom(base, behind) {
9
10
  const kinds = new Map();
10
- const creators = new Map();
11
+ const creators = new Map(Object.entries(behind ?? {}));
11
12
  return { base, end: base, read: [], kinds, creators, entries: [], records: { kindOf: (id) => kinds.get(id), creatorOf: (id) => creators.get(id) } };
12
13
  }
13
14
  /** Reads one op into the index: the kind of each record it first adds or removes, and who first made one. */
@@ -53,8 +54,11 @@ function cutBack(index, seq) {
53
54
  * is taken out, back to where the log and the index still agree. A log
54
55
  * compacted behind its horizon (FR-23) keeps what the index had already
55
56
  * read of the archived ops, so a record made behind the horizon is still
56
- * known by its maker for as long as this log is open; a log OPENED
57
- * compacted never read them, and knows its makers from the horizon on.
57
+ * known by its maker for as long as this log is open. A log OPENED
58
+ * compacted never read them: it knows them from its checkpoint, which keeps
59
+ * who made each record behind it (`Epoch.creators`, written by
60
+ * `store.checkpoint`), and a checkpoint without that (one written before it
61
+ * existed) leaves it knowing its makers from the horizon on.
58
62
  * The `Records` returned is the same object every time, and answers as of
59
63
  * the last call.
60
64
  */
@@ -85,11 +89,36 @@ export function recordsOf(log) {
85
89
  else
86
90
  index = undefined;
87
91
  if (!index)
88
- INDEXES.set(log, (index = indexFrom(first)));
92
+ INDEXES.set(log, (index = indexFrom(first, first > 0 ? checkpointOf(log, first)?.creators : undefined)));
89
93
  for (let seq = index.end; seq < end; seq++)
90
94
  readInto(index, ops[seq - first]);
91
95
  return index.records;
92
96
  }
97
+ /** The checkpoint a log compacted at `seq` begins from: the last epoch marked `horizon`, when it is there. */
98
+ function checkpointOf(log, seq) {
99
+ const checkpoint = [...(log.epochs?.() ?? [])].reverse().find((epoch) => epoch.horizon);
100
+ return checkpoint?.seq === seq ? checkpoint : undefined;
101
+ }
102
+ /**
103
+ * WHO MADE EACH RECORD BEFORE `seq`, as a checkpoint at `seq` keeps it
104
+ * (`Epoch.creators`): record id to the seat that first added it, in the
105
+ * order they were made — what the log's checkpoint already kept, then what
106
+ * the ops it holds before `seq` add.
107
+ */
108
+ export function creatorsBefore(log, seq) {
109
+ recordsOf(log);
110
+ const index = INDEXES.get(log);
111
+ const after = new Set();
112
+ for (let at = index.entries.length - 1; at >= 0 && index.entries[at].seq >= seq; at--) {
113
+ if (index.entries[at].creator)
114
+ after.add(index.entries[at].id);
115
+ }
116
+ const creators = {};
117
+ for (const [id, by] of index.creators)
118
+ if (!after.has(id))
119
+ creators[id] = by;
120
+ return creators;
121
+ }
93
122
  /**
94
123
  * Whether a principal may see one record.
95
124
  *