@mengine/medeo-client 2.1.1-dsl.1 → 2.1.1-dsl.2

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/README.md CHANGED
@@ -23,6 +23,20 @@ session.destroy();
23
23
 
24
24
  For explicit synchronization, use `ManualSyncDoc.open({client, peerId})`, then `pull()`, `editor`, `push()` and `dispose()`. Both lifecycles use the same binary snapshot/update and version-coverage contracts. Neither silently upgrades old documents or overwrites an incompatible cache.
25
25
 
26
+ ## Local persistence
27
+
28
+ Browser hosts can inject `IndexedDBDocStorage` through `localStorage`. An optional `initialSnapshot` merges a freshly fetched server snapshot into that storage before DocManager loads it; it never replaces locally saved, unconfirmed edits. The host can require a successful online fetch before constructing the session.
29
+
30
+ `getLocalSaveState()` / `subscribeLocalSaveState()` report `{ status: 'saving' | 'saved' | 'failed', error }` for this session's local commits. This is separate from `getSyncState()` and server confirmation. The storage adapter determines durability: IndexedDB resolves only after its transaction commits; MemoryDocStorage remains in memory. Failed saves retain their causal order and retry without blocking other documents.
31
+
32
+ `waitForLocalSave({ timeoutMs? })` waits until the local save queue drains, including in-flight writes and edits made while waiting (default deadline: 15 seconds). Continuous editing may keep it pending. Timing out only stops the wait; persistence continues. This does not wait for server acknowledgement.
33
+
34
+ For normal teardown, use `await session.close()`. It immediately rejects new edits and stops synchronization, then keeps local persistence alive until pending commits are saved before releasing connections. Storage failures remain retryable while closing. `destroy()` is immediate cancellation for exceptional teardown, including failed startup; it can discard unpersisted in-memory work. Neither API can guarantee a save after the browser process is forcibly terminated. Only completed local saves survive reopening.
35
+
36
+ Edits received from another local-storage peer also remain unconfirmed until covered by an authoritative server version. Receiving a cross-tab notification is not a server acknowledgement.
37
+
38
+ Reopening uses the stored Loro history and the server's version vector to derive missing updates, without replaying semantic operations or their external side effects. Undo history remains limited to the current session.
39
+
26
40
  ## Editing and conversion
27
41
 
28
42
  - Track: create, reorder, hide and delete.
@@ -1,6 +1,6 @@
1
1
  import { M as DslDiagnostic, o as VideoDraftContent, p as CommitOptions } from "./video-draft-types-c_shaRyq.js";
2
2
  import { LoroDoc, PeerID, VersionVector } from "loro-crdt";
3
- import { DocState } from "@mengine/sync";
3
+ import { DocState, LocalSaveState } from "@mengine/sync";
4
4
  import { BaseDocStorage, Connection, DocDiff, DocPushReceipt, DocSnapshotRecord, DocStorage, DocStorageOptions, DocUpdate, DocUpdateRecord } from "@mengine/storage";
5
5
 
6
6
  //#region src/client/base64.d.ts
@@ -532,13 +532,18 @@ declare class MengineAckFailedError extends Error {
532
532
  readonly cause: Error | undefined;
533
533
  constructor(reason: 'timeout' | 'rejected' | 'failed', code: string | undefined, cause: Error | undefined, message: string);
534
534
  }
535
+ type MengineLocalSaveState = LocalSaveState;
536
+ interface WaitForLocalSaveOptions {
537
+ /** Only limits waiting, never cancels persistence. */
538
+ timeoutMs?: number;
539
+ }
535
540
  interface WaitForServerAckOptions {
536
541
  /** Deadline in ms. Rejects with reason `timeout` when it elapses. */
537
542
  timeoutMs?: number;
538
543
  }
539
544
  /** Document-level confirmation is independent of local operation completion. */
540
545
  interface MengineDocSyncState {
541
- /** Remote-only imports do not create a new local confirmation obligation. */
546
+ /** Server-originated imports do not create a new local confirmation obligation. */
542
547
  readonly confirmation: 'unknown' | 'pending' | 'confirmed';
543
548
  /** Transport/activity phase. Idle does not imply confirmation; failure details
544
549
  * remain available through push outcomes and confirmation wait errors. */
@@ -553,6 +558,9 @@ interface MengineDocSessionOptions {
553
558
  * Browsers should pass an `IndexedDBDocStorage` for refresh/cross-tab support.
554
559
  */
555
560
  localStorage?: DocStorageLike;
561
+ /** A freshly fetched remote snapshot to merge into storage before loading.
562
+ * Existing local changes are retained; the host owns any online startup gate. */
563
+ initialSnapshot?: Uint8Array;
556
564
  sseReconnectDelayMs?: number;
557
565
  /**
558
566
  * Keep remote synchronization read-only: pull snapshot/diffs and subscribe to
@@ -587,6 +595,7 @@ declare class DocumentSession<Snapshot, Editor, Document> {
587
595
  private started;
588
596
  private startTask;
589
597
  private destroyed;
598
+ private closeTask;
590
599
  private lifetime;
591
600
  private hasConnected;
592
601
  private initialVersionValue;
@@ -687,7 +696,17 @@ declare class DocumentSession<Snapshot, Editor, Document> {
687
696
  */
688
697
  start(): Promise<Snapshot>;
689
698
  private open;
699
+ getLocalSaveState(): MengineLocalSaveState;
700
+ /** Immediately reports local transaction state; independent of server confirmation. */
701
+ subscribeLocalSaveState(cb: (state: MengineLocalSaveState) => void): () => void;
702
+ /** Waits until all local saves finish, including edits made while waiting. Does not wait for the network. */
703
+ waitForLocalSave(options?: WaitForLocalSaveOptions): Promise<void>;
704
+ /** Stop accepting edits and drain local persistence before releasing resources.
705
+ * A failed local write keeps retrying, even after the host has unmounted.
706
+ * Hosts should use this for normal teardown; destroy() is immediate cancellation. */
707
+ close(): Promise<void>;
690
708
  getSyncState(): MengineDocSyncState;
709
+ private trackLocalVersion;
691
710
  private confirmationTarget;
692
711
  /** Immediately supplies current state; unsubscribe when the host changes docs. */
693
712
  subscribeSyncState(cb: (state: MengineDocSyncState) => void): () => void;
@@ -725,4 +744,4 @@ declare class MemoryDocStorage extends BaseDocStorage {
725
744
  private now;
726
745
  }
727
746
  //#endregion
728
- export { MengineAuditResponse as A, base64ToBytes as B, MengineEventStreamOptions as C, MengineHttpRequestError as D, MengineHttpClientOptions as E, MengineRejectedResponse as F, MengineSnapshotResponse as I, MengineSseUpdateEvent as L, MengineDraftResponse as M, MenginePushResponse as N, MenginePushRejectedError as O, MenginePushUpdateResponse as P, MengineSyncResponse as R, PushResultKind as S, MengineHttpClient as T, bytesToBase64 as V, encodeDocVersionMark as _, MengineDocSessionOptions as a, PullResult as b, DocStorageLike as c, MedeoHttpDocStorageOptions as d, PushOutcome as f, decodeDocVersionMark as g, ManualSyncTransport as h, MengineAckFailedError as i, MengineDocumentVersion as j, MengineAuditEntry as k, MengineUndoState as l, ManualSyncDocOptions as m, DocumentSession as n, MengineDocSyncState as o, PushOutcomeKind as p, DocumentSessionUpdateEvent as r, WaitForServerAckOptions as s, MemoryDocStorage as t, MedeoHttpDocStorage as u, DocVersionMark as v, readMengineEventStream as w, PushResult as x, PullFailureReason as y, MengineUpdateMeta as z };
747
+ export { MenginePushRejectedError as A, MengineSyncResponse as B, PushResult as C, MengineHttpClient as D, readMengineEventStream as E, MenginePushResponse as F, base64ToBytes as H, MenginePushUpdateResponse as I, MengineRejectedResponse as L, MengineAuditResponse as M, MengineDocumentVersion as N, MengineHttpClientOptions as O, MengineDraftResponse as P, MengineSnapshotResponse as R, PullResult as S, MengineEventStreamOptions as T, bytesToBase64 as U, MengineUpdateMeta as V, ManualSyncTransport as _, MengineDocSessionOptions as a, DocVersionMark as b, WaitForLocalSaveOptions as c, MengineUndoState as d, MedeoHttpDocStorage as f, ManualSyncDocOptions as g, PushOutcomeKind as h, MengineAckFailedError as i, MengineAuditEntry as j, MengineHttpRequestError as k, WaitForServerAckOptions as l, PushOutcome as m, DocumentSession as n, MengineDocSyncState as o, MedeoHttpDocStorageOptions as p, DocumentSessionUpdateEvent as r, MengineLocalSaveState as s, MemoryDocStorage as t, DocStorageLike as u, decodeDocVersionMark as v, PushResultKind as w, PullFailureReason as x, encodeDocVersionMark as y, MengineSseUpdateEvent as z };
@@ -1,6 +1,6 @@
1
1
  import { G as ResourceSystem, et as TrackRole, z as MedeoDslSnapshot } from "./video-draft-types-c_shaRyq.js";
2
2
  import { _ as MedeoDsl, f as DslEditor, i as VideoDraftProjection } from "./index-Cu7SdKN5.js";
3
- import { a as MengineDocSessionOptions, h as ManualSyncTransport, m as ManualSyncDocOptions, n as DocumentSession, r as DocumentSessionUpdateEvent } from "./index-B7fC7q7P.js";
3
+ import { _ as ManualSyncTransport, a as MengineDocSessionOptions, g as ManualSyncDocOptions, n as DocumentSession, r as DocumentSessionUpdateEvent } from "./index-D-3rdfUP.js";
4
4
 
5
5
  //#region src/manual-sync/manual-sync-doc.d.ts
6
6
  /** Explicit synchronization over the same DSL/editor used by the live Session. */
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { $ as Track, A as ContainNode, B as Media, C as Caption, D as CaptionText, E as CaptionStyle, F as EntityTable, G as ResourceSystem, H as RelationTable, I as EntityTables, J as TextEdit, K as RichTextSnapshot, L as EntityValues, M as DslDiagnostic, N as DslMutation, O as Clip, P as EntityMutation, Q as TimeAnchor, R as FieldPatch, S as AudioScript, T as CaptionSelection, U as RelationTables, V as RelationMutation, W as RelationValues, X as TextMutation, Y as TextMark, Z as TextRun, _ as defaultIdFactory, a as VideoDraftCaptionStyle, b as tupleHash, c as VideoDraftInput, d as VideoDraftTrack, et as TrackRole, f as VideoDraftTrackItem, g as EntityIdKind, h as DslIdFactory, i as VideoDraft, j as DeepReadonly, k as ContainMutation, l as VideoDraftPartUnion, m as OpActor, n as DEFAULT_UNIT_TIME_MS, o as VideoDraftContent, p as CommitOptions, q as TextDelta, r as VideoCreationSettings, s as VideoDraftContentPartUnion, t as CaptionDisplayCue, tt as Voice, u as VideoDraftTimeline, v as relationId, w as CaptionContent, x as Asset, y as resourceId, z as MedeoDslSnapshot } from "./video-draft-types-c_shaRyq.js";
2
2
  import { A as AddDslClip, C as MedeoDslDraft, D as captionContentCodec, E as InvalidAtomic, F as ReplaceDslClipsInput, I as ClipPlacement, M as ClipSource, N as DslClipUpdate, O as isInvalidAtomic, P as MoveDslClipsInput, S as documentFormat, T as medeoDslLoroShape, _ as MedeoDsl, a as toVideoDraft, b as UnsupportedDocumentFormatError, c as DslLayout, d as createInitialMedeoDsl, f as DslEditor, g as CreateMedeoDslOptions, h as VoiceoverResult, i as VideoDraftProjection, j as AddDslClipsInput, k as timeAnchorCodec, l as DslProjectionError, m as VoiceoverCaptionInput, n as ImportedVideoDraft, o as resolveDslLayout, p as ApplyVoiceoverInput, r as fromVideoDraft, s as DslClipLayout, t as FromVideoDraftOptions, u as DslTrackLayout, v as DslAudit, w as MedeoDslLoroState, x as assertDslDocument, y as DslTransaction } from "./index-Cu7SdKN5.js";
3
- import { A as MengineAuditResponse, B as base64ToBytes, C as MengineEventStreamOptions, D as MengineHttpRequestError, E as MengineHttpClientOptions, F as MengineRejectedResponse, I as MengineSnapshotResponse, L as MengineSseUpdateEvent, M as MengineDraftResponse, N as MenginePushResponse, O as MenginePushRejectedError, P as MenginePushUpdateResponse, R as MengineSyncResponse, S as PushResultKind, T as MengineHttpClient, V as bytesToBase64, _ as encodeDocVersionMark, a as MengineDocSessionOptions, b as PullResult, c as DocStorageLike, d as MedeoHttpDocStorageOptions, f as PushOutcome, g as decodeDocVersionMark, i as MengineAckFailedError, j as MengineDocumentVersion, k as MengineAuditEntry, l as MengineUndoState, m as ManualSyncDocOptions, o as MengineDocSyncState, p as PushOutcomeKind, s as WaitForServerAckOptions, t as MemoryDocStorage, u as MedeoHttpDocStorage, v as DocVersionMark, w as readMengineEventStream, x as PushResult, y as PullFailureReason, z as MengineUpdateMeta } from "./index-B7fC7q7P.js";
4
- import { n as MengineDocSessionUpdateEvent, r as ManualSyncDoc, t as MengineDocSession } from "./index-yifEtZSP.js";
5
- export { type AddDslClip, type AddDslClipsInput, type ApplyVoiceoverInput, type Asset, type AudioScript, type Caption, type CaptionContent, type CaptionDisplayCue, type CaptionSelection, type CaptionStyle, type CaptionText, type Clip, type ClipPlacement, type ClipSource, type CommitOptions, type ContainMutation, type ContainNode, type CreateMedeoDslOptions, DEFAULT_UNIT_TIME_MS, type DeepReadonly, type DocStorageLike, type DocVersionMark, type DslAudit, type DslClipLayout, type DslClipUpdate, type DslDiagnostic, DslEditor, type DslIdFactory, type DslLayout, type DslMutation, DslProjectionError, type DslTrackLayout, type DslTransaction, type EntityIdKind, type EntityMutation, type EntityTable, type EntityTables, type EntityValues, type FieldPatch, type FromVideoDraftOptions, type ImportedVideoDraft, type InvalidAtomic, ManualSyncDoc, type ManualSyncDocOptions, MedeoDsl, type MedeoDslDraft, type MedeoDslLoroState, type MedeoDslSnapshot, MedeoHttpDocStorage, type MedeoHttpDocStorageOptions, type Media, MemoryDocStorage, MengineAckFailedError, type MengineAuditEntry, type MengineAuditResponse, MengineDocSession, type MengineDocSessionOptions, type MengineDocSessionUpdateEvent, type MengineDocSyncState, type MengineDocumentVersion, type MengineDraftResponse, type MengineEventStreamOptions, MengineHttpClient, type MengineHttpClientOptions, MengineHttpRequestError, MenginePushRejectedError, type MenginePushResponse, type MenginePushUpdateResponse, type MengineRejectedResponse, type MengineSnapshotResponse, type MengineSseUpdateEvent, type MengineSyncResponse, type MengineUndoState, type MengineUpdateMeta, type MoveDslClipsInput, type OpActor, type PullFailureReason, type PullResult, type PushOutcome, type PushOutcomeKind, type PushResult, type PushResultKind, type RelationMutation, type RelationTable, type RelationTables, type RelationValues, type ReplaceDslClipsInput, type ResourceSystem, type RichTextSnapshot, type TextDelta, type TextEdit, type TextMark, type TextMutation, type TextRun, type TimeAnchor, type Track, type TrackRole, UnsupportedDocumentFormatError, type VideoCreationSettings, type VideoDraft, type VideoDraftCaptionStyle, type VideoDraftContent, type VideoDraftContentPartUnion, type VideoDraftInput, type VideoDraftPartUnion, type VideoDraftProjection, type VideoDraftTimeline, type VideoDraftTrack, type VideoDraftTrackItem, type Voice, type VoiceoverCaptionInput, type VoiceoverResult, type WaitForServerAckOptions, assertDslDocument, base64ToBytes, bytesToBase64, captionContentCodec, createInitialMedeoDsl, decodeDocVersionMark, defaultIdFactory, documentFormat, encodeDocVersionMark, fromVideoDraft, isInvalidAtomic, medeoDslLoroShape, readMengineEventStream, relationId, resolveDslLayout, resourceId, timeAnchorCodec, toVideoDraft, tupleHash };
3
+ import { A as MenginePushRejectedError, B as MengineSyncResponse, C as PushResult, D as MengineHttpClient, E as readMengineEventStream, F as MenginePushResponse, H as base64ToBytes, I as MenginePushUpdateResponse, L as MengineRejectedResponse, M as MengineAuditResponse, N as MengineDocumentVersion, O as MengineHttpClientOptions, P as MengineDraftResponse, R as MengineSnapshotResponse, S as PullResult, T as MengineEventStreamOptions, U as bytesToBase64, V as MengineUpdateMeta, a as MengineDocSessionOptions, b as DocVersionMark, c as WaitForLocalSaveOptions, d as MengineUndoState, f as MedeoHttpDocStorage, g as ManualSyncDocOptions, h as PushOutcomeKind, i as MengineAckFailedError, j as MengineAuditEntry, k as MengineHttpRequestError, l as WaitForServerAckOptions, m as PushOutcome, o as MengineDocSyncState, p as MedeoHttpDocStorageOptions, s as MengineLocalSaveState, t as MemoryDocStorage, u as DocStorageLike, v as decodeDocVersionMark, w as PushResultKind, x as PullFailureReason, y as encodeDocVersionMark, z as MengineSseUpdateEvent } from "./index-D-3rdfUP.js";
4
+ import { n as MengineDocSessionUpdateEvent, r as ManualSyncDoc, t as MengineDocSession } from "./index-D-6uebu4.js";
5
+ export { type AddDslClip, type AddDslClipsInput, type ApplyVoiceoverInput, type Asset, type AudioScript, type Caption, type CaptionContent, type CaptionDisplayCue, type CaptionSelection, type CaptionStyle, type CaptionText, type Clip, type ClipPlacement, type ClipSource, type CommitOptions, type ContainMutation, type ContainNode, type CreateMedeoDslOptions, DEFAULT_UNIT_TIME_MS, type DeepReadonly, type DocStorageLike, type DocVersionMark, type DslAudit, type DslClipLayout, type DslClipUpdate, type DslDiagnostic, DslEditor, type DslIdFactory, type DslLayout, type DslMutation, DslProjectionError, type DslTrackLayout, type DslTransaction, type EntityIdKind, type EntityMutation, type EntityTable, type EntityTables, type EntityValues, type FieldPatch, type FromVideoDraftOptions, type ImportedVideoDraft, type InvalidAtomic, ManualSyncDoc, type ManualSyncDocOptions, MedeoDsl, type MedeoDslDraft, type MedeoDslLoroState, type MedeoDslSnapshot, MedeoHttpDocStorage, type MedeoHttpDocStorageOptions, type Media, MemoryDocStorage, MengineAckFailedError, type MengineAuditEntry, type MengineAuditResponse, MengineDocSession, type MengineDocSessionOptions, type MengineDocSessionUpdateEvent, type MengineDocSyncState, type MengineDocumentVersion, type MengineDraftResponse, type MengineEventStreamOptions, MengineHttpClient, type MengineHttpClientOptions, MengineHttpRequestError, type MengineLocalSaveState, MenginePushRejectedError, type MenginePushResponse, type MenginePushUpdateResponse, type MengineRejectedResponse, type MengineSnapshotResponse, type MengineSseUpdateEvent, type MengineSyncResponse, type MengineUndoState, type MengineUpdateMeta, type MoveDslClipsInput, type OpActor, type PullFailureReason, type PullResult, type PushOutcome, type PushOutcomeKind, type PushResult, type PushResultKind, type RelationMutation, type RelationTable, type RelationTables, type RelationValues, type ReplaceDslClipsInput, type ResourceSystem, type RichTextSnapshot, type TextDelta, type TextEdit, type TextMark, type TextMutation, type TextRun, type TimeAnchor, type Track, type TrackRole, UnsupportedDocumentFormatError, type VideoCreationSettings, type VideoDraft, type VideoDraftCaptionStyle, type VideoDraftContent, type VideoDraftContentPartUnion, type VideoDraftInput, type VideoDraftPartUnion, type VideoDraftProjection, type VideoDraftTimeline, type VideoDraftTrack, type VideoDraftTrackItem, type Voice, type VoiceoverCaptionInput, type VoiceoverResult, type WaitForLocalSaveOptions, type WaitForServerAckOptions, assertDslDocument, base64ToBytes, bytesToBase64, captionContentCodec, createInitialMedeoDsl, decodeDocVersionMark, defaultIdFactory, documentFormat, encodeDocVersionMark, fromVideoDraft, isInvalidAtomic, medeoDslLoroShape, readMengineEventStream, relationId, resolveDslLayout, resourceId, timeAnchorCodec, toVideoDraft, tupleHash };
package/dist/index.js CHANGED
@@ -2,7 +2,7 @@ import { n as bytesToBase64, t as base64ToBytes } from "./base64-D-jd-dr5.js";
2
2
  import { a as covers, c as readMengineEventStream, d as MenginePushRejectedError, i as openManualDocument, l as MengineHttpClient, n as MedeoHttpDocStorage, o as decodeDocVersionMark, r as ManualSyncTransport, s as encodeDocVersionMark, t as MemoryDocStorage, u as MengineHttpRequestError } from "./storage-BW3tI_ER.js";
3
3
  import { a as documentFormat, c as isInvalidAtomic, ct as relationId, i as assertDslDocument, l as timeAnchorCodec, lt as resourceId, o as medeoDslLoroShape, r as UnsupportedDocumentFormatError, s as captionContentCodec, st as defaultIdFactory, t as DEFAULT_UNIT_TIME_MS, ut as tupleHash } from "./video-draft-types-TXRv3wS6.js";
4
4
  import { a as MedeoDsl, i as DslEditor, n as toVideoDraft, o as resolveDslLayout, r as createInitialMedeoDsl, s as DslProjectionError, t as fromVideoDraft } from "./dsl-EV6kCdr6.js";
5
- import { UndoManager, VersionVector } from "loro-crdt";
5
+ import { UndoManager, VersionVector, decodeImportBlobMeta } from "loro-crdt";
6
6
  import { ClientServerSynchronizer, DocManager } from "@mengine/sync";
7
7
  import { DisposableSet, EventBus } from "@mengine/utils";
8
8
  //#region src/manual-sync/manual-sync-doc.ts
@@ -222,6 +222,7 @@ var DocumentSession = class {
222
222
  started = false;
223
223
  startTask = null;
224
224
  destroyed = false;
225
+ closeTask = null;
225
226
  lifetime = null;
226
227
  hasConnected = false;
227
228
  initialVersionValue = null;
@@ -438,6 +439,13 @@ var DocumentSession = class {
438
439
  this.local.connection.connect();
439
440
  await this.local.connection.waitForConnected().abortOn(this.lifetime.signal);
440
441
  if (this.destroyed) throw new Error("mengine doc session destroyed");
442
+ if (this.options.initialSnapshot) {
443
+ await this.local.pushDocUpdate({
444
+ docId: this.docId,
445
+ data: this.options.initialSnapshot
446
+ }, "initial-snapshot");
447
+ if (this.destroyed) throw new Error("mengine doc session destroyed");
448
+ }
441
449
  this.server.connection.connect();
442
450
  this.synchronizer.start();
443
451
  this.manager.start();
@@ -446,6 +454,10 @@ var DocumentSession = class {
446
454
  const doc = this.manager.connectDoc(this.docId);
447
455
  this.disposables.add(() => this.manager.disconnectDoc(this.docId));
448
456
  if (this.options.peerId != null) doc.setPeerId(this.options.peerId);
457
+ this.disposables.add(this.manager.onLocalUpdateReceived(this.docId, (update) => {
458
+ this.trackLocalVersion(decodeImportBlobMeta(update, true).partialEndVersionVector);
459
+ this.emitSyncState();
460
+ }));
449
461
  this.disposables.add(this.manager.onDocStateChange(this.docId, (state) => {
450
462
  this.events.emit("state", state);
451
463
  this.emitSyncState();
@@ -455,7 +467,7 @@ var DocumentSession = class {
455
467
  this.adapterValue = this.model(doc, this.mutationGuard);
456
468
  this.disposables.add(this.adapterValue.subscribe((source) => {
457
469
  if (!this.adapterValue) return;
458
- if (source === "local") this.localVersionValue = doc.version();
470
+ if (source === "local") this.trackLocalVersion(doc.version());
459
471
  this.emitSyncState();
460
472
  this.events.emit("update", {
461
473
  source,
@@ -470,9 +482,39 @@ var DocumentSession = class {
470
482
  this.undoManagerValue = new DocumentUndoManager(doc, this.mutationGuard, () => this.events.emit("undoState"));
471
483
  this.editorValue = this.adapterValue.editor;
472
484
  this.initialVersionValue = doc.version();
485
+ if (this.localVersionValue) this.trackLocalVersion(this.initialVersionValue);
473
486
  this.emitSyncState();
474
487
  return this.snapshot();
475
488
  }
489
+ getLocalSaveState() {
490
+ return this.getState().localSave;
491
+ }
492
+ /** Immediately reports local transaction state; independent of server confirmation. */
493
+ subscribeLocalSaveState(cb) {
494
+ const off = this.events.on("state", () => cb(this.getLocalSaveState()));
495
+ cb(this.getLocalSaveState());
496
+ return off;
497
+ }
498
+ /** Waits until all local saves finish, including edits made while waiting. Does not wait for the network. */
499
+ async waitForLocalSave(options = {}) {
500
+ if (!this.editorValue || this.destroyed) throw new Error("mengine doc session is not started");
501
+ await this.manager.waitForLocalSave(this.docId).timeout(options.timeoutMs ?? 15e3);
502
+ }
503
+ /** Stop accepting edits and drain local persistence before releasing resources.
504
+ * A failed local write keeps retrying, even after the host has unmounted.
505
+ * Hosts should use this for normal teardown; destroy() is immediate cancellation. */
506
+ close() {
507
+ if (this.closeTask) return this.closeTask;
508
+ if (this.destroyed || !this.editorValue) {
509
+ this.destroy();
510
+ return Promise.resolve();
511
+ }
512
+ this.mutationGuard.close();
513
+ this.synchronizer.stop();
514
+ this.server.connection.disconnect();
515
+ this.closeTask = this.manager.waitForLocalSave(this.docId).then(() => this.destroy());
516
+ return this.closeTask;
517
+ }
476
518
  getSyncState() {
477
519
  const state = this.getState();
478
520
  const target = this.confirmationTarget();
@@ -483,6 +525,14 @@ var DocumentSession = class {
483
525
  phase: connection === "disconnected" ? "offline" : this.pushErrorValue != null || state.error != null ? "failed" : connection === "connecting" ? "connecting" : state.syncing ? "syncing" : "idle"
484
526
  };
485
527
  }
528
+ trackLocalVersion(version) {
529
+ const target = VersionVector.parseJSON(this.confirmationTarget()?.toJSON() ?? /* @__PURE__ */ new Map());
530
+ for (const [peer, counter] of version.toJSON()) if ((target.get(peer) ?? 0) < counter) target.setEnd({
531
+ peer,
532
+ counter
533
+ });
534
+ this.localVersionValue = target;
535
+ }
486
536
  confirmationTarget() {
487
537
  return this.localVersionValue ?? this.initialVersionValue;
488
538
  }
package/dist/legacy.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { i as VideoDraft, l as VideoDraftPartUnion, o as VideoDraftContent, p as CommitOptions, t as CaptionDisplayCue } from "./video-draft-types-c_shaRyq.js";
2
- import { A as MengineAuditResponse, B as base64ToBytes, C as MengineEventStreamOptions, D as MengineHttpRequestError, E as MengineHttpClientOptions, F as MengineRejectedResponse, I as MengineSnapshotResponse, L as MengineSseUpdateEvent, M as MengineDraftResponse, N as MenginePushResponse, O as MenginePushRejectedError, P as MenginePushUpdateResponse, R as MengineSyncResponse, S as PushResultKind, T as MengineHttpClient, V as bytesToBase64, _ as encodeDocVersionMark, a as MengineDocSessionOptions, b as PullResult, c as DocStorageLike, d as MedeoHttpDocStorageOptions, f as PushOutcome, g as decodeDocVersionMark, h as ManualSyncTransport, j as MengineDocumentVersion, k as MengineAuditEntry, l as MengineUndoState, m as ManualSyncDocOptions, o as MengineDocSyncState, p as PushOutcomeKind, s as WaitForServerAckOptions, t as MemoryDocStorage, u as MedeoHttpDocStorage, v as DocVersionMark, w as readMengineEventStream, x as PushResult, y as PullFailureReason, z as MengineUpdateMeta } from "./index-B7fC7q7P.js";
2
+ import { A as MenginePushRejectedError, B as MengineSyncResponse, C as PushResult, D as MengineHttpClient, E as readMengineEventStream, F as MenginePushResponse, H as base64ToBytes, I as MenginePushUpdateResponse, L as MengineRejectedResponse, M as MengineAuditResponse, N as MengineDocumentVersion, O as MengineHttpClientOptions, P as MengineDraftResponse, R as MengineSnapshotResponse, S as PullResult, T as MengineEventStreamOptions, U as bytesToBase64, V as MengineUpdateMeta, _ as ManualSyncTransport, a as MengineDocSessionOptions, b as DocVersionMark, d as MengineUndoState, f as MedeoHttpDocStorage, g as ManualSyncDocOptions, h as PushOutcomeKind, j as MengineAuditEntry, k as MengineHttpRequestError, l as WaitForServerAckOptions, m as PushOutcome, o as MengineDocSyncState, p as MedeoHttpDocStorageOptions, t as MemoryDocStorage, u as DocStorageLike, v as decodeDocVersionMark, w as PushResultKind, x as PullFailureReason, y as encodeDocVersionMark, z as MengineSseUpdateEvent } from "./index-D-3rdfUP.js";
3
3
  import { LoroDoc, PeerID } from "loro-crdt";
4
4
 
5
5
  //#region ../medeo-dsl/depreacted/sandbox.d.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mengine/medeo-client",
3
- "version": "2.1.1-dsl.1",
3
+ "version": "2.1.1-dsl.2",
4
4
  "license": "UNLICENSED",
5
5
  "repository": {
6
6
  "type": "git",
@@ -29,9 +29,9 @@
29
29
  "@noble/hashes": "^2.4.0",
30
30
  "immer": "^10.2.0",
31
31
  "zod": "^4.4.3",
32
- "@mengine/storage": "2.1.1-dsl.1",
33
- "@mengine/sync": "2.1.1-dsl.1",
34
- "@mengine/utils": "2.1.1-dsl.1"
32
+ "@mengine/storage": "2.1.1-dsl.2",
33
+ "@mengine/sync": "2.1.1-dsl.2",
34
+ "@mengine/utils": "2.1.1-dsl.2"
35
35
  },
36
36
  "devDependencies": {
37
37
  "@types/node": "^25.9.1",