104 lines
3.7 KiB
JavaScript
104 lines
3.7 KiB
JavaScript
export const unknownFieldClock = () => ({ kind: "unknown" });
|
|
export const revisionFieldClock = (revision) => {
|
|
if (typeof revision === "bigint") {
|
|
return { kind: "revision", revision };
|
|
}
|
|
if (typeof revision === "number" && Number.isInteger(revision) && revision >= 0) {
|
|
return { kind: "revision", revision: BigInt(revision) };
|
|
}
|
|
if (typeof revision === "string" && /^\d+$/.test(revision)) {
|
|
return { kind: "revision", revision: BigInt(revision) };
|
|
}
|
|
return unknownFieldClock();
|
|
};
|
|
export const fieldClockToRevisionString = (clock) => clock.kind === "revision" ? clock.revision.toString() : "";
|
|
export const compareFieldClocks = (left, right) => {
|
|
if (left.kind === "revision" && right.kind === "revision") {
|
|
if (left.revision < right.revision) {
|
|
return "older";
|
|
}
|
|
if (left.revision > right.revision) {
|
|
return "newer";
|
|
}
|
|
return "same";
|
|
}
|
|
if (left.kind === "unknown" && right.kind === "unknown") {
|
|
return "same";
|
|
}
|
|
return "unknown";
|
|
};
|
|
export const sameFieldClock = (left, right) => compareFieldClocks(left, right) === "same";
|
|
const shouldAcceptCanonical = (incoming, current) => {
|
|
const order = compareFieldClocks(incoming, current);
|
|
if (order === "older") {
|
|
return false;
|
|
}
|
|
if (order === "unknown" && current.kind !== "unknown") {
|
|
return false;
|
|
}
|
|
return true;
|
|
};
|
|
const latestPendingValue = (state) => state.pending.at(-1)?.value;
|
|
const recomputeVisible = (state) => {
|
|
const pendingValue = latestPendingValue(state);
|
|
return pendingValue === undefined ? state.canonical.value : pendingValue;
|
|
};
|
|
export const createRegisterFieldState = (initial) => ({
|
|
canonical: initial,
|
|
visible: initial.value,
|
|
pending: [],
|
|
nextPendingSequence: 1,
|
|
});
|
|
export const applyCanonicalFieldValue = (state, incoming, clientMutationId) => {
|
|
const visibleBefore = state.visible;
|
|
const pendingBefore = state.pending.length;
|
|
if (!shouldAcceptCanonical(incoming.clock, state.canonical.clock)) {
|
|
return {
|
|
accepted: false,
|
|
canonicalChanged: false,
|
|
pendingChanged: false,
|
|
visibleChanged: false,
|
|
};
|
|
}
|
|
const canonicalChanged = !Object.is(state.canonical.value, incoming.value) ||
|
|
!sameFieldClock(state.canonical.clock, incoming.clock);
|
|
state.canonical = incoming;
|
|
if (clientMutationId) {
|
|
state.pending = state.pending.filter((entry) => entry.clientMutationId !== clientMutationId);
|
|
}
|
|
state.visible = recomputeVisible(state);
|
|
return {
|
|
accepted: true,
|
|
canonicalChanged,
|
|
pendingChanged: state.pending.length !== pendingBefore,
|
|
visibleChanged: !Object.is(state.visible, visibleBefore),
|
|
};
|
|
};
|
|
export const beginLocalFieldWrite = (state, clientMutationId, value) => {
|
|
const visibleBefore = state.visible;
|
|
state.pending.push({
|
|
clientMutationId,
|
|
sequence: state.nextPendingSequence++,
|
|
value,
|
|
});
|
|
state.visible = value;
|
|
return {
|
|
accepted: true,
|
|
canonicalChanged: false,
|
|
pendingChanged: true,
|
|
visibleChanged: !Object.is(state.visible, visibleBefore),
|
|
};
|
|
};
|
|
export const failLocalFieldWrite = (state, clientMutationId) => {
|
|
const visibleBefore = state.visible;
|
|
const pendingBefore = state.pending.length;
|
|
state.pending = state.pending.filter((entry) => entry.clientMutationId !== clientMutationId);
|
|
state.visible = recomputeVisible(state);
|
|
return {
|
|
accepted: true,
|
|
canonicalChanged: false,
|
|
pendingChanged: state.pending.length !== pendingBefore,
|
|
visibleChanged: !Object.is(state.visible, visibleBefore),
|
|
};
|
|
};
|
|
//# sourceMappingURL=field-semantics.js.map
|