@mengine/medeo-client 2.0.0 → 2.0.1-alpha.0

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
@@ -8,6 +8,18 @@ This package owns the shared `VideoDocument` contract, semantic editing layer, a
8
8
 
9
9
  `getState().loaded` means the first local load completed. Once loaded, `loadedFromLocal` reports whether that load imported a snapshot, including a valid empty document. It stays unchanged by later edits and remote updates. Document readiness does not depend on metadata or nonempty tracks.
10
10
 
11
+ ## Local persistence
12
+
13
+ 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.
14
+
15
+ `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.
16
+
17
+ `waitForLocalSave({ timeoutMs? })` waits for the commits present at invocation (default deadline: 15 seconds). Later edits do not extend that target. Timing out only stops the wait; persistence continues. This does not wait for server acknowledgement.
18
+
19
+ 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.
20
+
21
+ 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.
22
+
11
23
  ## Session undo/redo
12
24
 
13
25
  After `await session.start()`, `session.undo(options?)` and `session.redo(options?)` synchronously apply local history and return whether they produced a document update. `options` accepts the same actor/intent metadata as semantic edits. Read the resulting document through `session.snapshot()`; use `waitForServerAck()` only when durable confirmation is required.
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { $ as VideoDraft, A as assertValidVideoDocument, B as CaptionPart, C as index_d_exports, D as VideoDocumentMirrorSchema, E as VideoDocumentDraft, F as buildSpeechHostMap, G as Timeline, H as PartKind, I as derivePositionFromAbs, J as TrackItemTimePosition, K as Track, L as fromVideoDocument, M as buildInitialVideoDocument, N as DerivedItemPosition, O as videoDocumentMirrorSchema, P as SpeechHostMap, Q as VideoDocumentValidationIssueCode, R as toVideoDocument, S as ValidationError, T as TrackItemDraft, U as PartUnion, V as DEFAULT_UNIT_TIME_MS, W as SpeechPart, X as VideoDocument, Y as VideoClipPart, Z as VideoDocumentValidationIssue, _ as PlannedSemanticOpKind, a as MirrorVideoDocumentOptions, at as PartAggregation, b as SchemaValidator, c as CommitOptions, ct as Track$1, d as SemanticEditor, et as VideoDraftContent, f as SemanticOpInput, g as ImplementedSemanticOpKind, h as IMPLEMENTED_SEMANTIC_OP_KINDS, i as MirrorVideoDocumentAdapter, it as CaptionStyle, j as validateVideoDocument, k as VideoDocumentValidationError, l as OpActor, lt as TrackItem$1, m as TransactAudit, n as videoDocumentSchema, nt as Attachment, o as createMirrorVideoDocument, ot as SpeedShift, p as SemanticOpName, q as TrackItem, r as readVideoDocumentFromDraft, rt as CaptionPart$1, s as createMirrorVideoDocumentAdapter, st as Timeline$1, t as partUnionSchema, tt as VideoDraftPartUnion, u as SemanticDocumentAdapter, v as SemanticOpKind, w as TrackDraft, x as SnapshotReadable, y as isImplementedSemanticOpKind, z as BgmPart } from "./index-CnZ9l3rb.js";
2
2
  import { LoroDoc, PeerID } 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
@@ -628,6 +628,11 @@ declare class MengineAckFailedError extends Error {
628
628
  readonly cause: Error | undefined;
629
629
  constructor(reason: 'timeout' | 'rejected' | 'failed', code: string | undefined, cause: Error | undefined, message: string);
630
630
  }
631
+ type MengineLocalSaveState = LocalSaveState;
632
+ interface WaitForLocalSaveOptions {
633
+ /** Only limits waiting, never cancels persistence. */
634
+ timeoutMs?: number;
635
+ }
631
636
  interface WaitForServerAckOptions {
632
637
  /** Deadline in ms. Rejects with reason `timeout` when it elapses. */
633
638
  timeoutMs?: number;
@@ -649,6 +654,9 @@ interface MengineDocSessionOptions {
649
654
  * Browsers should pass an `IndexedDBDocStorage` for refresh/cross-tab support.
650
655
  */
651
656
  localStorage?: DocStorageLike;
657
+ /** A freshly fetched remote snapshot to merge into storage before loading.
658
+ * Existing local changes are retained; the host owns any online startup gate. */
659
+ initialSnapshot?: Uint8Array;
652
660
  sseReconnectDelayMs?: number;
653
661
  }
654
662
  /**
@@ -685,6 +693,7 @@ declare class MengineDocSession {
685
693
  private started;
686
694
  private startTask;
687
695
  private destroyed;
696
+ private closeTask;
688
697
  private lifetime;
689
698
  private hasConnected;
690
699
  private initialVersionValue;
@@ -778,6 +787,15 @@ declare class MengineDocSession {
778
787
  */
779
788
  start(): Promise<VideoDocument>;
780
789
  private open;
790
+ getLocalSaveState(): MengineLocalSaveState;
791
+ /** Immediately reports local transaction state; independent of server confirmation. */
792
+ subscribeLocalSaveState(cb: (state: MengineLocalSaveState) => void): () => void;
793
+ /** Waits for commits present now to reach local storage. Does not wait for the network. */
794
+ waitForLocalSave(options?: WaitForLocalSaveOptions): Promise<void>;
795
+ /** Stop accepting edits and drain local persistence before releasing resources.
796
+ * A failed local write keeps retrying, even after the host has unmounted.
797
+ * Hosts should use this for normal teardown; destroy() is immediate cancellation. */
798
+ close(): Promise<void>;
781
799
  getSyncState(): MengineDocSyncState;
782
800
  private confirmationTarget;
783
801
  /** Immediately supplies current state; unsubscribe when the host changes docs. */
@@ -1115,4 +1133,4 @@ declare function hostForAbsMs(ranges: MainClipRange[], absMs: number): MainClipR
1115
1133
  */
1116
1134
  declare function relativePositionForAbs(ranges: MainClipRange[], absMs: number): TrackItemTimePosition;
1117
1135
  //#endregion
1118
- export { type Aggregation, type Attachment, type BgmPart, type CaptionPart, type CaptionStyle, type CommitOptions, DEFAULT_UNIT_TIME_MS, type DerivedItemPosition, type DocStorageLike, type DocVersionMark, IMPLEMENTED_SEMANTIC_OP_KINDS, type ImplementedSemanticOpKind, LANE_KINDS_IN_STACK_ORDER, type LaneKind, type MainClipRange, type MakeEmptyPart, ManualSyncDoc, type ManualSyncDocOptions, MedeoHttpDocStorage, type MedeoHttpDocStorageOptions, MemoryDocStorage, MengineAckFailedError, type MengineAuditEntry, type MengineAuditResponse, MengineDocSession, type MengineDocSessionOptions, type MengineDocSessionUpdateEvent, type MengineDocSyncState, type MengineDocumentVersion, type MengineEventStreamOptions, MengineHttpClient, type MengineHttpClientOptions, MengineHttpRequestError, MenginePushRejectedError, type MenginePushResponse, type MenginePushUpdateResponse, type MengineRejectedResponse, type MengineSnapshotResponse, type MengineSseUpdateEvent, type MengineSyncResponse, type MengineUndoState, type MengineUpdateMeta, MirrorVideoDocumentAdapter, type MirrorVideoDocumentOptions, type OpActor, type PartAggregation, type PartKind, type PartUnion, type PlannedSemanticOpKind, type PullFailureReason, type PullResult, type PushOutcome, type PushOutcomeKind, type PushResult, type PushResultKind, SchemaValidator, type SemanticDocumentAdapter, SemanticEditor, type SemanticOpInput, type SemanticOpKind, type SemanticOpName, type SnapshotReadable, type SolvedVideoDocument, type SpeechHostMap, type SpeechPart, type SpeedShift, TIMELINE_SKELETON_DURATION_MS, type Timeline, type TimelineDoc, type TimelineItem, type Track, type TrackDraft, type TrackItem, type TrackItemDraft, type TrackItemTimePosition, type TransactAudit, ValidationError, type VideoClipPart, type VideoDocument, type VideoDocumentDraft, type VideoDocumentMirrorSchema, VideoDocumentValidationError, type VideoDocumentValidationIssue, type VideoDocumentValidationIssueCode, type VideoDraft, type CaptionPart$1 as VideoDraftCaptionPart, type VideoDraftContent, type VideoDraftPartUnion, type Timeline$1 as VideoDraftTimeline, type Track$1 as VideoDraftTrack, type TrackItem$1 as VideoDraftTrackItem, type WaitForServerAckOptions, arrangeMainTrackSeamlessly, assertValidVideoDocument, base64ToBytes, buildInitialVideoDocument, buildSpeechHostMap, bytesToBase64, cascadeAfterVideoClipChanges, createMirrorVideoDocument, createMirrorVideoDocumentAdapter, decodeDocVersionMark, derivePositionFromAbs, encodeDocVersionMark, ensureLaneTrack, fillMainTrackTimeGaps, findLaneTrack, fromVideoDocument, generatePartId, getAt, hostForAbsMs, isEmptyVideoClip, isImplementedSemanticOpKind, isMap, laneTrackId, mainTrackRanges, partDurationMs, partUnionSchema, readMainTrackItems, readMengineEventStream, readPart, readPartDurationMs, readVideoDocumentFromDraft, reassignSpeechesToVideoClipsByTime, recalculateTimelineDuration, relativePositionForAbs, resolveAllSpeechOverlaps, resolveSpeechOverlapByShiftingVideos, safeDurationMs, index_d_exports as schemas, snapshotToPlain, solveVideoDocument, syncAggregatedClipsTimePosition, toVideoDocument, validateVideoDocument, videoDocumentMirrorSchema, videoDocumentSchema };
1136
+ export { type Aggregation, type Attachment, type BgmPart, type CaptionPart, type CaptionStyle, type CommitOptions, DEFAULT_UNIT_TIME_MS, type DerivedItemPosition, type DocStorageLike, type DocVersionMark, IMPLEMENTED_SEMANTIC_OP_KINDS, type ImplementedSemanticOpKind, LANE_KINDS_IN_STACK_ORDER, type LaneKind, type MainClipRange, type MakeEmptyPart, ManualSyncDoc, type ManualSyncDocOptions, MedeoHttpDocStorage, type MedeoHttpDocStorageOptions, MemoryDocStorage, MengineAckFailedError, type MengineAuditEntry, type MengineAuditResponse, MengineDocSession, type MengineDocSessionOptions, type MengineDocSessionUpdateEvent, type MengineDocSyncState, type MengineDocumentVersion, 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, MirrorVideoDocumentAdapter, type MirrorVideoDocumentOptions, type OpActor, type PartAggregation, type PartKind, type PartUnion, type PlannedSemanticOpKind, type PullFailureReason, type PullResult, type PushOutcome, type PushOutcomeKind, type PushResult, type PushResultKind, SchemaValidator, type SemanticDocumentAdapter, SemanticEditor, type SemanticOpInput, type SemanticOpKind, type SemanticOpName, type SnapshotReadable, type SolvedVideoDocument, type SpeechHostMap, type SpeechPart, type SpeedShift, TIMELINE_SKELETON_DURATION_MS, type Timeline, type TimelineDoc, type TimelineItem, type Track, type TrackDraft, type TrackItem, type TrackItemDraft, type TrackItemTimePosition, type TransactAudit, ValidationError, type VideoClipPart, type VideoDocument, type VideoDocumentDraft, type VideoDocumentMirrorSchema, VideoDocumentValidationError, type VideoDocumentValidationIssue, type VideoDocumentValidationIssueCode, type VideoDraft, type CaptionPart$1 as VideoDraftCaptionPart, type VideoDraftContent, type VideoDraftPartUnion, type Timeline$1 as VideoDraftTimeline, type Track$1 as VideoDraftTrack, type TrackItem$1 as VideoDraftTrackItem, type WaitForLocalSaveOptions, type WaitForServerAckOptions, arrangeMainTrackSeamlessly, assertValidVideoDocument, base64ToBytes, buildInitialVideoDocument, buildSpeechHostMap, bytesToBase64, cascadeAfterVideoClipChanges, createMirrorVideoDocument, createMirrorVideoDocumentAdapter, decodeDocVersionMark, derivePositionFromAbs, encodeDocVersionMark, ensureLaneTrack, fillMainTrackTimeGaps, findLaneTrack, fromVideoDocument, generatePartId, getAt, hostForAbsMs, isEmptyVideoClip, isImplementedSemanticOpKind, isMap, laneTrackId, mainTrackRanges, partDurationMs, partUnionSchema, readMainTrackItems, readMengineEventStream, readPart, readPartDurationMs, readVideoDocumentFromDraft, reassignSpeechesToVideoClipsByTime, recalculateTimelineDuration, relativePositionForAbs, resolveAllSpeechOverlaps, resolveSpeechOverlapByShiftingVideos, safeDurationMs, index_d_exports as schemas, snapshotToPlain, solveVideoDocument, syncAggregatedClipsTimePosition, toVideoDocument, validateVideoDocument, videoDocumentMirrorSchema, videoDocumentSchema };
package/dist/index.js CHANGED
@@ -2705,6 +2705,7 @@ var MengineDocSession = class {
2705
2705
  started = false;
2706
2706
  startTask = null;
2707
2707
  destroyed = false;
2708
+ closeTask = null;
2708
2709
  lifetime = null;
2709
2710
  hasConnected = false;
2710
2711
  initialVersionValue = null;
@@ -2904,6 +2905,13 @@ var MengineDocSession = class {
2904
2905
  this.local.connection.connect();
2905
2906
  await this.local.connection.waitForConnected().abortOn(this.lifetime.signal);
2906
2907
  if (this.destroyed) throw new Error("mengine doc session destroyed");
2908
+ if (this.options.initialSnapshot) {
2909
+ await this.local.pushDocUpdate({
2910
+ docId: this.docId,
2911
+ data: this.options.initialSnapshot
2912
+ }, "initial-snapshot");
2913
+ if (this.destroyed) throw new Error("mengine doc session destroyed");
2914
+ }
2907
2915
  this.server.connection.connect();
2908
2916
  this.synchronizer.start();
2909
2917
  this.manager.start();
@@ -2936,6 +2944,35 @@ var MengineDocSession = class {
2936
2944
  this.emitSyncState();
2937
2945
  return this.snapshot();
2938
2946
  }
2947
+ getLocalSaveState() {
2948
+ return this.getState().localSave;
2949
+ }
2950
+ /** Immediately reports local transaction state; independent of server confirmation. */
2951
+ subscribeLocalSaveState(cb) {
2952
+ const off = this.events.on("state", () => cb(this.getLocalSaveState()));
2953
+ cb(this.getLocalSaveState());
2954
+ return off;
2955
+ }
2956
+ /** Waits for commits present now to reach local storage. Does not wait for the network. */
2957
+ async waitForLocalSave(options = {}) {
2958
+ if (!this.editorValue || this.destroyed) throw new Error("mengine doc session is not started");
2959
+ await this.manager.waitForLocalSave(this.docId).timeout(options.timeoutMs ?? 15e3);
2960
+ }
2961
+ /** Stop accepting edits and drain local persistence before releasing resources.
2962
+ * A failed local write keeps retrying, even after the host has unmounted.
2963
+ * Hosts should use this for normal teardown; destroy() is immediate cancellation. */
2964
+ close() {
2965
+ if (this.closeTask) return this.closeTask;
2966
+ if (this.destroyed || !this.editorValue) {
2967
+ this.destroy();
2968
+ return Promise.resolve();
2969
+ }
2970
+ this.mutationGuard.close();
2971
+ this.synchronizer.stop();
2972
+ this.server.connection.disconnect();
2973
+ this.closeTask = this.manager.waitForLocalSave(this.docId).then(() => this.destroy());
2974
+ return this.closeTask;
2975
+ }
2939
2976
  getSyncState() {
2940
2977
  const state = this.getState();
2941
2978
  const target = this.confirmationTarget();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mengine/medeo-client",
3
- "version": "2.0.0",
3
+ "version": "2.0.1-alpha.0",
4
4
  "license": "UNLICENSED",
5
5
  "repository": {
6
6
  "type": "git",
@@ -25,9 +25,9 @@
25
25
  "dependencies": {
26
26
  "loro-mirror": "^2.3.3",
27
27
  "zod": "^4.4.3",
28
- "@mengine/sync": "2.0.0",
29
- "@mengine/utils": "2.0.0",
30
- "@mengine/storage": "2.0.0"
28
+ "@mengine/storage": "2.0.1-alpha.0",
29
+ "@mengine/sync": "2.0.1-alpha.0",
30
+ "@mengine/utils": "2.0.1-alpha.0"
31
31
  },
32
32
  "devDependencies": {
33
33
  "@types/node": "^25.9.1",