@mengine/medeo-client 2.0.1-alpha.26 → 2.0.1-alpha.28

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.
@@ -2495,7 +2495,7 @@ const toAssetRelationSpec = Object.freeze({
2495
2495
  validateEndpoints: (endpoints) => hasExactlyOneAsset(endpoints),
2496
2496
  validateMetadata: isAssetBinding
2497
2497
  });
2498
- /** Media a generated medium can be made from, and a transcript can be read off. */
2498
+ /** Media lineage, speech inputs, transcription sources, and immutable script revisions. */
2499
2499
  const MEDIA_VARIANT_KINDS = [
2500
2500
  "video",
2501
2501
  "image",
@@ -2517,7 +2517,7 @@ const derivedFromRelationSpec = Object.freeze({
2517
2517
  kind: "derived-from",
2518
2518
  ordered: true,
2519
2519
  sequenced: false,
2520
- validateEndpoints: (endpoints) => hasKinds(endpoints, new Set(MEDIA_VARIANT_KINDS), new Set(MEDIA_VARIANT_KINDS)) || hasKinds(endpoints, new Set(["voiceover-recording"]), new Set(["phonetic-script"])) || hasKinds(endpoints, new Set(["audio-script"]), new Set(MEDIA_AUDIBLE_KINDS)),
2520
+ validateEndpoints: (endpoints) => hasKinds(endpoints, new Set(MEDIA_VARIANT_KINDS), new Set(MEDIA_VARIANT_KINDS)) || hasKinds(endpoints, new Set(["voiceover-recording"]), new Set(["phonetic-script"])) || hasKinds(endpoints, new Set(["audio-script"]), new Set(MEDIA_AUDIBLE_KINDS)) || hasKinds(endpoints, new Set(["audio-script"]), new Set(["audio-script"])),
2521
2521
  validateMetadata: isEmptyMetadata
2522
2522
  });
2523
2523
  const captionAlignmentRelationSpec = Object.freeze({
@@ -2887,6 +2887,42 @@ function errorMessage(error) {
2887
2887
  return error instanceof Error ? error.message : String(error);
2888
2888
  }
2889
2889
  //#endregion
2890
+ //#region ../medeo-dsl/src/relation-transition.ts
2891
+ var InvalidEntityRelationTransitionError = class extends Error {
2892
+ issues;
2893
+ constructor(issues) {
2894
+ super(`Invalid Medeo relation transition:\n- ${issues.join("\n- ")}`);
2895
+ this.issues = issues;
2896
+ this.name = "InvalidEntityRelationTransitionError";
2897
+ }
2898
+ };
2899
+ /** Enforce relation fact identity while admitting a complete candidate view. */
2900
+ function validateEntityRelationTransition(before, candidate) {
2901
+ const issues = [];
2902
+ const previous = new Map(before.relations.map((row) => [row.relationId, row]));
2903
+ for (const relation of candidate.relations) {
2904
+ const original = previous.get(relation.relationId);
2905
+ if (original == null) {
2906
+ if (relation.relationKind === "caption-alignment") issues.push(`new caption-alignment relation "${relation.relationId}" is forbidden`);
2907
+ continue;
2908
+ }
2909
+ if (original.relationKind !== relation.relationKind || original.endpoint0EntityId !== relation.endpoint0EntityId || original.endpoint1EntityId !== relation.endpoint1EntityId) {
2910
+ issues.push(`relation "${relation.relationId}" changed its kind or ordered endpoints`);
2911
+ continue;
2912
+ }
2913
+ if (!sameJson(original.metadata, relation.metadata)) issues.push(`relation "${relation.relationId}" changed semantic metadata without a new identity`);
2914
+ if (relation.relationKind === "caption-alignment" && !sameJson(original.trace, relation.trace)) issues.push(`legacy caption-alignment relation "${relation.relationId}" may only be carried unchanged or deleted`);
2915
+ }
2916
+ if (issues.length > 0) throw new InvalidEntityRelationTransitionError(issues);
2917
+ }
2918
+ function sameJson(left, right) {
2919
+ if (left === right) return true;
2920
+ if (Array.isArray(left) && Array.isArray(right)) return left.length === right.length && left.every((value, index) => sameJson(value, right[index]));
2921
+ if (!isJsonObject(left) || !isJsonObject(right)) return false;
2922
+ const leftKeys = Object.keys(left);
2923
+ return leftKeys.length === Object.keys(right).length && leftKeys.every((key) => Object.hasOwn(right, key) && sameJson(left[key], right[key]));
2924
+ }
2925
+ //#endregion
2890
2926
  //#region src/document/entity-projection-values.ts
2891
2927
  /** A graph cannot be represented faithfully by the compatibility reader. */
2892
2928
  var EntityTimelineProjectionError = class extends Error {
@@ -2955,8 +2991,19 @@ var EntityProjectionGraph = class {
2955
2991
  heldBy(id) {
2956
2992
  return this.rows.relations.filter((row) => row.relationKind === "contain" && row.endpoint1EntityId === id).map((row) => this.entity(row.endpoint0EntityId));
2957
2993
  }
2958
- one(id, relation, kind) {
2959
- const values = this.adjacent(id, relation);
2994
+ /**
2995
+ * Ordered relations put the product at endpoint 0 and its source at endpoint 1.
2996
+ * One entity can sit on both sides of the same kind — a Voiceover Recording is
2997
+ * spoken from a PhoneticScript and is itself what a transcript was read from —
2998
+ * so provenance is never read through the undirected `adjacent`.
2999
+ */
3000
+ madeFrom(id, kind) {
3001
+ return this.rows.relations.filter((row) => row.relationKind === kind && row.endpoint0EntityId === id).map((row) => this.entity(row.endpoint1EntityId));
3002
+ }
3003
+ madeInto(id, kind) {
3004
+ return this.rows.relations.filter((row) => row.relationKind === kind && row.endpoint1EntityId === id).map((row) => this.entity(row.endpoint0EntityId));
3005
+ }
3006
+ one(id, relation, kind, values = this.adjacent(id, relation)) {
2960
3007
  if (values.length !== 1 || kind !== void 0 && values[0]?.entityKind !== kind) fail(`entity ${id} requires exactly one ${relation}${kind === void 0 ? "" : ` to ${kind}`}`);
2961
3008
  return values[0];
2962
3009
  }
@@ -2992,7 +3039,7 @@ function resolveGraphLayout(graph) {
2992
3039
  const held = graph.holds(clip.entityId);
2993
3040
  if (held.length !== 1) fail(`Clip ${clip.entityId} must contain exactly one content entity`);
2994
3041
  const content = held[0];
2995
- const expected = typedRole === "video_clip" ? ["image", "video"] : typedRole === "speech" ? ["voiceover-recording"] : typedRole === "caption" ? ["caption"] : ["audio"];
3042
+ const expected = typedRole === "video_clip" ? ["image", "video"] : typedRole === "speech" ? ["voiceover-recording", "audio"] : typedRole === "caption" ? ["caption"] : ["audio"];
2996
3043
  if (!expected.includes(content.entityKind)) fail(`${typedRole} Track accepts ${expected.join(" or ")}, got ${content.entityKind}`);
2997
3044
  const inMs = milliseconds(marker.payload.inMs, "inMs");
2998
3045
  const outMs = milliseconds(marker.payload.outMs, "outMs");
@@ -3142,10 +3189,11 @@ function projectEntityTimeline(rows) {
3142
3189
  } else if (track.role === "speech") {
3143
3190
  const asset = mediaAssetFacts(graph, content);
3144
3191
  requireUntrimmedAudio(content, marker, placement);
3145
- const external = externalLocator(asset, "memota-speech");
3146
- const renderEdges = graph.adjacent(content.entityId, "derived-from");
3192
+ const synthesized = content.entityKind === "voiceover-recording";
3193
+ const external = externalLocator(asset, synthesized ? "memota-speech" : "memota");
3147
3194
  const timbreEdges = graph.adjacent(content.entityId, "voice-timbre");
3148
- const assembled = renderEdges.length > 0 || timbreEdges.length > 0 ? assemblePhoneticScriptContent(rows, graph.one(content.entityId, "derived-from", "phonetic-script").entityId) : void 0;
3195
+ const assembled = synthesized ? assemblePhoneticScriptContent(rows, synthesizedPhoneticScript(graph, content).entityId) : void 0;
3196
+ const transcript = synthesized ? void 0 : transcribedAudioScript(graph, content);
3149
3197
  if (timbreEdges.length > 1) fail("A voiceover resolves at most one Voice identity");
3150
3198
  const voice = timbreEdges.length === 0 ? void 0 : graph.one(content.entityId, "voice-timbre", "voice").payload.voice;
3151
3199
  if (voice !== void 0 && (!object(voice) || voice.system !== "voice-library" || typeof voice.key !== "string")) fail("Voice must use an explicit voice-library locator");
@@ -3156,7 +3204,7 @@ function projectEntityTimeline(rows) {
3156
3204
  media_duration_ms: placement.duration,
3157
3205
  audio_storage_key: storageKey(asset),
3158
3206
  origin_speech_id: external.key,
3159
- audio_script: assembled?.text ?? (graph.adjacent(content.entityId, "derived-from").length > 0 ? assembleScriptText(graph.one(content.entityId, "derived-from", "audio-script")) : ""),
3207
+ audio_script: assembled?.text ?? (transcript === void 0 ? "" : assembleScriptText(transcript)),
3160
3208
  volume: volume.data,
3161
3209
  voice: object(voice) ? {
3162
3210
  id: typeof voice.key === "string" ? voice.key : void 0,
@@ -3275,7 +3323,17 @@ function captionStyle(value) {
3275
3323
  }
3276
3324
  function requireUntrimmedAudio(content, marker, placement) {
3277
3325
  const source = markerWindow(marker);
3278
- if (source.start !== 0 || content.payload.durationMs !== source.end || placement.duration !== source.end || marker.payload.rate !== void 0) fail("Voiceover compatibility requires the complete, unremapped take");
3326
+ if (source.start !== 0 || content.payload.durationMs !== source.end || placement.duration !== source.end || marker.payload.rate !== void 0) fail("Speech compatibility requires the complete, unremapped take");
3327
+ }
3328
+ function synthesizedPhoneticScript(graph, content) {
3329
+ const scripts = graph.rows.relations.filter((row) => row.relationKind === "derived-from" && row.endpoint0EntityId === content.entityId).map((row) => graph.entity(row.endpoint1EntityId)).filter((row) => row.entityKind === "phonetic-script");
3330
+ if (scripts.length !== 1) fail("Synthesized Speech requires exactly one ordered PhoneticScript input");
3331
+ return scripts[0];
3332
+ }
3333
+ function transcribedAudioScript(graph, content) {
3334
+ const scripts = graph.rows.relations.filter((row) => row.relationKind === "derived-from" && row.endpoint1EntityId === content.entityId).map((row) => graph.entity(row.endpoint0EntityId)).filter((row) => row.entityKind === "audio-script");
3335
+ if (scripts.length > 1) fail("Recorded Speech has ambiguous AudioScript transcripts");
3336
+ return scripts[0];
3279
3337
  }
3280
3338
  /** The Marker's window, in the {start,end} shape this compatibility reader speaks. */
3281
3339
  function markerWindow(marker) {
@@ -3684,6 +3742,19 @@ function writeEntityOrder(doc, group, roots, token) {
3684
3742
  //#region src/entity-crdt/entity-document.ts
3685
3743
  const LORO_ENTITY_SCHEMA = "medeo.entities.loro.v1";
3686
3744
  const MATERIALIZE_ORIGIN = "medeo.materialize";
3745
+ /** Read persisted relation roots before current entity versions are projected. */
3746
+ function readEntityRelationRecords(doc) {
3747
+ if (root(doc).get("schema") !== "medeo.entities.loro.v1") throw new Error("Document has no Loro entity state");
3748
+ return relationRecords(doc).map(({ rootId, identity, map, deleted }) => ({
3749
+ relationId: createRelationId(rootId),
3750
+ relationKind: identity.kind,
3751
+ endpoint0EntityId: createEntityId(identity.endpoints[0]),
3752
+ endpoint1EntityId: createEntityId(identity.endpoints[1]),
3753
+ metadata: requireMap(map, "metadata").toJSON(),
3754
+ trace: requireMap(map, "trace").toJSON(),
3755
+ deleted
3756
+ }));
3757
+ }
3687
3758
  function hash(value) {
3688
3759
  return bytesToHex(sha256(new TextEncoder().encode(canonicalJson(value))));
3689
3760
  }
@@ -3770,35 +3841,31 @@ function readEntityDocument(doc, allowSelectionConflicts = false) {
3770
3841
  }
3771
3842
  };
3772
3843
  });
3773
- const relations = [];
3774
- for (const key of section(doc, "relations").keys().sort((a, b) => a < b ? -1 : a > b ? 1 : 0)) {
3775
- const map = section(doc, "relations").get(key);
3776
- if (!(map instanceof LoroMap) || map.get("deleted") === true) continue;
3777
- const original = map.get("identity");
3778
- if (original.endpoints.some((id) => !ids.has(family(doc, id)))) continue;
3779
- const endpoints = original.endpoints.map((id) => currentId(doc, id, ids));
3780
- const relationId = equalJson(endpoints, original.endpoints) ? key : `relation_${hash([key, endpoints])}`;
3781
- relations.push({
3782
- relationId: createRelationId(relationId),
3783
- relationKind: original.kind,
3844
+ const relations = activeRelationRecords(doc, ids).map(({ rootId, identity, map }) => {
3845
+ const endpoints = identity.endpoints.map((id) => currentId(doc, id, ids));
3846
+ return {
3847
+ relationId: createRelationId(equalJson(endpoints, identity.endpoints) ? rootId : `relation_${hash([rootId, endpoints])}`),
3848
+ relationKind: identity.kind,
3784
3849
  endpoint0EntityId: createEntityId(endpoints[0]),
3785
3850
  endpoint1EntityId: createEntityId(endpoints[1]),
3786
3851
  metadata: requireMap(map, "metadata").toJSON(),
3787
3852
  trace: requireMap(map, "trace").toJSON()
3788
- });
3789
- }
3853
+ };
3854
+ });
3790
3855
  sortSequencedRelations(doc, relations);
3791
3856
  const project = {};
3792
3857
  for (const [key, value] of Object.entries(section(doc, "attachments").toJSON())) {
3793
3858
  if (typeof value !== "string") throw new Error(`Invalid project attachment ${key}`);
3794
3859
  project[key] = currentId(doc, value, ids);
3795
3860
  }
3861
+ const phoneticScriptSelections = readPhoneticScriptSelections(doc, ids);
3796
3862
  const state = {
3797
3863
  rows: {
3798
3864
  entities,
3799
3865
  relations
3800
3866
  },
3801
- project
3867
+ project,
3868
+ ...phoneticScriptSelections === void 0 ? {} : { phoneticScriptSelections }
3802
3869
  };
3803
3870
  if (!allowSelectionConflicts) for (const [key, id] of ids) {
3804
3871
  const fingerprint = selectionFingerprint(state.rows, id);
@@ -3806,6 +3873,54 @@ function readEntityDocument(doc, allowSelectionConflicts = false) {
3806
3873
  }
3807
3874
  return state;
3808
3875
  }
3876
+ function readPhoneticScriptSelections(doc, ids) {
3877
+ const native = root(doc).get("phoneticScriptSelections");
3878
+ if (native === void 0) return void 0;
3879
+ if (!(native instanceof LoroMap)) throw new Error("Invalid phonetic-script selector");
3880
+ const selections = {};
3881
+ for (const clipRoot of native.keys().sort((a, b) => a < b ? -1 : a > b ? 1 : 0)) {
3882
+ const phoneticScriptRoot = native.get(clipRoot);
3883
+ if (typeof phoneticScriptRoot !== "string") throw new Error("Invalid phonetic-script selection");
3884
+ const clipId = ids.get(clipRoot);
3885
+ const phoneticScriptId = ids.get(phoneticScriptRoot);
3886
+ if (clipId === void 0 || phoneticScriptId === void 0) continue;
3887
+ if (workspace(doc, clipRoot).get("kind") !== "clip") throw new Error("Phonetic-script selection target must be a Clip");
3888
+ if (workspace(doc, phoneticScriptRoot).get("kind") !== "phonetic-script") throw new Error("Phonetic-script selection value must be a PhoneticScript");
3889
+ selections[clipRoot] = phoneticScriptId;
3890
+ }
3891
+ return Object.keys(selections).length === 0 ? void 0 : selections;
3892
+ }
3893
+ function relationRecords(doc) {
3894
+ const records = [];
3895
+ for (const rootId of section(doc, "relations").keys().sort((a, b) => a < b ? -1 : a > b ? 1 : 0)) {
3896
+ const map = section(doc, "relations").get(rootId);
3897
+ if (!(map instanceof LoroMap)) throw new Error(`Invalid relation record ${rootId}`);
3898
+ const value = map.get("identity");
3899
+ if (!isJsonObject(value) || typeof value.kind !== "string" || !Array.isArray(value.endpoints) || value.endpoints.length !== 2 || !value.endpoints.every((endpoint) => typeof endpoint === "string")) throw new Error(`Invalid relation identity ${rootId}`);
3900
+ records.push({
3901
+ rootId,
3902
+ identity: {
3903
+ kind: value.kind,
3904
+ endpoints: [value.endpoints[0], value.endpoints[1]]
3905
+ },
3906
+ map,
3907
+ deleted: map.get("deleted") === true
3908
+ });
3909
+ }
3910
+ return records;
3911
+ }
3912
+ function activeRelationRecords(doc, ids) {
3913
+ const selector = root(doc).get("clipContentSelections");
3914
+ if (selector !== void 0 && !(selector instanceof LoroMap)) throw new Error("Invalid clip-content selector");
3915
+ return relationRecords(doc).filter(({ rootId, identity, deleted }) => {
3916
+ if (deleted) return false;
3917
+ if (!identity.endpoints.every((id) => ids.has(family(doc, id)))) return false;
3918
+ if (identity.kind !== "contain" || selector === void 0 || workspace(doc, family(doc, identity.endpoints[0])).get("kind") !== "clip") return true;
3919
+ const selected = selector.get(family(doc, identity.endpoints[0]));
3920
+ if (selected !== void 0 && typeof selected !== "string") throw new Error("Invalid clip-content selection");
3921
+ return selected === void 0 || selected === rootId;
3922
+ });
3923
+ }
3809
3924
  /** Native editing containers survive domain version changes and merge independently. */
3810
3925
  var LoroEntityDocument = class LoroEntityDocument {
3811
3926
  doc;
@@ -3837,7 +3952,9 @@ var LoroEntityDocument = class LoroEntityDocument {
3837
3952
  "relations",
3838
3953
  "project",
3839
3954
  "attachments",
3840
- "ordering"
3955
+ "ordering",
3956
+ "clipContentSelections",
3957
+ "phoneticScriptSelections"
3841
3958
  ]) root(doc).ensureMergeableMap(name);
3842
3959
  for (const row of rows.entities) apply(doc, {
3843
3960
  type: "create",
@@ -4041,11 +4158,14 @@ var LoroEntityDraft = class {
4041
4158
  row: clone(row)
4042
4159
  });
4043
4160
  }
4044
- patchRelation(relationId, metadata, trace) {
4161
+ patchRelationTrace(relationId, trace) {
4162
+ const relation = this.snapshot().rows.relations.find((row) => row.relationId === relationId);
4163
+ if (relation == null) throw new Error(`Unknown relation ${relationId}`);
4164
+ if (relation.relationKind === "caption-alignment") throw new Error("caption-alignment Relations are read-only legacy facts");
4045
4165
  this.record({
4046
4166
  type: "relation-fields",
4047
4167
  root: this.relationRoot(relationId),
4048
- metadata: clone(metadata),
4168
+ metadata: clone(relation.metadata),
4049
4169
  trace: clone(trace)
4050
4170
  });
4051
4171
  }
@@ -4055,6 +4175,35 @@ var LoroEntityDraft = class {
4055
4175
  root: this.relationRoot(relationId)
4056
4176
  });
4057
4177
  }
4178
+ /** Select the one content membership projected for this stable Clip identity. */
4179
+ selectClipContent(relationId) {
4180
+ const relationRoot = this.relationRoot(relationId);
4181
+ const map = section(this.doc, "relations").get(relationRoot);
4182
+ if (!(map instanceof LoroMap)) throw new Error(`Unknown relation ${relationId}`);
4183
+ const identity = map.get("identity");
4184
+ if (!isJsonObject(identity) || identity.kind !== "contain" || !Array.isArray(identity.endpoints) || typeof identity.endpoints[0] !== "string") throw new Error("Clip-content selection requires a contain relation");
4185
+ const clipRoot = family(this.doc, identity.endpoints[0]);
4186
+ if (workspace(this.doc, clipRoot).get("kind") !== "clip") throw new Error("Clip-content selection requires a Clip container");
4187
+ this.record({
4188
+ type: "select-clip-content",
4189
+ clipRoot,
4190
+ relationRoot
4191
+ });
4192
+ }
4193
+ /** Select the pending immutable pronunciation variant for one stable Speech Clip. */
4194
+ selectPhoneticScript(clipEntityId, phoneticScriptEntityId) {
4195
+ const clipRoot = family(this.doc, clipEntityId);
4196
+ const phoneticScriptRoot = family(this.doc, phoneticScriptEntityId);
4197
+ const clip = workspace(this.doc, clipRoot);
4198
+ const phoneticScript = workspace(this.doc, phoneticScriptRoot);
4199
+ if (clip.get("deleted") === true || clip.get("kind") !== "clip") throw new Error("Phonetic-script selection target must be an active Clip");
4200
+ if (phoneticScript.get("deleted") === true || phoneticScript.get("kind") !== "phonetic-script") throw new Error("Phonetic-script selection value must be an active PhoneticScript");
4201
+ this.record({
4202
+ type: "select-phonetic-script",
4203
+ clipRoot,
4204
+ phoneticScriptRoot
4205
+ });
4206
+ }
4058
4207
  relationRoot(relationId) {
4059
4208
  const alias = this.relationAliases.get(relationId);
4060
4209
  if (alias !== void 0) return alias;
@@ -4141,8 +4290,9 @@ var LoroEntityDraft = class {
4141
4290
  const key = this.relationRoot(relationId);
4142
4291
  const map = section(this.doc, "relations").get(key);
4143
4292
  if (!(map instanceof LoroMap) || map.get("deleted") === true) throw new Error("Unknown relation");
4293
+ if (map.get("identity").kind === "caption-alignment") throw new Error("caption-alignment Relations are read-only legacy facts");
4144
4294
  for (const change of changes) {
4145
- if (!["metadata", "trace"].includes(change.path[0]) || change.path.length < 2) throw new Error("Relation changes may only edit metadata or trace fields");
4295
+ if (change.path[0] !== "trace" || change.path.length < 2) throw new Error("Relation changes may only edit trace fields");
4146
4296
  const payload = {
4147
4297
  metadata: requireMap(map, "metadata").toJSON(),
4148
4298
  trace: requireMap(map, "trace").toJSON()
@@ -4261,6 +4411,12 @@ function apply(doc, command) {
4261
4411
  map.set("deleted", true);
4262
4412
  break;
4263
4413
  }
4414
+ case "select-clip-content":
4415
+ root(doc).ensureMergeableMap("clipContentSelections").set(command.clipRoot, command.relationRoot);
4416
+ break;
4417
+ case "select-phonetic-script":
4418
+ root(doc).ensureMergeableMap("phoneticScriptSelections").set(command.clipRoot, command.phoneticScriptRoot);
4419
+ break;
4264
4420
  case "relation-fields": {
4265
4421
  const map = section(doc, "relations").get(command.root);
4266
4422
  if (!(map instanceof LoroMap)) throw new Error("Unknown relation");
@@ -4682,4 +4838,4 @@ function createPlainMemoryAdapter(document, options) {
4682
4838
  return new PlainMemoryAdapter(document, options);
4683
4839
  }
4684
4840
  //#endregion
4685
- export { assertValidVideoDocument as $, decodeEntityRelationRows as A, updateEntityFields as B, applyFieldChanges as C, speedOf as Ct, EntityProjectionGraph as D, base64ToBytes as Dt, projectEntityTimeline as E, videoDocumentMirrorSchema as Et, assembleEntityContent as F, hasSequence as G, isJsonObject as H, assemblePhoneticScriptContent as I, buildSpeechHostMap as J, isMediaAssetVariantKind as K, assembleScriptText as L, relationsTo as M, ScriptCompositionError as N, resolveEntityTimelineLayout as O, bytesToBase64 as Ot, assembleCaptionContent as P, VideoDocumentValidationError as Q, findComposedAudioScript as R, equalJson as S, effectiveVideoClipDurationMs as St, resolveFieldPath as T, recordEntries as Tt, createEntityId as U, variantBaseEntityIds as V, createRelationId as W, fromVideoDocument as X, derivePositionFromAbs as Y, toVideoDocument as Z, audioScriptAssetOf as _, TIMELINE_SKELETON_DURATION_MS as _t, createMirrorVideoDocument as a, findLaneTrack as at, phoneticScriptAssetOf as b, safeDurationMs as bt, DocumentMutationGuard as c, solveVideoDocument as ct, LoroEntityDraft as d, fillMainTrackTimeGaps as dt, validateVideoDocument as et, readEntityDocument as f, reassignSpeechesToVideoClipsByTime as ft, audioScriptAssetHash as g, syncAggregatedClipsTimePosition as gt, audioScriptAssetFields as h, resolveSpeechOverlapByShiftingVideos as ht, MirrorVideoDocumentAdapter as i, ensureLaneTrack as it, relationsFrom as j, EntityTimelineProjectionError as k, LORO_ENTITY_SCHEMA as l, cascadeAfterVideoClipChanges as lt, audioScriptAssetContent as m, resolveAllSpeechOverlaps as mt, createPlainMemoryAdapter as n, videoDocumentSchema as nt, createMirrorVideoDocumentAdapter as o, hasResolvedPlacement as ot, entityOrderGroups as p, recalculateTimelineDuration as pt, buildInitialVideoDocument as q, generatePartId as r, LANE_KINDS_IN_STACK_ORDER as rt, readVideoDocumentFromDraft as s, laneTrackId as st, PlainMemoryAdapter as t, partUnionSchema as tt, LoroEntityDocument as u, arrangeMainTrackSeamlessly as ut, phoneticScriptAssetContent as v, isEmptyVideoClip as vt, assertFieldChanges as w, partUnionToDraft as wt, scriptSourceAssetOf as x, DEFAULT_UNIT_TIME_MS as xt, phoneticScriptAssetHash as y, partDurationMs as yt, selectAudioScriptSegment as z };
4841
+ export { fromVideoDocument as $, resolveEntityTimelineLayout as A, base64ToBytes as At, assembleScriptText as B, canonicalJson as C, safeDurationMs as Ct, resolveFieldPath as D, partUnionToDraft as Dt, assertFieldChanges as E, speedOf as Et, relationsTo as F, isJsonObject as G, selectAudioScriptSegment as H, ScriptCompositionError as I, hasSequence as J, createEntityId as K, assembleCaptionContent as L, validateEntityRelationTransition as M, decodeEntityRelationRows as N, projectEntityTimeline as O, recordEntries as Ot, relationsFrom as P, derivePositionFromAbs as Q, assembleEntityContent as R, scriptSourceAssetOf as S, partDurationMs as St, applyFieldChanges as T, effectiveVideoClipDurationMs as Tt, updateEntityFields as U, findComposedAudioScript as V, variantBaseEntityIds as W, buildInitialVideoDocument as X, isMediaAssetVariantKind as Y, buildSpeechHostMap as Z, audioScriptAssetHash as _, resolveAllSpeechOverlaps as _t, createMirrorVideoDocument as a, videoDocumentSchema as at, phoneticScriptAssetHash as b, TIMELINE_SKELETON_DURATION_MS as bt, DocumentMutationGuard as c, findLaneTrack as ct, LoroEntityDraft as d, solveVideoDocument as dt, toVideoDocument as et, readEntityDocument as f, cascadeAfterVideoClipChanges as ft, audioScriptAssetFields as g, recalculateTimelineDuration as gt, audioScriptAssetContent as h, reassignSpeechesToVideoClipsByTime as ht, MirrorVideoDocumentAdapter as i, partUnionSchema as it, EntityTimelineProjectionError as j, bytesToBase64 as jt, EntityProjectionGraph as k, videoDocumentMirrorSchema as kt, LORO_ENTITY_SCHEMA as l, hasResolvedPlacement as lt, entityOrderGroups as m, fillMainTrackTimeGaps as mt, createPlainMemoryAdapter as n, assertValidVideoDocument as nt, createMirrorVideoDocumentAdapter as o, LANE_KINDS_IN_STACK_ORDER as ot, readEntityRelationRecords as p, arrangeMainTrackSeamlessly as pt, createRelationId as q, generatePartId as r, validateVideoDocument as rt, readVideoDocumentFromDraft as s, ensureLaneTrack as st, PlainMemoryAdapter as t, VideoDocumentValidationError as tt, LoroEntityDocument as u, laneTrackId as ut, audioScriptAssetOf as v, resolveSpeechOverlapByShiftingVideos as vt, equalJson as w, DEFAULT_UNIT_TIME_MS as wt, phoneticScriptAssetOf as x, isEmptyVideoClip as xt, phoneticScriptAssetContent as y, syncAggregatedClipsTimePosition as yt, assemblePhoneticScriptContent as z };