@mengine/medeo-client 2.0.1-alpha.22 → 2.0.1-alpha.23

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.
@@ -1177,7 +1177,8 @@ function toAuthoritativePartLibrary(partLibrary) {
1177
1177
  if (typeof part !== "object" || part == null) continue;
1178
1178
  if (part.video_clip != null) out[partId] = { video_clip: withTrimWindowFromDuration(part.video_clip) };
1179
1179
  else if (part.speech != null) {
1180
- const { duration_ms, rest } = splitDurationMs(part.speech);
1180
+ const { origin_asset_system: _originAssetSystem, ...readSpeech } = part.speech;
1181
+ const { duration_ms, rest } = splitDurationMs(readSpeech);
1181
1182
  out[partId] = { speech: {
1182
1183
  ...rest,
1183
1184
  media_duration_ms: rest.media_duration_ms ?? duration_ms
@@ -2462,7 +2463,7 @@ const axVideoMarkerRelationSpec = emptySpec("axvideo-marker", "axvideo", "sequen
2462
2463
  const markerTimelineRelationSpec = emptySpec("marker-timeline", "sequence-marker", "timeline");
2463
2464
  const markerContentRelationSpec = Object.freeze({
2464
2465
  kind: "marker-content",
2465
- validateEndpoints: (endpoints) => hasMarkerAndSequence(endpoints),
2466
+ validateEndpoints: (endpoints) => endpoints[0].current().entityKind === "sequence-marker" && hasSequence(endpoints[1].current()),
2466
2467
  validateMetadata: isEmptyMetadata
2467
2468
  });
2468
2469
  /**
@@ -2476,7 +2477,7 @@ const markerContentRelationSpec = Object.freeze({
2476
2477
  */
2477
2478
  const fromAssetRelationSpec = Object.freeze({
2478
2479
  kind: "from-asset",
2479
- validateEndpoints: (endpoints) => hasExactlyOneAsset(endpoints),
2480
+ validateEndpoints: (endpoints) => declaresKind(endpoints[0].current().entityKind, "MediaFile") && endpoints[1].current().entityKind === "asset",
2480
2481
  validateMetadata: isAssetBinding
2481
2482
  });
2482
2483
  /**
@@ -2496,13 +2497,13 @@ const fromAssetRelationSpec = Object.freeze({
2496
2497
  */
2497
2498
  const toAssetRelationSpec = Object.freeze({
2498
2499
  kind: "to-asset",
2499
- validateEndpoints: (endpoints) => hasExactlyOneAsset(endpoints),
2500
+ validateEndpoints: (endpoints) => declaresKind(endpoints[0].current().entityKind, "MediaFile") && endpoints[1].current().entityKind === "asset",
2500
2501
  validateMetadata: isAssetBinding
2501
2502
  });
2502
- /** `generated(output, input)` means endpoint 0 was generated from endpoint 1. */
2503
+ /** `generated(output, input)` records media generation or an independent AudioScript revision. */
2503
2504
  const generatedRelationSpec = Object.freeze({
2504
2505
  kind: "generated",
2505
- validateEndpoints: (endpoints) => endpoints.every((endpoint) => isGeneratedMedia(endpoint.current())),
2506
+ validateEndpoints: (endpoints) => endpoints.every((endpoint) => isGeneratedMedia(endpoint.current())) || endpoints.every((endpoint) => endpoint.current().entityKind === "audio-script"),
2506
2507
  validateMetadata: isEmptyMetadata
2507
2508
  });
2508
2509
  const captionAlignmentRelationSpec = Object.freeze({
@@ -2517,8 +2518,8 @@ const clipAnchorRelationSpec = Object.freeze({
2517
2518
  validateMetadata: isEmptyMetadata
2518
2519
  });
2519
2520
  /**
2520
- * The Voiceover and the PhoneticScript it was spoken from; kinds determine
2521
- * roles regardless of endpoint positions.
2521
+ * The Voiceover and the PhoneticScript it was spoken from. Endpoint 0 is the
2522
+ * output and endpoint 1 is the input script.
2522
2523
  *
2523
2524
  * Every Voiceover must carry one: which words were said, and how, is the only
2524
2525
  * thing that makes speech reproducible, and a Voiceover that cannot answer it
@@ -2539,7 +2540,7 @@ const voiceTimbreRelationSpec = Object.freeze({
2539
2540
  validateEndpoints: (endpoints) => hasKinds(endpoints, new Set(["voiceover"]), new Set(["voice"])),
2540
2541
  validateMetadata: isEmptyMetadata
2541
2542
  });
2542
- /** AudioScript was transcribed from Audio, Video, or recorded Voice; kinds determine roles. */
2543
+ /** AudioScript was transcribed from Audio or Video: `[script, source]`. */
2543
2544
  const audioScriptSourceRelationSpec = Object.freeze({
2544
2545
  kind: "audio-script-source",
2545
2546
  validateEndpoints: (endpoints) => hasKinds(endpoints, new Set(["audio-script"]), new Set([
@@ -2591,19 +2592,7 @@ function metadataSpec(kind, a, b, validateMetadata) {
2591
2592
  function hasKinds(endpoints, aKinds, bKinds) {
2592
2593
  const first = endpoints[0].current().entityKind;
2593
2594
  const second = endpoints[1].current().entityKind;
2594
- return aKinds.has(first) && bKinds.has(second) || aKinds.has(second) && bKinds.has(first);
2595
- }
2596
- function hasMarkerAndSequence(endpoints) {
2597
- const first = endpoints[0].current();
2598
- const second = endpoints[1].current();
2599
- return first.entityKind === "sequence-marker" && hasSequence(second) || second.entityKind === "sequence-marker" && hasSequence(first);
2600
- }
2601
- /** Exactly one end is the Asset, so the other end is unambiguously the bound entity. */
2602
- function hasExactlyOneAsset(endpoints) {
2603
- const kinds = endpoints.map((endpoint) => endpoint.current().entityKind);
2604
- const assets = kinds.map((kind) => kind === "asset");
2605
- if (assets[0] === assets[1]) return false;
2606
- return declaresKind(assets[0] ? kinds[1] : kinds[0], "MediaFile");
2595
+ return aKinds.has(first) && bKinds.has(second);
2607
2596
  }
2608
2597
  function isGeneratedMedia(entity) {
2609
2598
  return entity.entityKind === "video" || entity.entityKind === "image" || entity.entityKind === "audio" || entity.entityKind === "voiceover";
@@ -2939,6 +2928,42 @@ function errorMessage(error) {
2939
2928
  return error instanceof Error ? error.message : String(error);
2940
2929
  }
2941
2930
  //#endregion
2931
+ //#region ../medeo-dsl/src/relation-transition.ts
2932
+ var InvalidEntityRelationTransitionError = class extends Error {
2933
+ issues;
2934
+ constructor(issues) {
2935
+ super(`Invalid Medeo relation transition:\n- ${issues.join("\n- ")}`);
2936
+ this.issues = issues;
2937
+ this.name = "InvalidEntityRelationTransitionError";
2938
+ }
2939
+ };
2940
+ /** Enforce relation fact identity while admitting a complete candidate view. */
2941
+ function validateEntityRelationTransition(before, candidate) {
2942
+ const issues = [];
2943
+ const previous = new Map(before.relations.map((row) => [row.relationId, row]));
2944
+ for (const relation of candidate.relations) {
2945
+ const original = previous.get(relation.relationId);
2946
+ if (original == null) {
2947
+ if (relation.relationKind === "caption-alignment") issues.push(`new caption-alignment relation "${relation.relationId}" is forbidden`);
2948
+ continue;
2949
+ }
2950
+ if (original.relationKind !== relation.relationKind || original.endpoint0EntityId !== relation.endpoint0EntityId || original.endpoint1EntityId !== relation.endpoint1EntityId) {
2951
+ issues.push(`relation "${relation.relationId}" changed its kind or ordered endpoints`);
2952
+ continue;
2953
+ }
2954
+ if (!sameJson(original.metadata, relation.metadata)) issues.push(`relation "${relation.relationId}" changed semantic metadata without a new identity`);
2955
+ 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`);
2956
+ }
2957
+ if (issues.length > 0) throw new InvalidEntityRelationTransitionError(issues);
2958
+ }
2959
+ function sameJson(left, right) {
2960
+ if (left === right) return true;
2961
+ if (Array.isArray(left) && Array.isArray(right)) return left.length === right.length && left.every((value, index) => sameJson(value, right[index]));
2962
+ if (!isJsonObject(left) || !isJsonObject(right)) return false;
2963
+ const leftKeys = Object.keys(left);
2964
+ return leftKeys.length === Object.keys(right).length && leftKeys.every((key) => Object.hasOwn(right, key) && sameJson(left[key], right[key]));
2965
+ }
2966
+ //#endregion
2942
2967
  //#region src/document/entity-projection-values.ts
2943
2968
  /** A graph cannot be represented faithfully by the compatibility reader. */
2944
2969
  var EntityTimelineProjectionError = class extends Error {
@@ -3035,8 +3060,7 @@ function resolveGraphLayout(graph) {
3035
3060
  if (clip.entityKind !== "clip" || graph.adjacent(clip.entityId, "track-clip").length !== 1) fail(`Clip ${clip.entityId} must belong to exactly one Track`);
3036
3061
  const marker = graph.one(clip.entityId, "clip-marker", "sequence-marker");
3037
3062
  const content = graph.one(marker.entityId, "marker-content");
3038
- const expected = typedRole === "video_clip" ? ["image", "video"] : typedRole === "speech" ? ["voiceover"] : typedRole === "caption" ? ["caption"] : ["audio"];
3039
- if (!expected.includes(content.entityKind)) fail(`${typedRole} Track accepts ${expected.join(" or ")}, got ${content.entityKind}`);
3063
+ assertTrackContent(graph, typedRole, content);
3040
3064
  const source = range(marker.payload.sourceRange, "sourceRange");
3041
3065
  const durationFact = marker.payload.duration;
3042
3066
  if (!object(durationFact)) fail(`Marker ${marker.entityId} requires duration`);
@@ -3155,6 +3179,33 @@ function resolveGraphLayout(graph) {
3155
3179
  }))
3156
3180
  };
3157
3181
  }
3182
+ function assertTrackContent(graph, role, content) {
3183
+ if (role === "speech") {
3184
+ assertSpeechContent(graph, content);
3185
+ return;
3186
+ }
3187
+ const expected = role === "video_clip" ? ["image", "video"] : role === "caption" ? ["caption"] : ["audio"];
3188
+ if (!expected.includes(content.entityKind)) fail(`${role} Track accepts ${expected.join(" or ")}, got ${content.entityKind}`);
3189
+ }
3190
+ function assertSpeechContent(graph, content) {
3191
+ const assetLinks = graph.rows.relations.filter((relation) => relation.relationKind === "from-asset" && (relation.endpoint0EntityId === content.entityId || relation.endpoint1EntityId === content.entityId));
3192
+ if (assetLinks.length !== 1 || assetLinks[0].endpoint0EntityId !== content.entityId) fail("Speech content requires one ordered from-asset binding");
3193
+ const asset = graph.entity(assetLinks[0].endpoint1EntityId, "asset");
3194
+ if (content.entityKind === "voiceover") {
3195
+ const renders = graph.rows.relations.filter((relation) => relation.relationKind === "phonetic-script-render" && (relation.endpoint0EntityId === content.entityId || relation.endpoint1EntityId === content.entityId));
3196
+ if (renders.length !== 1 || renders[0].endpoint0EntityId !== content.entityId || graph.entity(renders[0].endpoint1EntityId).entityKind !== "phonetic-script") fail("Synthesized Speech Voiceover requires one ordered PhoneticScript render");
3197
+ if (asset.payload.system !== "memota-speech") fail("Synthesized Speech Voiceover requires an explicit memota-speech key");
3198
+ return;
3199
+ }
3200
+ if (content.entityKind === "audio") {
3201
+ const sources = graph.rows.relations.filter((relation) => relation.relationKind === "audio-script-source" && (relation.endpoint0EntityId === content.entityId || relation.endpoint1EntityId === content.entityId));
3202
+ if (sources.length > 1) fail("Recorded speech has an ambiguous AudioScript source receipt");
3203
+ if (sources.length === 1 && (sources[0].endpoint1EntityId !== content.entityId || graph.entity(sources[0].endpoint0EntityId).entityKind !== "audio-script")) fail("Recorded Speech AudioScript source must use ordered endpoints");
3204
+ if (asset.payload.system !== "memota") fail("Recorded Speech Audio requires an explicit memota key");
3205
+ return;
3206
+ }
3207
+ fail(`speech Track accepts voiceover or audio, got ${content.entityKind}`);
3208
+ }
3158
3209
  //#endregion
3159
3210
  //#region src/document/entity-projection.ts
3160
3211
  /**
@@ -3196,20 +3247,21 @@ function projectEntityTimeline(rows) {
3196
3247
  } else if (track.role === "speech") {
3197
3248
  const asset = mediaAssetFacts(graph, content);
3198
3249
  requireUntrimmedAudio(content, marker, placement);
3199
- const external = externalLocator(asset, "memota-speech");
3200
- const renderEdges = graph.adjacent(content.entityId, "phonetic-script-render");
3201
3250
  const timbreEdges = graph.adjacent(content.entityId, "voice-timbre");
3202
- const assembled = renderEdges.length > 0 || timbreEdges.length > 0 ? assemblePhoneticScriptContent(rows, graph.one(content.entityId, "phonetic-script-render", "phonetic-script").entityId) : void 0;
3251
+ const isSynthesized = content.entityKind === "voiceover";
3252
+ const external = externalLocator(asset, isSynthesized ? "memota-speech" : "memota");
3253
+ const assembled = isSynthesized ? assemblePhoneticScriptContent(rows, graph.one(content.entityId, "phonetic-script-render", "phonetic-script").entityId) : void 0;
3203
3254
  if (timbreEdges.length > 1) fail("A voiceover resolves at most one Voice identity");
3204
3255
  const voice = timbreEdges.length === 0 ? void 0 : graph.one(content.entityId, "voice-timbre", "voice").payload.voice;
3205
3256
  if (voice !== void 0 && (!object(voice) || voice.system !== "voice-library" || typeof voice.key !== "string")) fail("Voice must use an explicit voice-library locator");
3206
3257
  const captionIds = layout.tracks.flatMap((candidate) => candidate.role === "caption" ? candidate.clips.filter((caption) => caption.anchorClipEntityId === clip.entityId).map((caption) => caption.clipEntityId) : []);
3207
- parts[clip.entityId] = { speech: {
3258
+ const speech = {
3208
3259
  id: clip.entityId,
3209
3260
  kind: "speech",
3210
3261
  media_duration_ms: placement.duration,
3211
3262
  audio_storage_key: storageKey(asset),
3212
3263
  origin_speech_id: external.key,
3264
+ origin_asset_system: external.system,
3213
3265
  audio_script: assembled?.text ?? (graph.adjacent(content.entityId, "audio-script-source").length > 0 ? assembleScriptText(graph.one(content.entityId, "audio-script-source", "audio-script")) : ""),
3214
3266
  volume: volume.data,
3215
3267
  voice: object(voice) ? {
@@ -3217,7 +3269,8 @@ function projectEntityTimeline(rows) {
3217
3269
  name: typeof voice.name === "string" ? voice.name : void 0
3218
3270
  } : void 0,
3219
3271
  caption_ids: captionIds
3220
- } };
3272
+ };
3273
+ parts[clip.entityId] = { speech };
3221
3274
  } else if (track.role === "caption") {
3222
3275
  const assembled = assembleCaptionContent(rows, content.entityId);
3223
3276
  const text = assembled.text;
@@ -3297,7 +3350,10 @@ function storageKey(asset) {
3297
3350
  function externalLocator(asset, system) {
3298
3351
  const { system: found, key } = asset.payload;
3299
3352
  if (found !== system || typeof key !== "string" || key.trim() === "") fail("Asset " + asset.entityId + " requires an explicit " + system + " key");
3300
- return { key };
3353
+ return {
3354
+ key,
3355
+ system
3356
+ };
3301
3357
  }
3302
3358
  function captionStyle(value) {
3303
3359
  if (value === void 0) return void 0;
@@ -3750,6 +3806,19 @@ function writeEntityOrder(doc, group, roots, token) {
3750
3806
  //#region src/entity-crdt/entity-document.ts
3751
3807
  const LORO_ENTITY_SCHEMA = "medeo.entities.loro.v1";
3752
3808
  const MATERIALIZE_ORIGIN = "medeo.materialize";
3809
+ /** Read persisted relation roots before current entity versions are projected. */
3810
+ function readEntityRelationRecords(doc) {
3811
+ if (root(doc).get("schema") !== "medeo.entities.loro.v1") throw new Error("Document has no Loro entity state");
3812
+ return relationRecords(doc).map(({ rootId, identity, map, deleted }) => ({
3813
+ relationId: createRelationId(rootId),
3814
+ relationKind: identity.kind,
3815
+ endpoint0EntityId: createEntityId(identity.endpoints[0]),
3816
+ endpoint1EntityId: createEntityId(identity.endpoints[1]),
3817
+ metadata: requireMap(map, "metadata").toJSON(),
3818
+ trace: requireMap(map, "trace").toJSON(),
3819
+ deleted
3820
+ }));
3821
+ }
3753
3822
  function hash(value) {
3754
3823
  return bytesToHex(sha256(new TextEncoder().encode(canonicalJson(value))));
3755
3824
  }
@@ -3841,23 +3910,17 @@ function readEntityDocument(doc, allowSelectionConflicts = false) {
3841
3910
  }
3842
3911
  };
3843
3912
  });
3844
- const relations = [];
3845
- for (const key of section(doc, "relations").keys().sort((a, b) => a < b ? -1 : a > b ? 1 : 0)) {
3846
- const map = section(doc, "relations").get(key);
3847
- if (!(map instanceof LoroMap) || map.get("deleted") === true) continue;
3848
- const original = map.get("identity");
3849
- if (original.endpoints.some((id) => !ids.has(family(doc, id)))) continue;
3850
- const endpoints = original.endpoints.map((id) => currentId(doc, id, ids));
3851
- const relationId = equalJson(endpoints, original.endpoints) ? key : `relation_${hash([key, endpoints])}`;
3852
- relations.push({
3853
- relationId: createRelationId(relationId),
3854
- relationKind: original.kind,
3913
+ const relations = activeRelationRecords(doc, ids).map(({ rootId, identity, map }) => {
3914
+ const endpoints = identity.endpoints.map((id) => currentId(doc, id, ids));
3915
+ return {
3916
+ relationId: createRelationId(equalJson(endpoints, identity.endpoints) ? rootId : `relation_${hash([rootId, endpoints])}`),
3917
+ relationKind: identity.kind,
3855
3918
  endpoint0EntityId: createEntityId(endpoints[0]),
3856
3919
  endpoint1EntityId: createEntityId(endpoints[1]),
3857
3920
  metadata: requireMap(map, "metadata").toJSON(),
3858
3921
  trace: requireMap(map, "trace").toJSON()
3859
- });
3860
- }
3922
+ };
3923
+ });
3861
3924
  const project = {};
3862
3925
  for (const [key, value] of Object.entries(section(doc, "attachments").toJSON())) {
3863
3926
  if (typeof value !== "string") throw new Error(`Invalid project attachment ${key}`);
@@ -3876,6 +3939,32 @@ function readEntityDocument(doc, allowSelectionConflicts = false) {
3876
3939
  }
3877
3940
  return state;
3878
3941
  }
3942
+ function relationRecords(doc) {
3943
+ const records = [];
3944
+ for (const rootId of section(doc, "relations").keys().sort((a, b) => a < b ? -1 : a > b ? 1 : 0)) {
3945
+ const map = section(doc, "relations").get(rootId);
3946
+ if (!(map instanceof LoroMap)) throw new Error(`Invalid relation record ${rootId}`);
3947
+ const value = map.get("identity");
3948
+ 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}`);
3949
+ const identity = {
3950
+ kind: value.kind,
3951
+ endpoints: [value.endpoints[0], value.endpoints[1]]
3952
+ };
3953
+ records.push({
3954
+ rootId,
3955
+ identity,
3956
+ map,
3957
+ deleted: map.get("deleted") === true
3958
+ });
3959
+ }
3960
+ return records;
3961
+ }
3962
+ function activeRelationRecords(doc, ids) {
3963
+ return relationRecords(doc).filter(({ identity, deleted }) => {
3964
+ if (deleted) return false;
3965
+ return identity.endpoints.every((id) => ids.has(family(doc, id)));
3966
+ });
3967
+ }
3879
3968
  /** Native editing containers survive domain version changes and merge independently. */
3880
3969
  var LoroEntityDocument = class LoroEntityDocument {
3881
3970
  doc;
@@ -4106,16 +4195,47 @@ var LoroEntityDraft = class {
4106
4195
  });
4107
4196
  }
4108
4197
  link(row) {
4198
+ if (row.relationKind === "caption-alignment") throw new Error("caption-alignment Relations are read-only legacy facts");
4199
+ if (row.relationKind === "generated" || row.relationKind === "clip-anchor") throw new Error(`Author ordered ${row.relationKind} Relations with the dedicated role-named method`);
4109
4200
  this.record({
4110
4201
  type: "relation",
4111
4202
  row: clone(row)
4112
4203
  });
4113
4204
  }
4114
- patchRelation(relationId, metadata, trace) {
4205
+ linkGenerated(input) {
4206
+ this.record({
4207
+ type: "relation",
4208
+ row: {
4209
+ relationId: input.relationId,
4210
+ relationKind: "generated",
4211
+ endpoint0EntityId: input.outputEntityId,
4212
+ endpoint1EntityId: input.inputEntityId,
4213
+ metadata: {},
4214
+ trace: clone(input.trace ?? {})
4215
+ }
4216
+ });
4217
+ }
4218
+ linkClipAnchor(input) {
4219
+ this.record({
4220
+ type: "relation",
4221
+ row: {
4222
+ relationId: input.relationId,
4223
+ relationKind: "clip-anchor",
4224
+ endpoint0EntityId: input.childClipEntityId,
4225
+ endpoint1EntityId: input.hostClipEntityId,
4226
+ metadata: {},
4227
+ trace: clone(input.trace ?? {})
4228
+ }
4229
+ });
4230
+ }
4231
+ patchRelationTrace(relationId, trace) {
4232
+ const relation = this.snapshot().rows.relations.find((row) => row.relationId === relationId);
4233
+ if (relation == null) throw new Error(`Unknown relation ${relationId}`);
4234
+ if (relation.relationKind === "caption-alignment") throw new Error("caption-alignment Relations are read-only legacy facts");
4115
4235
  this.record({
4116
4236
  type: "relation-fields",
4117
4237
  root: this.relationRoot(relationId),
4118
- metadata: clone(metadata),
4238
+ metadata: clone(relation.metadata),
4119
4239
  trace: clone(trace)
4120
4240
  });
4121
4241
  }
@@ -4211,8 +4331,9 @@ var LoroEntityDraft = class {
4211
4331
  const key = this.relationRoot(relationId);
4212
4332
  const map = section(this.doc, "relations").get(key);
4213
4333
  if (!(map instanceof LoroMap) || map.get("deleted") === true) throw new Error("Unknown relation");
4334
+ if (map.get("identity").kind === "caption-alignment") throw new Error("caption-alignment Relations are read-only legacy facts");
4214
4335
  for (const change of changes) {
4215
- if (!["metadata", "trace"].includes(change.path[0]) || change.path.length < 2) throw new Error("Relation changes may only edit metadata or trace fields");
4336
+ if (change.path[0] !== "trace" || change.path.length < 2) throw new Error("Relation changes may only edit trace fields");
4216
4337
  const payload = {
4217
4338
  metadata: requireMap(map, "metadata").toJSON(),
4218
4339
  trace: requireMap(map, "trace").toJSON()
@@ -4728,4 +4849,4 @@ function createPlainMemoryAdapter(document, options) {
4728
4849
  return new PlainMemoryAdapter(document, options);
4729
4850
  }
4730
4851
  //#endregion
4731
- export { partUnionSchema as $, decodeEntityRelationRows as A, isJsonObject as B, applyFieldChanges as C, recordEntries as Ct, EntityProjectionGraph as D, projectEntityTimeline as E, bytesToBase64 as Et, assembleScriptText as F, buildInitialVideoDocument as G, createRelationId as H, findComposedAudioScript as I, fromVideoDocument as J, buildSpeechHostMap as K, selectAudioScriptSegment as L, assembleCaptionContent as M, assembleEntityContent as N, resolveEntityTimelineLayout as O, assemblePhoneticScriptContent as P, validateVideoDocument as Q, updateEntityFields as R, equalJson as S, partUnionToDraft as St, resolveFieldPath as T, base64ToBytes as Tt, hasSequence as U, createEntityId as V, isMediaAssetVariantKind as W, VideoDocumentValidationError as X, toVideoDocument as Y, assertValidVideoDocument as Z, audioScriptAssetOf as _, partDurationMs as _t, createMirrorVideoDocument as a, laneTrackId as at, phoneticScriptAssetOf as b, effectiveVideoClipDurationMs as bt, DocumentMutationGuard as c, arrangeMainTrackSeamlessly as ct, LoroEntityDraft as d, recalculateTimelineDuration as dt, videoDocumentSchema as et, readEntityDocument as f, resolveAllSpeechOverlaps as ft, audioScriptAssetHash as g, isEmptyVideoClip as gt, audioScriptAssetFields as h, TIMELINE_SKELETON_DURATION_MS as ht, MirrorVideoDocumentAdapter as i, hasResolvedPlacement as it, ScriptCompositionError as j, EntityTimelineProjectionError as k, LORO_ENTITY_SCHEMA as l, fillMainTrackTimeGaps as lt, audioScriptAssetContent as m, syncAggregatedClipsTimePosition as mt, createPlainMemoryAdapter as n, ensureLaneTrack as nt, createMirrorVideoDocumentAdapter as o, solveVideoDocument as ot, entityOrderGroups as p, resolveSpeechOverlapByShiftingVideos as pt, derivePositionFromAbs as q, generatePartId as r, findLaneTrack as rt, readVideoDocumentFromDraft as s, cascadeAfterVideoClipChanges as st, PlainMemoryAdapter as t, LANE_KINDS_IN_STACK_ORDER as tt, LoroEntityDocument as u, reassignSpeechesToVideoClipsByTime as ut, phoneticScriptAssetContent as v, safeDurationMs as vt, assertFieldChanges as w, videoDocumentMirrorSchema as wt, scriptSourceAssetOf as x, speedOf as xt, phoneticScriptAssetHash as y, DEFAULT_UNIT_TIME_MS as yt, variantBaseEntityIds as z };
4852
+ export { VideoDocumentValidationError as $, resolveEntityTimelineLayout as A, selectAudioScriptSegment as B, canonicalJson as C, effectiveVideoClipDurationMs as Ct, resolveFieldPath as D, videoDocumentMirrorSchema as Dt, assertFieldChanges as E, recordEntries as Et, assembleCaptionContent as F, createRelationId as G, variantBaseEntityIds as H, assembleEntityContent as I, buildInitialVideoDocument as J, hasSequence as K, assemblePhoneticScriptContent as L, validateEntityRelationTransition as M, decodeEntityRelationRows as N, projectEntityTimeline as O, base64ToBytes as Ot, ScriptCompositionError as P, toVideoDocument as Q, assembleScriptText as R, scriptSourceAssetOf as S, DEFAULT_UNIT_TIME_MS as St, applyFieldChanges as T, partUnionToDraft as Tt, isJsonObject as U, updateEntityFields as V, createEntityId as W, derivePositionFromAbs as X, buildSpeechHostMap as Y, fromVideoDocument as Z, audioScriptAssetHash as _, syncAggregatedClipsTimePosition as _t, createMirrorVideoDocument as a, ensureLaneTrack as at, phoneticScriptAssetHash as b, partDurationMs as bt, DocumentMutationGuard as c, laneTrackId as ct, LoroEntityDraft as d, arrangeMainTrackSeamlessly as dt, assertValidVideoDocument as et, readEntityDocument as f, fillMainTrackTimeGaps as ft, audioScriptAssetFields as g, resolveSpeechOverlapByShiftingVideos as gt, audioScriptAssetContent as h, resolveAllSpeechOverlaps as ht, MirrorVideoDocumentAdapter as i, LANE_KINDS_IN_STACK_ORDER as it, EntityTimelineProjectionError as j, EntityProjectionGraph as k, bytesToBase64 as kt, LORO_ENTITY_SCHEMA as l, solveVideoDocument as lt, entityOrderGroups as m, recalculateTimelineDuration as mt, createPlainMemoryAdapter as n, partUnionSchema as nt, createMirrorVideoDocumentAdapter as o, findLaneTrack as ot, readEntityRelationRecords as p, reassignSpeechesToVideoClipsByTime as pt, isMediaAssetVariantKind as q, generatePartId as r, videoDocumentSchema as rt, readVideoDocumentFromDraft as s, hasResolvedPlacement as st, PlainMemoryAdapter as t, validateVideoDocument as tt, LoroEntityDocument as u, cascadeAfterVideoClipChanges as ut, audioScriptAssetOf as v, TIMELINE_SKELETON_DURATION_MS as vt, equalJson as w, speedOf as wt, phoneticScriptAssetOf as x, safeDurationMs as xt, phoneticScriptAssetContent as y, isEmptyVideoClip as yt, findComposedAudioScript as z };