@mengine/medeo-client 2.0.1-alpha.27 → 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.
- package/dist/{document-BGYPKtJ3.js → document-DcpvSvQK.js} +172 -28
- package/dist/{index-CcIoMxs1.d.ts → index-BYv9FPSS.d.ts} +339 -74
- package/dist/index.d.ts +86 -16
- package/dist/index.js +422 -32
- package/dist/testing.d.ts +1 -1
- package/dist/testing.js +1 -1
- package/package.json +4 -4
|
@@ -2495,7 +2495,7 @@ const toAssetRelationSpec = Object.freeze({
|
|
|
2495
2495
|
validateEndpoints: (endpoints) => hasExactlyOneAsset(endpoints),
|
|
2496
2496
|
validateMetadata: isAssetBinding
|
|
2497
2497
|
});
|
|
2498
|
-
/** Media
|
|
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 {
|
|
@@ -3003,7 +3039,7 @@ function resolveGraphLayout(graph) {
|
|
|
3003
3039
|
const held = graph.holds(clip.entityId);
|
|
3004
3040
|
if (held.length !== 1) fail(`Clip ${clip.entityId} must contain exactly one content entity`);
|
|
3005
3041
|
const content = held[0];
|
|
3006
|
-
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"];
|
|
3007
3043
|
if (!expected.includes(content.entityKind)) fail(`${typedRole} Track accepts ${expected.join(" or ")}, got ${content.entityKind}`);
|
|
3008
3044
|
const inMs = milliseconds(marker.payload.inMs, "inMs");
|
|
3009
3045
|
const outMs = milliseconds(marker.payload.outMs, "outMs");
|
|
@@ -3153,12 +3189,12 @@ function projectEntityTimeline(rows) {
|
|
|
3153
3189
|
} else if (track.role === "speech") {
|
|
3154
3190
|
const asset = mediaAssetFacts(graph, content);
|
|
3155
3191
|
requireUntrimmedAudio(content, marker, placement);
|
|
3156
|
-
const
|
|
3157
|
-
const
|
|
3192
|
+
const synthesized = content.entityKind === "voiceover-recording";
|
|
3193
|
+
const external = externalLocator(asset, synthesized ? "memota-speech" : "memota");
|
|
3158
3194
|
const timbreEdges = graph.adjacent(content.entityId, "voice-timbre");
|
|
3159
|
-
const assembled =
|
|
3195
|
+
const assembled = synthesized ? assemblePhoneticScriptContent(rows, synthesizedPhoneticScript(graph, content).entityId) : void 0;
|
|
3196
|
+
const transcript = synthesized ? void 0 : transcribedAudioScript(graph, content);
|
|
3160
3197
|
if (timbreEdges.length > 1) fail("A voiceover resolves at most one Voice identity");
|
|
3161
|
-
const transcripts = graph.madeInto(content.entityId, "derived-from");
|
|
3162
3198
|
const voice = timbreEdges.length === 0 ? void 0 : graph.one(content.entityId, "voice-timbre", "voice").payload.voice;
|
|
3163
3199
|
if (voice !== void 0 && (!object(voice) || voice.system !== "voice-library" || typeof voice.key !== "string")) fail("Voice must use an explicit voice-library locator");
|
|
3164
3200
|
const captionIds = layout.tracks.flatMap((candidate) => candidate.role === "caption" ? candidate.clips.filter((caption) => caption.anchorClipEntityId === clip.entityId).map((caption) => caption.clipEntityId) : []);
|
|
@@ -3168,7 +3204,7 @@ function projectEntityTimeline(rows) {
|
|
|
3168
3204
|
media_duration_ms: placement.duration,
|
|
3169
3205
|
audio_storage_key: storageKey(asset),
|
|
3170
3206
|
origin_speech_id: external.key,
|
|
3171
|
-
audio_script: assembled?.text ?? (
|
|
3207
|
+
audio_script: assembled?.text ?? (transcript === void 0 ? "" : assembleScriptText(transcript)),
|
|
3172
3208
|
volume: volume.data,
|
|
3173
3209
|
voice: object(voice) ? {
|
|
3174
3210
|
id: typeof voice.key === "string" ? voice.key : void 0,
|
|
@@ -3287,7 +3323,17 @@ function captionStyle(value) {
|
|
|
3287
3323
|
}
|
|
3288
3324
|
function requireUntrimmedAudio(content, marker, placement) {
|
|
3289
3325
|
const source = markerWindow(marker);
|
|
3290
|
-
if (source.start !== 0 || content.payload.durationMs !== source.end || placement.duration !== source.end || marker.payload.rate !== void 0) fail("
|
|
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];
|
|
3291
3337
|
}
|
|
3292
3338
|
/** The Marker's window, in the {start,end} shape this compatibility reader speaks. */
|
|
3293
3339
|
function markerWindow(marker) {
|
|
@@ -3696,6 +3742,19 @@ function writeEntityOrder(doc, group, roots, token) {
|
|
|
3696
3742
|
//#region src/entity-crdt/entity-document.ts
|
|
3697
3743
|
const LORO_ENTITY_SCHEMA = "medeo.entities.loro.v1";
|
|
3698
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
|
+
}
|
|
3699
3758
|
function hash(value) {
|
|
3700
3759
|
return bytesToHex(sha256(new TextEncoder().encode(canonicalJson(value))));
|
|
3701
3760
|
}
|
|
@@ -3782,35 +3841,31 @@ function readEntityDocument(doc, allowSelectionConflicts = false) {
|
|
|
3782
3841
|
}
|
|
3783
3842
|
};
|
|
3784
3843
|
});
|
|
3785
|
-
const relations =
|
|
3786
|
-
|
|
3787
|
-
|
|
3788
|
-
|
|
3789
|
-
|
|
3790
|
-
if (original.endpoints.some((id) => !ids.has(family(doc, id)))) continue;
|
|
3791
|
-
const endpoints = original.endpoints.map((id) => currentId(doc, id, ids));
|
|
3792
|
-
const relationId = equalJson(endpoints, original.endpoints) ? key : `relation_${hash([key, endpoints])}`;
|
|
3793
|
-
relations.push({
|
|
3794
|
-
relationId: createRelationId(relationId),
|
|
3795
|
-
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,
|
|
3796
3849
|
endpoint0EntityId: createEntityId(endpoints[0]),
|
|
3797
3850
|
endpoint1EntityId: createEntityId(endpoints[1]),
|
|
3798
3851
|
metadata: requireMap(map, "metadata").toJSON(),
|
|
3799
3852
|
trace: requireMap(map, "trace").toJSON()
|
|
3800
|
-
}
|
|
3801
|
-
}
|
|
3853
|
+
};
|
|
3854
|
+
});
|
|
3802
3855
|
sortSequencedRelations(doc, relations);
|
|
3803
3856
|
const project = {};
|
|
3804
3857
|
for (const [key, value] of Object.entries(section(doc, "attachments").toJSON())) {
|
|
3805
3858
|
if (typeof value !== "string") throw new Error(`Invalid project attachment ${key}`);
|
|
3806
3859
|
project[key] = currentId(doc, value, ids);
|
|
3807
3860
|
}
|
|
3861
|
+
const phoneticScriptSelections = readPhoneticScriptSelections(doc, ids);
|
|
3808
3862
|
const state = {
|
|
3809
3863
|
rows: {
|
|
3810
3864
|
entities,
|
|
3811
3865
|
relations
|
|
3812
3866
|
},
|
|
3813
|
-
project
|
|
3867
|
+
project,
|
|
3868
|
+
...phoneticScriptSelections === void 0 ? {} : { phoneticScriptSelections }
|
|
3814
3869
|
};
|
|
3815
3870
|
if (!allowSelectionConflicts) for (const [key, id] of ids) {
|
|
3816
3871
|
const fingerprint = selectionFingerprint(state.rows, id);
|
|
@@ -3818,6 +3873,54 @@ function readEntityDocument(doc, allowSelectionConflicts = false) {
|
|
|
3818
3873
|
}
|
|
3819
3874
|
return state;
|
|
3820
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
|
+
}
|
|
3821
3924
|
/** Native editing containers survive domain version changes and merge independently. */
|
|
3822
3925
|
var LoroEntityDocument = class LoroEntityDocument {
|
|
3823
3926
|
doc;
|
|
@@ -3849,7 +3952,9 @@ var LoroEntityDocument = class LoroEntityDocument {
|
|
|
3849
3952
|
"relations",
|
|
3850
3953
|
"project",
|
|
3851
3954
|
"attachments",
|
|
3852
|
-
"ordering"
|
|
3955
|
+
"ordering",
|
|
3956
|
+
"clipContentSelections",
|
|
3957
|
+
"phoneticScriptSelections"
|
|
3853
3958
|
]) root(doc).ensureMergeableMap(name);
|
|
3854
3959
|
for (const row of rows.entities) apply(doc, {
|
|
3855
3960
|
type: "create",
|
|
@@ -4053,11 +4158,14 @@ var LoroEntityDraft = class {
|
|
|
4053
4158
|
row: clone(row)
|
|
4054
4159
|
});
|
|
4055
4160
|
}
|
|
4056
|
-
|
|
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");
|
|
4057
4165
|
this.record({
|
|
4058
4166
|
type: "relation-fields",
|
|
4059
4167
|
root: this.relationRoot(relationId),
|
|
4060
|
-
metadata: clone(metadata),
|
|
4168
|
+
metadata: clone(relation.metadata),
|
|
4061
4169
|
trace: clone(trace)
|
|
4062
4170
|
});
|
|
4063
4171
|
}
|
|
@@ -4067,6 +4175,35 @@ var LoroEntityDraft = class {
|
|
|
4067
4175
|
root: this.relationRoot(relationId)
|
|
4068
4176
|
});
|
|
4069
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
|
+
}
|
|
4070
4207
|
relationRoot(relationId) {
|
|
4071
4208
|
const alias = this.relationAliases.get(relationId);
|
|
4072
4209
|
if (alias !== void 0) return alias;
|
|
@@ -4153,8 +4290,9 @@ var LoroEntityDraft = class {
|
|
|
4153
4290
|
const key = this.relationRoot(relationId);
|
|
4154
4291
|
const map = section(this.doc, "relations").get(key);
|
|
4155
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");
|
|
4156
4294
|
for (const change of changes) {
|
|
4157
|
-
if (
|
|
4295
|
+
if (change.path[0] !== "trace" || change.path.length < 2) throw new Error("Relation changes may only edit trace fields");
|
|
4158
4296
|
const payload = {
|
|
4159
4297
|
metadata: requireMap(map, "metadata").toJSON(),
|
|
4160
4298
|
trace: requireMap(map, "trace").toJSON()
|
|
@@ -4273,6 +4411,12 @@ function apply(doc, command) {
|
|
|
4273
4411
|
map.set("deleted", true);
|
|
4274
4412
|
break;
|
|
4275
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;
|
|
4276
4420
|
case "relation-fields": {
|
|
4277
4421
|
const map = section(doc, "relations").get(command.root);
|
|
4278
4422
|
if (!(map instanceof LoroMap)) throw new Error("Unknown relation");
|
|
@@ -4694,4 +4838,4 @@ function createPlainMemoryAdapter(document, options) {
|
|
|
4694
4838
|
return new PlainMemoryAdapter(document, options);
|
|
4695
4839
|
}
|
|
4696
4840
|
//#endregion
|
|
4697
|
-
export {
|
|
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 };
|