[Init] Initial commit - NetMesh terminal manager
Some checks failed
build-packages / resolve bundled mosh-client (push) Has been cancelled
build-packages / resolve bundled et-client (push) Has been cancelled
build-packages / build-macos (push) Has been cancelled
build-packages / build-windows (push) Has been cancelled
build-packages / build-linux-x64 (push) Has been cancelled
build-packages / build-linux-arm64 (push) Has been cancelled
build-packages / release (push) Has been cancelled
build-packages / update Nix release metadata (push) Has been cancelled
build-packages / bump homebrew tap (push) Has been cancelled
test / lint-and-test (push) Has been cancelled
AI automation / Route event (push) Has been cancelled
AI automation / Hand reopened issue to maintainers (push) Has been cancelled
AI automation / Clean source issue state (push) Has been cancelled
AI automation / Reconcile handoffs (push) Has been cancelled
AI automation / Classify issue (push) Has been cancelled
AI automation / Claude Code smoke (push) Has been cancelled
AI automation / Review issue follow-up (push) Has been cancelled
AI automation / Publish issue follow-up (push) Has been cancelled
AI automation / Implement with Claude Code (push) Has been cancelled
AI automation / Publish implement PR (push) Has been cancelled
AI automation / Continue queued issue comments (push) Has been cancelled
AI automation / Codex review loop (push) Has been cancelled
AI automation / Publish Codex fix (push) Has been cancelled
AI automation / Clear Codex dispatch marker (push) Has been cancelled
AI automation / Own PR re-request Codex (push) Has been cancelled
AI automation / External PR re-request Codex (push) Has been cancelled
AI automation / Poll Codex reaction / retry (push) Has been cancelled
build-et-binaries / build-linux-x64 (push) Has been cancelled
build-et-binaries / build-linux-arm64 (push) Has been cancelled
build-et-binaries / build-macos-universal (push) Has been cancelled
build-et-binaries / build-windows-x64 (push) Has been cancelled
build-et-binaries / release (push) Has been cancelled
Some checks failed
build-packages / resolve bundled mosh-client (push) Has been cancelled
build-packages / resolve bundled et-client (push) Has been cancelled
build-packages / build-macos (push) Has been cancelled
build-packages / build-windows (push) Has been cancelled
build-packages / build-linux-x64 (push) Has been cancelled
build-packages / build-linux-arm64 (push) Has been cancelled
build-packages / release (push) Has been cancelled
build-packages / update Nix release metadata (push) Has been cancelled
build-packages / bump homebrew tap (push) Has been cancelled
test / lint-and-test (push) Has been cancelled
AI automation / Route event (push) Has been cancelled
AI automation / Hand reopened issue to maintainers (push) Has been cancelled
AI automation / Clean source issue state (push) Has been cancelled
AI automation / Reconcile handoffs (push) Has been cancelled
AI automation / Classify issue (push) Has been cancelled
AI automation / Claude Code smoke (push) Has been cancelled
AI automation / Review issue follow-up (push) Has been cancelled
AI automation / Publish issue follow-up (push) Has been cancelled
AI automation / Implement with Claude Code (push) Has been cancelled
AI automation / Publish implement PR (push) Has been cancelled
AI automation / Continue queued issue comments (push) Has been cancelled
AI automation / Codex review loop (push) Has been cancelled
AI automation / Publish Codex fix (push) Has been cancelled
AI automation / Clear Codex dispatch marker (push) Has been cancelled
AI automation / Own PR re-request Codex (push) Has been cancelled
AI automation / External PR re-request Codex (push) Has been cancelled
AI automation / Poll Codex reaction / retry (push) Has been cancelled
build-et-binaries / build-linux-x64 (push) Has been cancelled
build-et-binaries / build-linux-arm64 (push) Has been cancelled
build-et-binaries / build-macos-universal (push) Has been cancelled
build-et-binaries / build-windows-x64 (push) Has been cancelled
build-et-binaries / release (push) Has been cancelled
This commit is contained in:
80
domain/convergentSync/conflicts.ts
Normal file
80
domain/convergentSync/conflicts.ts
Normal file
@@ -0,0 +1,80 @@
|
||||
import { dotKey } from './clock';
|
||||
import { applyConvergentMutations } from './state';
|
||||
import type {
|
||||
ConvergentConflictAddress,
|
||||
ConvergentConflictCandidate,
|
||||
ConvergentFieldConflict,
|
||||
ConvergentSyncStateV2,
|
||||
JsonValue,
|
||||
RegisterAddress,
|
||||
} from './types';
|
||||
|
||||
export function convergentConflictAddressKey(address: ConvergentConflictAddress): string {
|
||||
switch (address.kind) {
|
||||
case 'entity-presence':
|
||||
case 'entity-position':
|
||||
return JSON.stringify([address.kind, address.collection, address.entityId]);
|
||||
case 'entity-field':
|
||||
return JSON.stringify([address.kind, address.collection, address.entityId, address.field]);
|
||||
case 'setting':
|
||||
return JSON.stringify([address.kind, ...address.path]);
|
||||
case 'setting-structure':
|
||||
return JSON.stringify([address.kind, ...address.paths]);
|
||||
case 'string-entry-presence':
|
||||
case 'string-entry-position':
|
||||
return JSON.stringify([address.kind, address.collection, address.value]);
|
||||
}
|
||||
}
|
||||
|
||||
function selectedAddress(
|
||||
conflict: ConvergentFieldConflict,
|
||||
candidate: ConvergentConflictCandidate,
|
||||
): RegisterAddress {
|
||||
if (conflict.address.kind !== 'setting-structure') return conflict.address;
|
||||
if (!candidate.settingPath?.length) {
|
||||
throw new Error('A setting-structure candidate must identify its setting path');
|
||||
}
|
||||
return { kind: 'setting', path: candidate.settingPath };
|
||||
}
|
||||
|
||||
export function resolveConvergentFieldConflict(
|
||||
state: ConvergentSyncStateV2,
|
||||
conflict: ConvergentFieldConflict,
|
||||
candidateDot: string,
|
||||
deviceId: string,
|
||||
now: number,
|
||||
): ConvergentSyncStateV2 {
|
||||
const candidate = conflict.candidates.find((entry) => dotKey(entry.dot) === candidateDot);
|
||||
if (!candidate) throw new Error('The selected convergent conflict candidate no longer exists');
|
||||
if (!candidate.tombstone && candidate.value === undefined) {
|
||||
throw new Error('The selected convergent conflict candidate has no value');
|
||||
}
|
||||
return applyConvergentMutations(state, deviceId, [{
|
||||
kind: 'resolve-register',
|
||||
address: selectedAddress(conflict, candidate),
|
||||
...(candidate.tombstone ? { tombstone: true } : { value: candidate.value }),
|
||||
}], now);
|
||||
}
|
||||
|
||||
const SECRET_SEGMENT = /(?:password|passphrase|privatekey|secret|token|api[_-]?key|access[_-]?key)/i;
|
||||
|
||||
function valueContainsSecretField(value: JsonValue | undefined): boolean {
|
||||
if (!value || typeof value !== 'object') return false;
|
||||
if (Array.isArray(value)) return value.some((nested) => valueContainsSecretField(nested));
|
||||
return Object.entries(value).some(([key, nested]) =>
|
||||
SECRET_SEGMENT.test(key) || valueContainsSecretField(nested));
|
||||
}
|
||||
|
||||
/** Values from secret-bearing registers must never be rendered or logged. */
|
||||
export function isConvergentConflictSecret(conflict: ConvergentFieldConflict): boolean {
|
||||
const { address } = conflict;
|
||||
if (address.kind === 'entity-field' && SECRET_SEGMENT.test(address.field)) return true;
|
||||
if (address.kind === 'setting' && address.path.some((segment) => SECRET_SEGMENT.test(segment))) {
|
||||
return true;
|
||||
}
|
||||
if (
|
||||
address.kind === 'setting-structure'
|
||||
&& address.paths.some((path) => path.some((segment) => SECRET_SEGMENT.test(segment)))
|
||||
) return true;
|
||||
return conflict.candidates.some((candidate) => valueContainsSecretField(candidate.value));
|
||||
}
|
||||
Reference in New Issue
Block a user