dsh-ferry 0.1.2 → 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.
- package/lib/state.js +8 -1
- package/lib/transfer.js +41 -1
- package/package.json +1 -1
package/lib/state.js
CHANGED
|
@@ -15,7 +15,14 @@ import { createHash } from 'node:crypto';
|
|
|
15
15
|
* move when a log is renumbered, so they are compared as "a reference exists"
|
|
16
16
|
* rather than by value.
|
|
17
17
|
*/
|
|
18
|
-
const SEQ_REFERENCE_KEYS = new Set([
|
|
18
|
+
const SEQ_REFERENCE_KEYS = new Set([
|
|
19
|
+
'headerSeq',
|
|
20
|
+
'sourceEventSeq',
|
|
21
|
+
'sourceEventSeqs',
|
|
22
|
+
'messageSeqs',
|
|
23
|
+
'shadowedSeqs',
|
|
24
|
+
'shadowedRange',
|
|
25
|
+
]);
|
|
19
26
|
|
|
20
27
|
/** Number of trailing events that make up a tail fingerprint. */
|
|
21
28
|
export const TAIL_EVENTS = 8;
|
package/lib/transfer.js
CHANGED
|
@@ -771,6 +771,8 @@ export function createTransfer({ ctx, config, rules, log = () => {} }) {
|
|
|
771
771
|
const seq = nextSeq + index;
|
|
772
772
|
return seq === event.seq ? event : renumberEvent(event, seq, delta, shared);
|
|
773
773
|
});
|
|
774
|
+
// A mismatch here would write a log the receiver can never open again.
|
|
775
|
+
assertTailReferences([...local.events, ...tail], nextSeq);
|
|
774
776
|
let handle;
|
|
775
777
|
try {
|
|
776
778
|
handle = await ctx.sessionPersistence.open(sessionId, 'write', { signal });
|
|
@@ -1014,6 +1016,44 @@ async function* once(buffer) {
|
|
|
1014
1016
|
if (buffer !== undefined && buffer.length > 0) yield buffer;
|
|
1015
1017
|
}
|
|
1016
1018
|
|
|
1019
|
+
/**
|
|
1020
|
+
* Check that the references inside a renumbered tail still resolve.
|
|
1021
|
+
*
|
|
1022
|
+
* The stored format validates a few of them on open (`headerSeq` must point at
|
|
1023
|
+
* a request header, `messageSeqs` at human messages). A field this plugin does
|
|
1024
|
+
* not know about cannot be checked, so the known ones are verified before the
|
|
1025
|
+
* write rather than after it.
|
|
1026
|
+
* @param {readonly object[]} events - the receiver's log with the tail appended.
|
|
1027
|
+
* @param {number} from - index of the first appended event.
|
|
1028
|
+
* @throws {HttpError} when a reference would not resolve on the receiver.
|
|
1029
|
+
*/
|
|
1030
|
+
function assertTailReferences(events, from) {
|
|
1031
|
+
for (let index = from; index < events.length; index += 1) {
|
|
1032
|
+
const data = events[index].data;
|
|
1033
|
+
if (data === null || typeof data !== 'object') continue;
|
|
1034
|
+
const headerSeq = data.headerSeq;
|
|
1035
|
+
if (Number.isSafeInteger(headerSeq) && events[headerSeq]?.type !== 'request/header') {
|
|
1036
|
+
throw new HttpError(
|
|
1037
|
+
409,
|
|
1038
|
+
'session-reference-mismatch',
|
|
1039
|
+
`Cannot renumber this session: event ${events[index].seq} references request header ${headerSeq}, which would not exist`,
|
|
1040
|
+
);
|
|
1041
|
+
}
|
|
1042
|
+
const messageSeqs = data.messageSeqs;
|
|
1043
|
+
if (Array.isArray(messageSeqs)) {
|
|
1044
|
+
for (const seq of messageSeqs) {
|
|
1045
|
+
if (events[seq]?.type !== 'user/message') {
|
|
1046
|
+
throw new HttpError(
|
|
1047
|
+
409,
|
|
1048
|
+
'session-reference-mismatch',
|
|
1049
|
+
`Cannot renumber this session: event ${events[index].seq} references message ${seq}, which would not be a user message`,
|
|
1050
|
+
);
|
|
1051
|
+
}
|
|
1052
|
+
}
|
|
1053
|
+
}
|
|
1054
|
+
}
|
|
1055
|
+
}
|
|
1056
|
+
|
|
1017
1057
|
/**
|
|
1018
1058
|
* Rewrite one event for a log that numbers the same content differently.
|
|
1019
1059
|
* @param {object} event - source event.
|
|
@@ -1046,7 +1086,7 @@ function shiftSeqRefs(value, delta, from) {
|
|
|
1046
1086
|
out[key] = item + delta;
|
|
1047
1087
|
continue;
|
|
1048
1088
|
}
|
|
1049
|
-
if (key === 'shadowedSeqs' && Array.isArray(item)) {
|
|
1089
|
+
if ((key === 'shadowedSeqs' || key === 'messageSeqs' || key === 'sourceEventSeqs') && Array.isArray(item)) {
|
|
1050
1090
|
out[key] = item.map((entry) => (Number.isSafeInteger(entry) && entry >= from ? entry + delta : entry));
|
|
1051
1091
|
continue;
|
|
1052
1092
|
}
|