@cello-protocol/daemon 0.0.132 → 0.0.134
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.
- package/dist/bin/cello-daemon.js +25 -0
- package/dist/bin/cello-daemon.js.map +1 -1
- package/dist/content-park.d.ts.map +1 -1
- package/dist/content-park.js +29 -11
- package/dist/content-park.js.map +1 -1
- package/dist/daemon.d.ts +11 -0
- package/dist/daemon.d.ts.map +1 -1
- package/dist/daemon.js +455 -17
- package/dist/daemon.js.map +1 -1
- package/dist/document-ack-inbound.d.ts +57 -0
- package/dist/document-ack-inbound.d.ts.map +1 -0
- package/dist/document-ack-inbound.js +174 -0
- package/dist/document-ack-inbound.js.map +1 -0
- package/dist/document-control-notifier.d.ts +61 -0
- package/dist/document-control-notifier.d.ts.map +1 -0
- package/dist/document-control-notifier.js +70 -0
- package/dist/document-control-notifier.js.map +1 -0
- package/dist/document-delivery-transport.d.ts +94 -0
- package/dist/document-delivery-transport.d.ts.map +1 -0
- package/dist/document-delivery-transport.js +179 -0
- package/dist/document-delivery-transport.js.map +1 -0
- package/dist/document-delivery.d.ts +181 -0
- package/dist/document-delivery.d.ts.map +1 -0
- package/dist/document-delivery.js +289 -0
- package/dist/document-delivery.js.map +1 -0
- package/dist/document-frame-router.d.ts +210 -0
- package/dist/document-frame-router.d.ts.map +1 -0
- package/dist/document-frame-router.js +396 -0
- package/dist/document-frame-router.js.map +1 -0
- package/dist/document-handlers.d.ts +47 -0
- package/dist/document-handlers.d.ts.map +1 -0
- package/dist/document-handlers.js +657 -0
- package/dist/document-handlers.js.map +1 -0
- package/dist/document-handshake.d.ts +156 -0
- package/dist/document-handshake.d.ts.map +1 -0
- package/dist/document-handshake.js +398 -0
- package/dist/document-handshake.js.map +1 -0
- package/dist/document-inbound.d.ts +91 -0
- package/dist/document-inbound.d.ts.map +1 -0
- package/dist/document-inbound.js +290 -0
- package/dist/document-inbound.js.map +1 -0
- package/dist/document-layer.d.ts +137 -0
- package/dist/document-layer.d.ts.map +1 -0
- package/dist/document-layer.js +255 -0
- package/dist/document-layer.js.map +1 -0
- package/dist/document-lifecycle.d.ts +125 -0
- package/dist/document-lifecycle.d.ts.map +1 -0
- package/dist/document-lifecycle.js +433 -0
- package/dist/document-lifecycle.js.map +1 -0
- package/dist/document-live-docs.d.ts +58 -0
- package/dist/document-live-docs.d.ts.map +1 -0
- package/dist/document-live-docs.js +126 -0
- package/dist/document-live-docs.js.map +1 -0
- package/dist/document-notify.d.ts +173 -0
- package/dist/document-notify.d.ts.map +1 -0
- package/dist/document-notify.js +438 -0
- package/dist/document-notify.js.map +1 -0
- package/dist/document-publish.d.ts +67 -0
- package/dist/document-publish.d.ts.map +1 -0
- package/dist/document-publish.js +149 -0
- package/dist/document-publish.js.map +1 -0
- package/dist/document-reachability.d.ts +42 -0
- package/dist/document-reachability.d.ts.map +1 -0
- package/dist/document-reachability.js +80 -0
- package/dist/document-reachability.js.map +1 -0
- package/dist/document-rejection.d.ts +240 -0
- package/dist/document-rejection.d.ts.map +1 -0
- package/dist/document-rejection.js +407 -0
- package/dist/document-rejection.js.map +1 -0
- package/dist/document-store.d.ts +154 -8
- package/dist/document-store.d.ts.map +1 -1
- package/dist/document-store.js +462 -4
- package/dist/document-store.js.map +1 -1
- package/dist/document-write-path.d.ts.map +1 -1
- package/dist/document-write-path.js +10 -43
- package/dist/document-write-path.js.map +1 -1
- package/dist/inbound-sessions.d.ts.map +1 -1
- package/dist/inbound-sessions.js +4 -0
- package/dist/inbound-sessions.js.map +1 -1
- package/dist/initiate-session-handler.d.ts +24 -1
- package/dist/initiate-session-handler.d.ts.map +1 -1
- package/dist/initiate-session-handler.js +35 -9
- package/dist/initiate-session-handler.js.map +1 -1
- package/dist/ipc-server.d.ts +11 -1
- package/dist/ipc-server.d.ts.map +1 -1
- package/dist/ipc-server.js +7 -1
- package/dist/ipc-server.js.map +1 -1
- package/dist/line-lcs.d.ts +51 -0
- package/dist/line-lcs.d.ts.map +1 -0
- package/dist/line-lcs.js +71 -0
- package/dist/line-lcs.js.map +1 -0
- package/dist/notification-handlers.d.ts.map +1 -1
- package/dist/notification-handlers.js +31 -14
- package/dist/notification-handlers.js.map +1 -1
- package/dist/outbound-sessions.d.ts +2 -0
- package/dist/outbound-sessions.d.ts.map +1 -1
- package/dist/outbound-sessions.js +11 -1
- package/dist/outbound-sessions.js.map +1 -1
- package/dist/session-content-handlers.d.ts.map +1 -1
- package/dist/session-content-handlers.js +3 -2
- package/dist/session-content-handlers.js.map +1 -1
- package/dist/session-node-manager.d.ts +26 -4
- package/dist/session-node-manager.d.ts.map +1 -1
- package/dist/session-node-manager.js +201 -15
- package/dist/session-node-manager.js.map +1 -1
- package/dist/session-read-handlers.js +1 -1
- package/dist/session-read-handlers.js.map +1 -1
- package/dist/types.d.ts +40 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/vocabulary.d.ts.map +1 -1
- package/dist/vocabulary.js +16 -0
- package/dist/vocabulary.js.map +1 -1
- package/dist/wire-content-hash.d.ts +27 -0
- package/dist/wire-content-hash.d.ts.map +1 -0
- package/dist/wire-content-hash.js +37 -0
- package/dist/wire-content-hash.js.map +1 -0
- package/package.json +5 -5
|
@@ -0,0 +1,407 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DOD-DOC-REJECT-1 — rejection and supersession (§3.2, §16.7-2).
|
|
3
|
+
*
|
|
4
|
+
* ── WHY SUPERSESSION, AND NOT "BOTH SIDES ROLL BACK" ──────────────────────────────────────────
|
|
5
|
+
*
|
|
6
|
+
* The naive protocol is: the receiver discards, the sender undoes locally, and both return to the
|
|
7
|
+
* pre-update state. It does not work, and the reason is a property of CRDTs rather than a bug.
|
|
8
|
+
*
|
|
9
|
+
* **Yjs undo adds INVERSES; it does not erase.** The rejected operations stay in the sender's
|
|
10
|
+
* document. So every later update the sender computes against the receiver's state vector
|
|
11
|
+
* re-transmits them, and the receiver — which refuses to hold them — can never integrate the
|
|
12
|
+
* legitimate work stacked causally on top, because Yjs will not apply operations whose
|
|
13
|
+
* predecessors are missing. A permanent causal gap, from a protocol that looked symmetric.
|
|
14
|
+
*
|
|
15
|
+
* The protocol that works (§3.2):
|
|
16
|
+
* 1. The receiver rejects with a REASON — a protocol message and its own `0x05` leaf, never a
|
|
17
|
+
* silent drop. The update goes to quarantine, held rather than discarded.
|
|
18
|
+
* 2. The sender rolls back locally, which emits inverses into its own log and leaves an
|
|
19
|
+
* auditable "wrote X, was rejected, undid X" trail.
|
|
20
|
+
* 3. The sender publishes a SUPERSEDING update against the receiver's state vector, which
|
|
21
|
+
* necessarily carries the rejected operations PLUS their inverses plus any new work.
|
|
22
|
+
* 4. The receiver validates the now-clean projected diff — the rejected content nets to zero —
|
|
23
|
+
* admits it, and clears the quarantine. Causality intact, both parties converge, and the
|
|
24
|
+
* rejected content survives only as inert tombstones.
|
|
25
|
+
*
|
|
26
|
+
* ── SCOPE ─────────────────────────────────────────────────────────────────────────────────────
|
|
27
|
+
*
|
|
28
|
+
* This proves the protocol against STORE-1's local envelope log. The CBOR wire encoding is
|
|
29
|
+
* DOD-DOC-ENVELOPE-1's job and the cross-daemon proof is DOD-DOC-E2E-REJECT-1's.
|
|
30
|
+
*/
|
|
31
|
+
import * as Y from "yjs";
|
|
32
|
+
import { buildDocumentRejectionTbs, documentRejectionHash, encodeDocumentRejection, DOCUMENT_REJECTION_VERSION, } from "@cello-protocol/protocol-types";
|
|
33
|
+
/**
|
|
34
|
+
* The document stalls on the THIRD rejected round: the original refusal, one superseding attempt,
|
|
35
|
+
* and no more (§16.7-2 "one retry, then freeze").
|
|
36
|
+
*
|
|
37
|
+
* Counted in rounds rather than retries because the previous spelling — a `REJECTION_RETRY_LIMIT`
|
|
38
|
+
* of 1 compared as `round > LIMIT + 1` — permitted two retries under a name that said one, which
|
|
39
|
+
* is the kind of thing the next edit gets wrong.
|
|
40
|
+
*
|
|
41
|
+
* Bounded at all because an unbounded retry loop between two daemons that disagree is not
|
|
42
|
+
* convergence, it is a hot loop neither operator can see. Stalling is the visible failure.
|
|
43
|
+
*/
|
|
44
|
+
export const MAX_REJECTED_ROUNDS = 3;
|
|
45
|
+
export class DocumentRejections {
|
|
46
|
+
#store;
|
|
47
|
+
#logger;
|
|
48
|
+
constructor(store, logger) {
|
|
49
|
+
this.#store = store;
|
|
50
|
+
this.#logger = logger;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Record a rejection: a `0x05` row referencing the rejected envelope, the quarantined bytes
|
|
54
|
+
* held, and a policy record carrying the reason.
|
|
55
|
+
*
|
|
56
|
+
* The envelope log is append-only, so this is a NEW ROW — nothing is edited or removed.
|
|
57
|
+
*
|
|
58
|
+
* ── HOW A REFUSAL IS ACTUALLY REALIZED, AND WHY NOT THE WAY §9 SAYS ─────────────────────────
|
|
59
|
+
*
|
|
60
|
+
* §9 phrases effectiveness as a replay-time set property: "an update leaf is effective iff no
|
|
61
|
+
* rejection leaf references it". Implemented literally that is unsound, and it was measured
|
|
62
|
+
* rather than argued. Sender publishes a base, then a refused update, then rolls back and
|
|
63
|
+
* supersedes. Replaying the log while SKIPPING the refused leaf gives:
|
|
64
|
+
*
|
|
65
|
+
* text "agreed base. " pendingStructs PRESENT pendingDs PRESENT
|
|
66
|
+
*
|
|
67
|
+
* The supersession is causally stacked on the refused operations — the rollback is a DELETION of
|
|
68
|
+
* those structs and the new work is positioned after them — so dropping them leaves everything
|
|
69
|
+
* later permanently pending. The document reads as complete and is silently missing the
|
|
70
|
+
* legitimate work. §16.7-5 already retired §9's "document-log order" phrasing; this retires its
|
|
71
|
+
* effectiveness phrasing on the same grounds.
|
|
72
|
+
*
|
|
73
|
+
* What is sound: the receiver NEVER WRITES the refused payload to its log. There is nothing to
|
|
74
|
+
* subtract at replay because it was never added, and the peer's supersession — computed against
|
|
75
|
+
* the RECEIVER's state vector per §3.2 step 3 — is self-contained. Measured on the same fixture:
|
|
76
|
+
*
|
|
77
|
+
* text "agreed base. clean text. " pendingStructs null pendingDs null converged true
|
|
78
|
+
*
|
|
79
|
+
* The refused bytes do travel again inside that supersession, carrying their own inverses, which
|
|
80
|
+
* is precisely "inverses, not erasure" (§3.2) — the content nets to zero and survives only as
|
|
81
|
+
* tombstones. The bytes we refused live in `document_quarantine`, and the chain bridges across
|
|
82
|
+
* the refused envelope so the peer's next link still resolves (see `verifyChainLinkage`).
|
|
83
|
+
*/
|
|
84
|
+
async reject(agentId, documentId, input) {
|
|
85
|
+
// THE SIGNED PREIMAGE (DOD-DOC-REJECT-2). Until it existed, this method took a `signature` from
|
|
86
|
+
// its caller with nothing defining what that signature was OVER — so it could only ever be
|
|
87
|
+
// filled dishonestly, which is what writing the composition-root signer proved. The rejection
|
|
88
|
+
// is now built, signed, and its LEAF HASH derived from the same preimage: the leaf commits to
|
|
89
|
+
// exactly the bytes whose signature was verified.
|
|
90
|
+
// DUPLICATE DETECTION MOVED UP FRONT. It used to fall out of the leaf hash colliding, because
|
|
91
|
+
// that hash was sha256(envelope + reason + nonce) and a caller reusing the nonce produced the
|
|
92
|
+
// same row. The real preimage binds the ROUND and the TIMESTAMP, so two rejections of the same
|
|
93
|
+
// envelope for the same reason now hash differently and would each advance the round — driving
|
|
94
|
+
// a document to `stalled` on retries that never happened. The check has to be on what makes a
|
|
95
|
+
// rejection the SAME rejection: this envelope, refused for this reason, by us.
|
|
96
|
+
const already = this.#store
|
|
97
|
+
.listQuarantined(agentId, documentId)
|
|
98
|
+
.find((q) => q.rejectedEnvelopeHash === input.rejectedEnvelopeHash && q.reason === input.reason);
|
|
99
|
+
if (already) {
|
|
100
|
+
this.#logger.warn("document.rejection.duplicate", {
|
|
101
|
+
documentId,
|
|
102
|
+
rejectedEnvelopeHash: input.rejectedEnvelopeHash,
|
|
103
|
+
reason: input.reason,
|
|
104
|
+
});
|
|
105
|
+
return {
|
|
106
|
+
stalled: this.#isStalled(agentId, documentId),
|
|
107
|
+
round: this.#round(agentId, documentId),
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
const round = this.#round(agentId, documentId) + 1;
|
|
111
|
+
const envelope = {
|
|
112
|
+
type: "document_rejection",
|
|
113
|
+
rejection_version: DOCUMENT_REJECTION_VERSION,
|
|
114
|
+
document_id: documentId,
|
|
115
|
+
rejected_envelope_hash: input.rejectedEnvelopeHash,
|
|
116
|
+
rejecting_agent_id: agentId,
|
|
117
|
+
reason: input.reason,
|
|
118
|
+
...(input.detail === undefined ? {} : { detail: input.detail }),
|
|
119
|
+
round,
|
|
120
|
+
rejected_at_ms: input.nowMs,
|
|
121
|
+
signature: new Uint8Array(0),
|
|
122
|
+
};
|
|
123
|
+
envelope.signature = await input.sign(buildDocumentRejectionTbs(envelope));
|
|
124
|
+
// CHAIN IT. Writing `docPrevHash: null` made every rejection a second GENESIS row for this
|
|
125
|
+
// agent, so two rejections forked the chain — and the retry protocol guarantees at least two
|
|
126
|
+
// before a stall. `verifyChainLinkage` then refuses, and since `rebuildSnapshot` is how a
|
|
127
|
+
// document survives a restart, the document rebuilt until the next daemon start and was
|
|
128
|
+
// permanently unopenable after it. Measured. A rejection is this agent's own authored act, so
|
|
129
|
+
// it belongs in this agent's own chain.
|
|
130
|
+
const rejectionEnvelopeHash = documentRejectionHash(envelope);
|
|
131
|
+
const appended = this.#store.appendEnvelope(agentId, {
|
|
132
|
+
envelopeHash: rejectionEnvelopeHash,
|
|
133
|
+
documentId,
|
|
134
|
+
senderAgentId: agentId, // WE authored the rejection, whoever authored the update
|
|
135
|
+
docPrevHash: this.#store.lastEnvelopeHashBySender(agentId, documentId, agentId),
|
|
136
|
+
epochId: 0,
|
|
137
|
+
signature: envelope.signature,
|
|
138
|
+
// The rejection's own state vector — what THIS agent had seen when it refused. Empty is
|
|
139
|
+
// honest here: a rejection asserts nothing about document state, and REJECT-1's requirement
|
|
140
|
+
// was that the SIGNATURE be real, not that every field be populated for its own sake.
|
|
141
|
+
stateVector: new Uint8Array(0),
|
|
142
|
+
payload: null, // an audit record carries no content
|
|
143
|
+
kind: "rejection",
|
|
144
|
+
referencesEnvelopeHash: input.rejectedEnvelopeHash,
|
|
145
|
+
createdAtMs: input.nowMs,
|
|
146
|
+
});
|
|
147
|
+
if (!appended) {
|
|
148
|
+
// The store tells callers whether a row was written precisely so this is not inferred. A
|
|
149
|
+
// duplicate must not advance the retry round, or a document reaches `stalled` with fewer
|
|
150
|
+
// rejection leaves than rounds and an auditor replaying the log cannot see why.
|
|
151
|
+
this.#logger.warn("document.rejection.duplicate", {
|
|
152
|
+
documentId,
|
|
153
|
+
rejectedEnvelopeHash: input.rejectedEnvelopeHash,
|
|
154
|
+
reason: input.reason,
|
|
155
|
+
});
|
|
156
|
+
return { stalled: this.#isStalled(agentId, documentId), round: this.#round(agentId, documentId) };
|
|
157
|
+
}
|
|
158
|
+
// PERSISTED, not remembered. The 0x05 leaf references these bytes; holding them in a Map
|
|
159
|
+
// meant the reference outlived the thing it referenced across a restart.
|
|
160
|
+
const held = this.#store.holdQuarantined(agentId, {
|
|
161
|
+
documentId,
|
|
162
|
+
rejectionEnvelopeHash,
|
|
163
|
+
rejectedEnvelopeHash: input.rejectedEnvelopeHash,
|
|
164
|
+
// The refused envelope's own author and chain link, so `verifyChainLinkage` can bridge it —
|
|
165
|
+
// the peer chains its supersession onto an envelope our log deliberately never holds.
|
|
166
|
+
rejectedSenderAgentId: input.senderAgentId,
|
|
167
|
+
rejectedDocPrevHash: input.rejectedDocPrevHash,
|
|
168
|
+
// COPY: the caller's buffer may be a pooled network read, and a 0x05 leaf must reference
|
|
169
|
+
// the bytes that were actually refused.
|
|
170
|
+
payload: new Uint8Array(input.quarantined),
|
|
171
|
+
reason: input.reason,
|
|
172
|
+
detail: input.detail,
|
|
173
|
+
// The gate produces these deliberately; dropping them leaves an operator unable to see
|
|
174
|
+
// WHICH rule refused or WHAT number was exceeded.
|
|
175
|
+
rule: input.rule,
|
|
176
|
+
limitName: input.limit?.name,
|
|
177
|
+
limitValue: input.limit?.limit,
|
|
178
|
+
limitActual: input.limit?.actual,
|
|
179
|
+
createdAtMs: input.nowMs,
|
|
180
|
+
});
|
|
181
|
+
if (!held) {
|
|
182
|
+
// Cannot happen while the leaf hash is unique per rejection, which the append above already
|
|
183
|
+
// established. Logged rather than assumed: the previous key silently dropped every round
|
|
184
|
+
// after the first, and the way that stayed invisible was precisely that nothing said so.
|
|
185
|
+
this.#logger.error("document.rejection.quarantine_not_written", {
|
|
186
|
+
documentId,
|
|
187
|
+
rejectionEnvelopeHash,
|
|
188
|
+
rejectedEnvelopeHash: input.rejectedEnvelopeHash,
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
// The policy record — on BOTH sides, per §3.2. This is the sending half; the receiving half
|
|
192
|
+
// is written when a rejection arrives.
|
|
193
|
+
this.#logger.warn("document.rejection.sent", {
|
|
194
|
+
documentId,
|
|
195
|
+
senderAgentId: input.senderAgentId,
|
|
196
|
+
reason: input.reason,
|
|
197
|
+
detail: input.detail,
|
|
198
|
+
// Carried, not merely stored: an operator asking "why was this refused" needs the rule and
|
|
199
|
+
// the number, and a column no surface reads is a column that is not there.
|
|
200
|
+
rule: input.rule,
|
|
201
|
+
limitName: input.limit?.name,
|
|
202
|
+
limitValue: input.limit?.limit,
|
|
203
|
+
limitActual: input.limit?.actual,
|
|
204
|
+
round,
|
|
205
|
+
});
|
|
206
|
+
const stalled = round >= MAX_REJECTED_ROUNDS;
|
|
207
|
+
const alreadyStalled = this.#store.getDocument(agentId, documentId)?.status === "stalled";
|
|
208
|
+
if (stalled && !alreadyStalled) {
|
|
209
|
+
this.#store.setDocumentStatus(agentId, documentId, "stalled");
|
|
210
|
+
// Once, not on every subsequent refusal — a stalled document that keeps shouting is noise
|
|
211
|
+
// an operator learns to filter.
|
|
212
|
+
this.#logger.error("document.stalled", {
|
|
213
|
+
documentId,
|
|
214
|
+
reason: input.reason,
|
|
215
|
+
detail: input.detail,
|
|
216
|
+
rounds: round,
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
// The bytes go back to the caller so the peer can actually be told. See `RejectionOutcome.wire`.
|
|
220
|
+
return { stalled, round, wire: encodeDocumentRejection(envelope) };
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Record a rejection ARRIVING from the peer — the receiving half of §3.2's "both sides".
|
|
224
|
+
*
|
|
225
|
+
* ── THE ROUTING DECISION (the DoD requires it resolved in-unit) ─────────────────────────────
|
|
226
|
+
*
|
|
227
|
+
* A document rejection is written **daemon-side**, into this document's own log and quarantine,
|
|
228
|
+
* NOT through the gateway record store's `source` discriminator.
|
|
229
|
+
*
|
|
230
|
+
* The gateway's record store exists for SCREENING verdicts, and most V1 rejection reasons are
|
|
231
|
+
* not screening at all — `append_only`, the receiver-local limits, malformed updates, unresolved
|
|
232
|
+
* dependencies. Routing every document rejection through a screening store would file structural
|
|
233
|
+
* protocol events as policy verdicts, and it would couple this unit to a schema owned by a
|
|
234
|
+
* component that is not involved. DOD-DOC-SCREEN-1 is parked, so that coupling would also have
|
|
235
|
+
* to be built speculatively and unwound if screening lands differently.
|
|
236
|
+
*
|
|
237
|
+
* When SCREEN-1 does land, a rejection whose reason came from the screening rule can ADDITIONALLY
|
|
238
|
+
* write a gateway record — the discriminator exists for exactly that, and adding it later costs
|
|
239
|
+
* nothing, whereas removing a premature coupling costs a migration.
|
|
240
|
+
*/
|
|
241
|
+
recordIncomingRejection(agentId, documentId, input) {
|
|
242
|
+
// DURABLE, not a log line. Everything the PUBLISHING operator needs depends on this surviving
|
|
243
|
+
// a restart: why their work was refused, and how many rounds remain before the document
|
|
244
|
+
// stalls. It also has to exist for the retry bound to bind at all — the round was counted from
|
|
245
|
+
// rejections this agent AUTHORED, which on a pure publisher is zero forever, so the loop the
|
|
246
|
+
// limit exists to stop was the one side that had no limit.
|
|
247
|
+
const written = this.#store.recordRejectionReceived(agentId, {
|
|
248
|
+
documentId,
|
|
249
|
+
rejectionEnvelopeHash: input.rejectionEnvelopeHash,
|
|
250
|
+
rejectedEnvelopeHash: input.rejectedEnvelopeHash,
|
|
251
|
+
fromAgentId: input.fromAgentId,
|
|
252
|
+
reason: input.reason,
|
|
253
|
+
detail: input.detail,
|
|
254
|
+
createdAtMs: Date.now(),
|
|
255
|
+
});
|
|
256
|
+
const round = this.#store.countRejectionsReceived(agentId, documentId);
|
|
257
|
+
if (!written) {
|
|
258
|
+
this.#logger.warn("document.rejection.received_duplicate", {
|
|
259
|
+
documentId,
|
|
260
|
+
rejectionEnvelopeHash: input.rejectionEnvelopeHash,
|
|
261
|
+
round,
|
|
262
|
+
});
|
|
263
|
+
return { stalled: this.#isStalled(agentId, documentId), round };
|
|
264
|
+
}
|
|
265
|
+
this.#logger.warn("document.rejection.received", {
|
|
266
|
+
documentId,
|
|
267
|
+
fromAgentId: input.fromAgentId,
|
|
268
|
+
rejectedEnvelopeHash: input.rejectedEnvelopeHash,
|
|
269
|
+
reason: input.reason,
|
|
270
|
+
detail: input.detail,
|
|
271
|
+
round,
|
|
272
|
+
});
|
|
273
|
+
// The publisher stops superseding on the same threshold the receiver stops accepting on. Two
|
|
274
|
+
// sides of one bound: without this, the receiver freezes and the sender keeps retrying into a
|
|
275
|
+
// document that will never take it.
|
|
276
|
+
const stalled = round >= MAX_REJECTED_ROUNDS;
|
|
277
|
+
if (stalled && !this.#isStalled(agentId, documentId)) {
|
|
278
|
+
this.#store.setDocumentStatus(agentId, documentId, "stalled");
|
|
279
|
+
this.#logger.error("document.stalled", {
|
|
280
|
+
documentId,
|
|
281
|
+
reason: input.reason,
|
|
282
|
+
detail: input.detail,
|
|
283
|
+
rounds: round,
|
|
284
|
+
side: "publisher",
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
return { stalled, round };
|
|
288
|
+
}
|
|
289
|
+
/** Entries held for this document — never admitted, never discarded (§3.2). From the store. */
|
|
290
|
+
quarantined(agentId, documentId) {
|
|
291
|
+
return this.#store.listQuarantined(agentId, documentId).map((r) => ({
|
|
292
|
+
rejectedEnvelopeHash: r.rejectedEnvelopeHash,
|
|
293
|
+
quarantined: r.payload,
|
|
294
|
+
reason: r.reason,
|
|
295
|
+
detail: r.detail,
|
|
296
|
+
rule: r.rule,
|
|
297
|
+
limitName: r.limitName,
|
|
298
|
+
limitValue: r.limitValue,
|
|
299
|
+
limitActual: r.limitActual,
|
|
300
|
+
}));
|
|
301
|
+
}
|
|
302
|
+
/** Clear one entry once its superseding update has been admitted (§3.2 step 4). */
|
|
303
|
+
clearQuarantine(agentId, documentId, rejectedEnvelopeHash) {
|
|
304
|
+
// Only announce an admission that actually happened — an event that fires on a no-op is a
|
|
305
|
+
// signal on the wrong case.
|
|
306
|
+
if (this.#store.releaseQuarantined(agentId, documentId, rejectedEnvelopeHash)) {
|
|
307
|
+
this.#logger.info("document.supersession.admitted", { documentId, rejectedEnvelopeHash });
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
/**
|
|
311
|
+
* Whether this document still accepts updates.
|
|
312
|
+
*
|
|
313
|
+
* A stalled document REFUSES, naming the reason it stalled — both operators need to see why,
|
|
314
|
+
* because a document that silently stops converging is the failure this exists to prevent.
|
|
315
|
+
*/
|
|
316
|
+
acceptsUpdates(agentId, documentId) {
|
|
317
|
+
// Read the STORE, not memory. The status was persisted while the flag that gated this check
|
|
318
|
+
// was not, so after a restart the document row said `stalled` while the daemon happily
|
|
319
|
+
// accepted updates on it — the system reporting two contradictory states at once.
|
|
320
|
+
if (this.#isStalled(agentId, documentId)) {
|
|
321
|
+
const held = this.#store.listQuarantined(agentId, documentId);
|
|
322
|
+
const last = held[held.length - 1];
|
|
323
|
+
return {
|
|
324
|
+
ok: false,
|
|
325
|
+
reason: "document_stalled",
|
|
326
|
+
detail: `this document stopped accepting updates after ` +
|
|
327
|
+
`${this.#round(agentId, documentId)} rejected rounds` +
|
|
328
|
+
(last ? `; the most recent reason was ${last.reason}` : ""),
|
|
329
|
+
};
|
|
330
|
+
}
|
|
331
|
+
return { ok: true };
|
|
332
|
+
}
|
|
333
|
+
/**
|
|
334
|
+
* Start tracking local edits so a rejection can be rolled back.
|
|
335
|
+
*
|
|
336
|
+
* Yjs's own UndoManager, deliberately: rolling back by hand would mean computing inverses, and
|
|
337
|
+
* the inverse of a CRDT operation is not something to hand-roll — the whole reason supersession
|
|
338
|
+
* works is that Yjs's undo produces operations that compose correctly with everything stacked
|
|
339
|
+
* on top of them.
|
|
340
|
+
*/
|
|
341
|
+
trackLocalEdits(doc) {
|
|
342
|
+
// The two roots this codebase's write path projects from, taken through their TYPED getters.
|
|
343
|
+
//
|
|
344
|
+
// Not the `doc.share` placeholders: `doc.get(name)` hands back the untyped AbstractType, and
|
|
345
|
+
// the typed getter later instantiates a DIFFERENT object — so the UndoManager would watch a
|
|
346
|
+
// root nobody edits and `undo()` would silently do nothing. Caught by the out-of-order guard
|
|
347
|
+
// below, which is the guard earning its keep on its first outing.
|
|
348
|
+
//
|
|
349
|
+
// Instantiating these two as Text and Map is not the guessed-type hazard GATE-1 measured:
|
|
350
|
+
// the write path DEFINES them as Text and Map. A document that puts content under any other
|
|
351
|
+
// root is not tracked here, and that is a real gap — recorded rather than papered over,
|
|
352
|
+
// because it belongs with whichever unit lets a document declare its own roots.
|
|
353
|
+
const undoManager = new Y.UndoManager([doc.getText("content"), doc.getMap("data")], {
|
|
354
|
+
captureTimeout: 0,
|
|
355
|
+
});
|
|
356
|
+
return {
|
|
357
|
+
doc,
|
|
358
|
+
undoManager,
|
|
359
|
+
depthAtTracking: undoManager.undoStack.length,
|
|
360
|
+
dispose: () => undoManager.destroy(),
|
|
361
|
+
};
|
|
362
|
+
}
|
|
363
|
+
/**
|
|
364
|
+
* Roll back the last tracked edit, as INVERSES.
|
|
365
|
+
*
|
|
366
|
+
* The sender's history grows rather than shrinking. That is not a limitation to work around: it
|
|
367
|
+
* is what leaves an auditable "wrote X, was rejected, undid X" trail, and what lets the
|
|
368
|
+
* superseding update carry the rejected operations plus their inverses so causality survives.
|
|
369
|
+
*
|
|
370
|
+
* **PRECONDITION: roll back before making further local edits.** Yjs's UndoManager undoes the
|
|
371
|
+
* most recent stack item, and it cannot undo one out of order — so a sender that keeps editing
|
|
372
|
+
* after a rejection arrives and then rolls back would undo the WRONG transaction. §3.2's
|
|
373
|
+
* ordering is steps 2 then 3 for exactly this reason: roll back, THEN do new work, THEN
|
|
374
|
+
* publish the supersession that carries all three. A caller that needs to interleave has a
|
|
375
|
+
* genuine design question, not a call-order detail, and it belongs in the unit that wires
|
|
376
|
+
* publish to the receive path.
|
|
377
|
+
*/
|
|
378
|
+
rollback(tracked) {
|
|
379
|
+
// REFUSE rather than undo the wrong thing. Measured, the unguarded version had three failure
|
|
380
|
+
// modes that all reported success: nothing tracked (silent no-op), an untracked root (silent
|
|
381
|
+
// no-op), and — the damaging one — a sender that kept editing after the rejection arrived,
|
|
382
|
+
// where undo removed the agent's LEGITIMATE later work and KEPT the refused content. The
|
|
383
|
+
// supersession then re-ships the refused bytes, is rejected again, and the document stalls
|
|
384
|
+
// with a reason pointing at the peer while the operator's writing is gone.
|
|
385
|
+
const depth = tracked.undoManager.undoStack.length;
|
|
386
|
+
if (depth !== tracked.depthAtTracking + 1) {
|
|
387
|
+
throw new Error(`document_rollback_out_of_order: ${depth - tracked.depthAtTracking} local edit(s) are ` +
|
|
388
|
+
`stacked since tracking began, and Yjs can only undo the most recent — rolling back now ` +
|
|
389
|
+
`would discard the wrong work. Roll back before making further local edits (§3.2 step 2).`);
|
|
390
|
+
}
|
|
391
|
+
if (tracked.undoManager.undo() === null) {
|
|
392
|
+
throw new Error("document_rollback_nothing_tracked: no tracked edit to roll back — the rejected update " +
|
|
393
|
+
"was not produced through this tracker");
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
/** Derived from the log, not remembered — so it survives a restart. */
|
|
397
|
+
#round(agentId, documentId) {
|
|
398
|
+
// Scoped to the rejections THIS agent authored. A mutual exchange puts both directions' 0x05
|
|
399
|
+
// leaves in one document log, so an unscoped count conflated them and a document stalled at
|
|
400
|
+
// half the intended rounds the moment the peer also rejected something.
|
|
401
|
+
return this.#store.countRejections(agentId, documentId, agentId);
|
|
402
|
+
}
|
|
403
|
+
#isStalled(agentId, documentId) {
|
|
404
|
+
return this.#store.getDocument(agentId, documentId)?.status === "stalled";
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
//# sourceMappingURL=document-rejection.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"document-rejection.js","sourceRoot":"","sources":["../src/document-rejection.ts"],"names":[],"mappings":"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"}
|
package/dist/document-store.d.ts
CHANGED
|
@@ -40,14 +40,17 @@ export type DocumentStatus = "active" | "closed" | "killed" | "stalled";
|
|
|
40
40
|
* to say "that one no longer counts" is to add a record saying so.
|
|
41
41
|
*/
|
|
42
42
|
export type DocumentEnvelopeKind = "update" | "withdrawal" | "rejection";
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
43
|
+
/**
|
|
44
|
+
* RE-EXPORTED, not redefined.
|
|
45
|
+
*
|
|
46
|
+
* This was a second, structurally-similar interface declared here, and that is a drift risk with
|
|
47
|
+
* teeth: `properties` is inside the proposal's SIGNED preimage and `seamViolation` reads it, so two
|
|
48
|
+
* definitions of what a property is means two answers to what is admissible — one enforced at the
|
|
49
|
+
* signature and one at the store. They agreed by coincidence, and the first field added to one of
|
|
50
|
+
* them would have ended that quietly.
|
|
51
|
+
*/
|
|
52
|
+
export type { DocumentProperties } from "@cello-protocol/protocol-types";
|
|
53
|
+
import type { DocumentProperties } from "@cello-protocol/protocol-types";
|
|
51
54
|
export interface DocumentRow {
|
|
52
55
|
documentId: string;
|
|
53
56
|
ownerAgentId: string;
|
|
@@ -77,8 +80,33 @@ export interface DocumentEnvelopeRow {
|
|
|
77
80
|
/** For a withdrawal or rejection: the envelope it concerns. */
|
|
78
81
|
referencesEnvelopeHash?: string | null;
|
|
79
82
|
createdAtMs: number;
|
|
83
|
+
/** The Yjs clientID this sender signed for — the gate's authorship binding. */
|
|
84
|
+
senderClientId?: number | null;
|
|
80
85
|
/** Position in this document's log. Assigned on append; absent until then. */
|
|
81
86
|
logIndex?: number;
|
|
87
|
+
/** DELIVERY bookkeeping, read back from the log. Absent on a row being appended. */
|
|
88
|
+
deliveredAtMs?: number | null;
|
|
89
|
+
ackedAtMs?: number | null;
|
|
90
|
+
attempts?: number;
|
|
91
|
+
nextAttemptAtMs?: number | null;
|
|
92
|
+
}
|
|
93
|
+
export interface QuarantineRow {
|
|
94
|
+
documentId: string;
|
|
95
|
+
/** The 0x05 leaf this row belongs to — the PK. One leaf, one row; see the table comment. */
|
|
96
|
+
rejectionEnvelopeHash: string;
|
|
97
|
+
rejectedEnvelopeHash: string;
|
|
98
|
+
/** The refused envelope's own author and chain link, so the chain verifier can bridge it. */
|
|
99
|
+
rejectedSenderAgentId: string;
|
|
100
|
+
rejectedDocPrevHash: string | null;
|
|
101
|
+
payload: Uint8Array;
|
|
102
|
+
reason: string;
|
|
103
|
+
detail?: string;
|
|
104
|
+
/** Which pluggable rule refused, when one did — the gate produces this and it must not be lost. */
|
|
105
|
+
rule?: string;
|
|
106
|
+
limitName?: string;
|
|
107
|
+
limitValue?: number;
|
|
108
|
+
limitActual?: number;
|
|
109
|
+
createdAtMs: number;
|
|
82
110
|
}
|
|
83
111
|
export interface DocumentSnapshot {
|
|
84
112
|
binary: Uint8Array;
|
|
@@ -122,9 +150,24 @@ export type ReplayFn = (envelopes: DocumentEnvelopeRow[]) => {
|
|
|
122
150
|
};
|
|
123
151
|
export declare class DocumentStore {
|
|
124
152
|
#private;
|
|
153
|
+
/**
|
|
154
|
+
* The underlying handle, for modules that own their OWN tables alongside this one
|
|
155
|
+
* (DocumentLifecycle). Deliberately not a licence to query this store's tables from outside —
|
|
156
|
+
* every one of them has a method here, and a second query path is a second set of rules about
|
|
157
|
+
* scoping that nobody remembers to keep in step.
|
|
158
|
+
*/
|
|
159
|
+
get rawDb(): DaemonDatabase;
|
|
125
160
|
constructor(db: DaemonDatabase, logger: Logger);
|
|
126
161
|
createDocument(row: DocumentRow): void;
|
|
127
162
|
getDocument(ownerAgentId: string, documentId: string): DocumentRow | null;
|
|
163
|
+
/**
|
|
164
|
+
* Does this daemon hold ANY document, under any owner?
|
|
165
|
+
*
|
|
166
|
+
* Deliberately unscoped, and that is the point: it is the only question that can tell a delivery
|
|
167
|
+
* sweep visiting zero agents apart from a daemon that simply has no documents. Without it the two
|
|
168
|
+
* look identical in the log, which is what let an owner-key mismatch hide.
|
|
169
|
+
*/
|
|
170
|
+
anyDocumentExists(): boolean;
|
|
128
171
|
listDocuments(ownerAgentId: string): DocumentRow[];
|
|
129
172
|
setDocumentStatus(ownerAgentId: string, documentId: string, status: DocumentStatus): void;
|
|
130
173
|
/**
|
|
@@ -165,6 +208,109 @@ export declare class DocumentStore {
|
|
|
165
208
|
* caller must not read `ok: true` as authenticity — that belongs to the engine.
|
|
166
209
|
*/
|
|
167
210
|
verifyChainLinkage(ownerAgentId: string, documentId: string): ChainVerdict;
|
|
211
|
+
/**
|
|
212
|
+
* Hold a refused update's bytes. Idempotent per REJECTION (per 0x05 leaf), not per refused
|
|
213
|
+
* envelope — one envelope may be refused across several retry rounds and each round's reason,
|
|
214
|
+
* rule and limit are distinct facts an operator needs.
|
|
215
|
+
*
|
|
216
|
+
* Returns whether a row was written, so a caller never has to infer it. The previous signature
|
|
217
|
+
* returned void and the conflict clause silently dropped every round after the first.
|
|
218
|
+
*/
|
|
219
|
+
holdQuarantined(ownerAgentId: string, row: QuarantineRow): boolean;
|
|
220
|
+
listQuarantined(ownerAgentId: string, documentId: string): QuarantineRow[];
|
|
221
|
+
/**
|
|
222
|
+
* Release every entry for a refused envelope once its superseding update has been admitted.
|
|
223
|
+
* Returns whether anything was released. Keyed by the REFUSED envelope rather than the rejection
|
|
224
|
+
* leaf, because admitting the supersession resolves all of that envelope's rounds at once.
|
|
225
|
+
*/
|
|
226
|
+
releaseQuarantined(ownerAgentId: string, documentId: string, rejectedEnvelopeHash: string): boolean;
|
|
227
|
+
/**
|
|
228
|
+
* Every envelope hash we hold from one sender. Hash-only on purpose: the caller needs a set for a
|
|
229
|
+
* membership test, and `getEnvelopeLog` selects `*` — including every payload BLOB — so building
|
|
230
|
+
* that set from it materialized the whole document's content on every inbound update.
|
|
231
|
+
*/
|
|
232
|
+
knownEnvelopeHashesBySender(ownerAgentId: string, documentId: string, senderAgentId: string): Set<string>;
|
|
233
|
+
/** The clientIDs one sender has signed for on this document. Also hash-free — see above. */
|
|
234
|
+
senderClientIdsFor(ownerAgentId: string, documentId: string, senderAgentId: string): number[];
|
|
235
|
+
/**
|
|
236
|
+
* The state vector the PEER last told us they had — from their most recent envelope.
|
|
237
|
+
*
|
|
238
|
+
* This is what a publish diffs against (§3.2 step 3, §7): an update computed against what they
|
|
239
|
+
* have already seen carries exactly the operations they lack. Diffing against OUR OWN snapshot
|
|
240
|
+
* instead publishes what we have not yet snapshotted, which is a different question with an
|
|
241
|
+
* answer that happens to look plausible.
|
|
242
|
+
*/
|
|
243
|
+
peerStateVector(ownerAgentId: string, documentId: string, peerAgentId: string): Uint8Array | null;
|
|
244
|
+
/**
|
|
245
|
+
* The state vector OUR last published envelope carried — what we had when we last spoke.
|
|
246
|
+
*
|
|
247
|
+
* Distinct from `peerStateVector`, and the distinction is the point: that one answers "what does
|
|
248
|
+
* the peer lack" (what to send), this one answers "has anything changed since I last published"
|
|
249
|
+
* (whether to send at all). Conflating them meant a republish with no edits still produced a
|
|
250
|
+
* non-empty update, because before the peer has ever published the diff is the whole document.
|
|
251
|
+
*/
|
|
252
|
+
lastPublishedStateVector(ownerAgentId: string, documentId: string, senderAgentId: string): Uint8Array | null;
|
|
253
|
+
/** The rejecting agent's most recent envelope for this document, for chain linkage. */
|
|
254
|
+
lastEnvelopeHashBySender(ownerAgentId: string, documentId: string, senderAgentId: string): string | null;
|
|
255
|
+
/**
|
|
256
|
+
* Envelopes this agent authored that the peer has not acknowledged, and whose next attempt is
|
|
257
|
+
* due. DERIVED — there is no queue. Survives a restart because the log does.
|
|
258
|
+
*
|
|
259
|
+
* Scoped to `senderAgentId = ownerAgentId`: an envelope we RECEIVED is not ours to deliver, and
|
|
260
|
+
* without the scope a receiver would helpfully re-send the sender's own updates back at it.
|
|
261
|
+
*/
|
|
262
|
+
pendingDeliveries(ownerAgentId: string, nowMs: number,
|
|
263
|
+
/**
|
|
264
|
+
* OUR OWN sender id on the wire — the author's pubkey hex (M14-D5).
|
|
265
|
+
*
|
|
266
|
+
* Kept as a separate parameter, and separately named, even though the daemon now scopes the
|
|
267
|
+
* store by that same pubkey hex so the two coincide. They are different FACTS: the owner key
|
|
268
|
+
* says whose local store this row is in, the sender id says who signed the envelope. An
|
|
269
|
+
* earlier version of the daemon scoped by agent name, and the mismatch returned nothing
|
|
270
|
+
* pending — every published update sitting in the log undelivered, with no error anywhere.
|
|
271
|
+
* Collapsing them into one argument makes that class of bug unrepresentable in the call and
|
|
272
|
+
* invisible in the query.
|
|
273
|
+
*/
|
|
274
|
+
senderAgentId?: string, limit?: number): DocumentEnvelopeRow[];
|
|
275
|
+
/** Mark an attempt: bumps the counter and schedules the next one. Returns the new count. */
|
|
276
|
+
recordDeliveryAttempt(ownerAgentId: string, documentId: string, envelopeHash: string, nextAttemptAtMs: number): number;
|
|
277
|
+
/** Record that the envelope left. Not an ack — the peer has not answered yet. */
|
|
278
|
+
markDelivered(ownerAgentId: string, documentId: string, envelopeHash: string, nowMs: number): boolean;
|
|
279
|
+
/** Record that the peer acknowledged. Idempotent — a redelivered ack must not move the clock. */
|
|
280
|
+
markAcked(ownerAgentId: string, documentId: string, envelopeHash: string, nowMs: number): boolean;
|
|
281
|
+
/** Record a rejection the PEER sent us. Returns whether a row was written (idempotent by leaf). */
|
|
282
|
+
recordRejectionReceived(ownerAgentId: string, row: {
|
|
283
|
+
documentId: string;
|
|
284
|
+
rejectionEnvelopeHash: string;
|
|
285
|
+
rejectedEnvelopeHash: string;
|
|
286
|
+
fromAgentId: string;
|
|
287
|
+
reason: string;
|
|
288
|
+
detail?: string;
|
|
289
|
+
createdAtMs: number;
|
|
290
|
+
}): boolean;
|
|
291
|
+
/** How many rejections this document has RECEIVED — the publishing side's retry round. */
|
|
292
|
+
countRejectionsReceived(ownerAgentId: string, documentId: string): number;
|
|
293
|
+
/**
|
|
294
|
+
* Was a rejection received for THIS envelope? Envelope-scoped on purpose: the document-scoped
|
|
295
|
+
* reader below answers a different question, and using it to decide whether one envelope was
|
|
296
|
+
* refused makes a rejection of any OTHER envelope in the document look like a rejection of this
|
|
297
|
+
* one.
|
|
298
|
+
*/
|
|
299
|
+
rejectionReceivedFor(ownerAgentId: string, documentId: string, envelopeHash: string): boolean;
|
|
300
|
+
/** The most recently received rejection, for the reason an operator is shown on a stall. */
|
|
301
|
+
latestRejectionReceived(ownerAgentId: string, documentId: string): {
|
|
302
|
+
reason: string;
|
|
303
|
+
detail?: string;
|
|
304
|
+
fromAgentId: string;
|
|
305
|
+
} | null;
|
|
306
|
+
/**
|
|
307
|
+
* How many rejection records this document carries, PER REJECTING AGENT — the retry round.
|
|
308
|
+
*
|
|
309
|
+
* Scoped by author, because a mutual exchange puts both directions' 0x05 leaves in one document
|
|
310
|
+
* log. Counting across senders conflated them, so two peers each rejecting once read as round 2
|
|
311
|
+
* and the document stalled at half the intended rounds.
|
|
312
|
+
*/
|
|
313
|
+
countRejections(ownerAgentId: string, documentId: string, rejectingAgentId: string): number;
|
|
168
314
|
getSnapshot(ownerAgentId: string, documentId: string): DocumentSnapshot | null;
|
|
169
315
|
/** REPLACES — a snapshot is a cache of the log, not a second log. */
|
|
170
316
|
putSnapshot(ownerAgentId: string, documentId: string, snapshot: DocumentSnapshot): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"document-store.d.ts","sourceRoot":"","sources":["../src/document-store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAEzC,mGAAmG;AACnG,MAAM,MAAM,cAAc,GAAG,QAAQ,GAAG,QAAQ,GAAG,QAAQ,GAAG,SAAS,CAAC;AAExE;;;;GAIG;AACH,MAAM,MAAM,oBAAoB,GAAG,QAAQ,GAAG,YAAY,GAAG,WAAW,CAAC;AAEzE,
|
|
1
|
+
{"version":3,"file":"document-store.d.ts","sourceRoot":"","sources":["../src/document-store.ts"],"names":[],"mappings":"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"}
|