diff options
| author | nsfisis <nsfisis@gmail.com> | 2025-12-31 15:25:36 +0900 |
|---|---|---|
| committer | nsfisis <nsfisis@gmail.com> | 2025-12-31 15:25:36 +0900 |
| commit | a1383a9304ff457d6671e12ded4265b135256004 (patch) | |
| tree | 49eead4bfea09d0877daff611844d4b50bba4c1d /src/client/sync/push.ts | |
| parent | db60c5cc3e6dd2e51fce7dd946e477b3e125ba69 (diff) | |
| download | kioku-a1383a9304ff457d6671e12ded4265b135256004.tar.gz kioku-a1383a9304ff457d6671e12ded4265b135256004.tar.zst kioku-a1383a9304ff457d6671e12ded4265b135256004.zip | |
feat(crdt): add crdtChanges to sync push payload
Add CRDT document generation to the sync push flow. Each pending entity
is now converted to an Automerge CRDT document and included as base64-
encoded binary in the push payload alongside the existing entity data.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Diffstat (limited to 'src/client/sync/push.ts')
| -rw-r--r-- | src/client/sync/push.ts | 102 |
1 files changed, 102 insertions, 0 deletions
diff --git a/src/client/sync/push.ts b/src/client/sync/push.ts index b83136e..eea671b 100644 --- a/src/client/sync/push.ts +++ b/src/client/sync/push.ts @@ -7,6 +7,17 @@ import type { LocalNoteType, LocalReviewLog, } from "../db/index"; +import { + crdtCardRepository, + crdtDeckRepository, + crdtNoteFieldTypeRepository, + crdtNoteFieldValueRepository, + crdtNoteRepository, + crdtNoteTypeRepository, + crdtReviewLogRepository, +} from "./crdt"; +import type { CrdtSyncPayload } from "./crdt/sync-state"; +import { binaryToBase64 } from "./crdt/sync-state"; import type { PendingChanges, SyncQueue } from "./queue"; /** @@ -20,6 +31,8 @@ export interface SyncPushData { noteFieldTypes: SyncNoteFieldTypeData[]; notes: SyncNoteData[]; noteFieldValues: SyncNoteFieldValueData[]; + /** CRDT document changes for conflict-free sync */ + crdtChanges: CrdtSyncPayload[]; } export interface SyncDeckData { @@ -255,6 +268,94 @@ function noteFieldValueToSyncData( } /** + * Generate CRDT sync payloads from pending changes + */ +export function generateCrdtChanges( + changes: PendingChanges, +): CrdtSyncPayload[] { + const crdtChanges: CrdtSyncPayload[] = []; + + // Convert decks to CRDT documents + for (const deck of changes.decks) { + const result = crdtDeckRepository.toCrdtDocument(deck); + crdtChanges.push({ + documentId: result.documentId, + entityType: crdtDeckRepository.entityType, + entityId: deck.id, + binary: binaryToBase64(result.binary), + }); + } + + // Convert note types to CRDT documents + for (const noteType of changes.noteTypes) { + const result = crdtNoteTypeRepository.toCrdtDocument(noteType); + crdtChanges.push({ + documentId: result.documentId, + entityType: crdtNoteTypeRepository.entityType, + entityId: noteType.id, + binary: binaryToBase64(result.binary), + }); + } + + // Convert note field types to CRDT documents + for (const fieldType of changes.noteFieldTypes) { + const result = crdtNoteFieldTypeRepository.toCrdtDocument(fieldType); + crdtChanges.push({ + documentId: result.documentId, + entityType: crdtNoteFieldTypeRepository.entityType, + entityId: fieldType.id, + binary: binaryToBase64(result.binary), + }); + } + + // Convert notes to CRDT documents + for (const note of changes.notes) { + const result = crdtNoteRepository.toCrdtDocument(note); + crdtChanges.push({ + documentId: result.documentId, + entityType: crdtNoteRepository.entityType, + entityId: note.id, + binary: binaryToBase64(result.binary), + }); + } + + // Convert note field values to CRDT documents + for (const fieldValue of changes.noteFieldValues) { + const result = crdtNoteFieldValueRepository.toCrdtDocument(fieldValue); + crdtChanges.push({ + documentId: result.documentId, + entityType: crdtNoteFieldValueRepository.entityType, + entityId: fieldValue.id, + binary: binaryToBase64(result.binary), + }); + } + + // Convert cards to CRDT documents + for (const card of changes.cards) { + const result = crdtCardRepository.toCrdtDocument(card); + crdtChanges.push({ + documentId: result.documentId, + entityType: crdtCardRepository.entityType, + entityId: card.id, + binary: binaryToBase64(result.binary), + }); + } + + // Convert review logs to CRDT documents + for (const reviewLog of changes.reviewLogs) { + const result = crdtReviewLogRepository.toCrdtDocument(reviewLog); + crdtChanges.push({ + documentId: result.documentId, + entityType: crdtReviewLogRepository.entityType, + entityId: reviewLog.id, + binary: binaryToBase64(result.binary), + }); + } + + return crdtChanges; +} + +/** * Convert pending changes to sync push data format */ export function pendingChangesToPushData( @@ -268,6 +369,7 @@ export function pendingChangesToPushData( noteFieldTypes: changes.noteFieldTypes.map(noteFieldTypeToSyncData), notes: changes.notes.map(noteToSyncData), noteFieldValues: changes.noteFieldValues.map(noteFieldValueToSyncData), + crdtChanges: generateCrdtChanges(changes), }; } |
