@mengine/medeo-client 2.1.1-dsl.1 → 2.1.1-dsl.3
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 +17 -3
- package/dist/{dsl-EV6kCdr6.js → dsl-Dm3Sq1fa.js} +307 -354
- package/dist/dsl.d.ts +3 -3
- package/dist/dsl.js +2 -2
- package/dist/{index-B7fC7q7P.d.ts → index-CwG4lYKp.d.ts} +23 -4
- package/dist/{index-Cu7SdKN5.d.ts → index-DP9FG0z_.d.ts} +41 -137
- package/dist/{index-yifEtZSP.d.ts → index-W1eHhVus.d.ts} +22 -53
- package/dist/index.d.ts +5 -5
- package/dist/index.js +54 -4
- package/dist/legacy.d.ts +2 -2
- package/dist/legacy.js +1 -1
- package/dist/testing.d.ts +2 -2
- package/dist/testing.js +2 -2
- package/dist/{video-draft-types-c_shaRyq.d.ts → video-draft-types-BfkIuW5f.d.ts} +40 -44
- package/dist/{video-draft-types-TXRv3wS6.js → video-draft-types-DvPeam87.js} +105 -98
- package/package.json +6 -6
package/dist/testing.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { n as bytesToBase64, t as base64ToBytes } from "./base64-D-jd-dr5.js";
|
|
2
|
-
import { a as documentFormat, i as assertDslDocument } from "./video-draft-types-
|
|
3
|
-
import { a as MedeoDsl, i as DslEditor, n as toVideoDraft, r as createInitialMedeoDsl } from "./dsl-
|
|
2
|
+
import { a as documentFormat, i as assertDslDocument } from "./video-draft-types-DvPeam87.js";
|
|
3
|
+
import { a as MedeoDsl, i as DslEditor, n as toVideoDraft, r as createInitialMedeoDsl } from "./dsl-Dm3Sq1fa.js";
|
|
4
4
|
import { t as classifyUpdate } from "./loro-relay-doc-Bi6JKqtQ.js";
|
|
5
5
|
import { LoroDoc, VersionVector, encodeFrontiers } from "loro-crdt";
|
|
6
6
|
//#region src/testing/in-memory-mengine-server.ts
|
|
@@ -17,9 +17,24 @@ interface SeqMarker {
|
|
|
17
17
|
rate?: number;
|
|
18
18
|
fillTimeline?: true;
|
|
19
19
|
}
|
|
20
|
+
/**
|
|
21
|
+
* Where a Clip starts: at an absolute time on the Timeline, or following another Clip. No anchor
|
|
22
|
+
* means it plays in Tree order. One value, so concurrent placements keep one whole placement.
|
|
23
|
+
*/
|
|
24
|
+
interface TimeAnchor {
|
|
25
|
+
/** The Timeline (absolute) or another Clip (following). */
|
|
26
|
+
to: string;
|
|
27
|
+
offsetMs: number;
|
|
28
|
+
/** The absolute start recorded by the placement edit; used when the anchor cannot be solved. */
|
|
29
|
+
fallback_abs_ms: number;
|
|
30
|
+
}
|
|
31
|
+
/** A Clip's single-valued relations are fields of its own row: its content and its time anchor. */
|
|
20
32
|
interface Clip {
|
|
21
33
|
seqMarker: SeqMarker;
|
|
22
34
|
volume?: number;
|
|
35
|
+
/** The media or Caption this use plays. */
|
|
36
|
+
source: string;
|
|
37
|
+
anchor?: TimeAnchor;
|
|
23
38
|
}
|
|
24
39
|
interface MediaLocator {
|
|
25
40
|
system: ResourceSystem;
|
|
@@ -57,21 +72,33 @@ interface CaptionStyle$1 {
|
|
|
57
72
|
positionX?: number;
|
|
58
73
|
positionY?: number;
|
|
59
74
|
}
|
|
75
|
+
/** A run of a script's body a Caption reads its words from. One value: script and range move together. */
|
|
76
|
+
interface CaptionContent {
|
|
77
|
+
audioScriptId: string;
|
|
78
|
+
start: Cursor;
|
|
79
|
+
end: Cursor;
|
|
80
|
+
}
|
|
81
|
+
/** Words come from a selected run of a script or from the Caption's own text, never both. */
|
|
60
82
|
interface Caption {
|
|
83
|
+
selection?: CaptionContent;
|
|
61
84
|
text?: string;
|
|
62
85
|
style?: CaptionStyle$1;
|
|
63
86
|
}
|
|
87
|
+
/** A phonetic script's base script and the pronunciation written for it, taken or replaced together. */
|
|
88
|
+
interface Phoneme {
|
|
89
|
+
audioScriptId: string;
|
|
90
|
+
/** One whole script in the target TTS vocabulary, such as a `<speak>` document. */
|
|
91
|
+
phonemeScript?: string;
|
|
92
|
+
}
|
|
93
|
+
/** A variant of an AudioScript: the script it reads, plus how to pronounce it. */
|
|
94
|
+
interface PhoneticScript {
|
|
95
|
+
phoneme: Phoneme;
|
|
96
|
+
}
|
|
64
97
|
interface Voice {
|
|
65
98
|
system: 'voice-library';
|
|
66
99
|
key: string;
|
|
67
100
|
name?: string;
|
|
68
101
|
}
|
|
69
|
-
interface Asset {
|
|
70
|
-
system: ResourceSystem;
|
|
71
|
-
key: string;
|
|
72
|
-
storageKey?: string;
|
|
73
|
-
digest?: string;
|
|
74
|
-
}
|
|
75
102
|
/** Table keys carry entity identity; values never duplicate an id or a single-table kind. */
|
|
76
103
|
interface EntityValues {
|
|
77
104
|
'timeline-entities': Record<string, never>;
|
|
@@ -79,35 +106,12 @@ interface EntityValues {
|
|
|
79
106
|
'clip-entities': Clip;
|
|
80
107
|
'media-entities': Media;
|
|
81
108
|
'audio-script-entities': AudioScript;
|
|
109
|
+
'phonetic-script-entities': PhoneticScript;
|
|
82
110
|
'caption-entities': Caption;
|
|
83
111
|
'voice-entities': Voice;
|
|
84
|
-
'asset-entities': Asset;
|
|
85
|
-
}
|
|
86
|
-
interface TimeAnchor {
|
|
87
|
-
from: string;
|
|
88
|
-
to: string;
|
|
89
|
-
offsetMs: number;
|
|
90
|
-
fallback_abs_ms: number;
|
|
91
|
-
}
|
|
92
|
-
interface CaptionContent {
|
|
93
|
-
captionId: string;
|
|
94
|
-
audioScriptId: string;
|
|
95
|
-
start: Cursor;
|
|
96
|
-
end: Cursor;
|
|
97
112
|
}
|
|
113
|
+
/** Only relations an entity may hold many of have tables; single-valued ones are fields of their owner. */
|
|
98
114
|
interface RelationValues {
|
|
99
|
-
'clip-source-relations': {
|
|
100
|
-
clipId: string;
|
|
101
|
-
sourceId: string;
|
|
102
|
-
};
|
|
103
|
-
'time-anchor-relations': TimeAnchor;
|
|
104
|
-
'caption-content-relations': CaptionContent;
|
|
105
|
-
'caption-alignment-relations': {
|
|
106
|
-
captionId: string;
|
|
107
|
-
mediaId: string;
|
|
108
|
-
startMs: number;
|
|
109
|
-
endMs: number;
|
|
110
|
-
};
|
|
111
115
|
'voice-timbre-relations': {
|
|
112
116
|
recordingId: string;
|
|
113
117
|
voiceId: string;
|
|
@@ -116,14 +120,6 @@ interface RelationValues {
|
|
|
116
120
|
entityId: string;
|
|
117
121
|
sourceId: string;
|
|
118
122
|
};
|
|
119
|
-
'from-asset-relations': {
|
|
120
|
-
audioScriptId: string;
|
|
121
|
-
assetId: string;
|
|
122
|
-
};
|
|
123
|
-
'to-asset-relations': {
|
|
124
|
-
audioScriptId: string;
|
|
125
|
-
assetId: string;
|
|
126
|
-
};
|
|
127
123
|
}
|
|
128
124
|
type EntityTable = keyof EntityValues;
|
|
129
125
|
type RelationTable = keyof RelationValues;
|
|
@@ -220,7 +216,7 @@ type MedeoDslSnapshot = DeepReadonly<EntityTables & RelationTables & {
|
|
|
220
216
|
captionText: Record<string, CaptionText>;
|
|
221
217
|
diagnostics: DslDiagnostic[];
|
|
222
218
|
}>;
|
|
223
|
-
type TextMark = 'language'
|
|
219
|
+
type TextMark = 'language';
|
|
224
220
|
type TextEdit = {
|
|
225
221
|
type: 'insert';
|
|
226
222
|
at: number;
|
|
@@ -234,7 +230,7 @@ type TextEdit = {
|
|
|
234
230
|
start: number;
|
|
235
231
|
end: number;
|
|
236
232
|
key: TextMark;
|
|
237
|
-
value: string
|
|
233
|
+
value: string;
|
|
238
234
|
} | {
|
|
239
235
|
type: 'unmark';
|
|
240
236
|
start: number;
|
|
@@ -256,9 +252,9 @@ type TextDelta = {
|
|
|
256
252
|
//#endregion
|
|
257
253
|
//#region ../medeo-dsl/src/ids.d.ts
|
|
258
254
|
declare function tupleHash(tuple: readonly string[]): string;
|
|
259
|
-
declare function resourceId(kind: 'media' | 'voice'
|
|
255
|
+
declare function resourceId(kind: 'media' | 'voice', system: string, key: string): string;
|
|
260
256
|
declare function relationId<K extends RelationTable>(table: K, value: RelationValues[K]): string;
|
|
261
|
-
type EntityIdKind = 'timeline' | 'track' | 'clip' | 'audio-script' | 'caption';
|
|
257
|
+
type EntityIdKind = 'timeline' | 'track' | 'clip' | 'audio-script' | 'phonetic-script' | 'caption';
|
|
262
258
|
type DslIdFactory = (kind: EntityIdKind) => string;
|
|
263
259
|
declare const defaultIdFactory: DslIdFactory;
|
|
264
260
|
//#endregion
|
|
@@ -806,4 +802,4 @@ type VideoDraftContent = Pick<VideoDraft, 'timeline' | 'main_track' | 'above_mai
|
|
|
806
802
|
type VideoDraftInput = Pick<VideoDraft, keyof VideoDraftContent>;
|
|
807
803
|
declare const DEFAULT_UNIT_TIME_MS = 33.333;
|
|
808
804
|
//#endregion
|
|
809
|
-
export {
|
|
805
|
+
export { TrackRole as $, DeepReadonly as A, RelationMutation as B, CaptionContent as C, Clip as D, CaptionText as E, EntityTables as F, RichTextSnapshot as G, RelationTables as H, EntityValues as I, TextMark as J, TextDelta as K, FieldPatch as L, DslMutation as M, EntityMutation as N, ContainMutation as O, EntityTable as P, Track$1 as Q, MedeoDslSnapshot as R, Caption as S, CaptionStyle$1 as T, RelationValues as U, RelationTable as V, ResourceSystem as W, TextRun as X, TextMutation as Y, TimeAnchor as Z, defaultIdFactory as _, VideoDraftCaptionStyle as a, tupleHash as b, VideoDraftInput as c, VideoDraftTrack as d, Voice as et, VideoDraftTrackItem as f, EntityIdKind as g, DslIdFactory as h, VideoDraft as i, DslDiagnostic as j, ContainNode as k, VideoDraftPartUnion as l, OpActor as m, DEFAULT_UNIT_TIME_MS as n, VideoDraftContent as o, CommitOptions as p, TextEdit as q, VideoCreationSettings as r, VideoDraftContentPartUnion as s, CaptionDisplayCue as t, VideoDraftTimeline as u, relationId as v, CaptionSelection as w, AudioScript as x, resourceId as y, Media as z };
|
|
@@ -21,29 +21,22 @@ function resourceId(kind, system, key) {
|
|
|
21
21
|
function relationId(table, value) {
|
|
22
22
|
const v = value;
|
|
23
23
|
switch (table) {
|
|
24
|
-
case "clip-source-relations": return `cs:${v.clipId}`;
|
|
25
|
-
case "time-anchor-relations": return `ta:${v.from}`;
|
|
26
|
-
case "caption-content-relations": return `cc:${v.captionId}`;
|
|
27
|
-
case "caption-alignment-relations": return `ca:${v.captionId}`;
|
|
28
24
|
case "voice-timbre-relations": return `voice-timbre:${tupleHash([v.recordingId, v.voiceId])}`;
|
|
29
25
|
case "derived-from-relations": return `derived-from:${tupleHash([v.entityId, v.sourceId])}`;
|
|
30
|
-
case "from-asset-relations": return `from-asset:${v.audioScriptId}`;
|
|
31
|
-
case "to-asset-relations": return `to-asset:${v.audioScriptId}`;
|
|
32
26
|
}
|
|
33
27
|
throw new Error(`Unknown relation table: ${table}`);
|
|
34
28
|
}
|
|
35
29
|
const defaultIdFactory = (kind) => `${kind}_${globalThis.crypto.randomUUID()}`;
|
|
36
30
|
const resourceKinds = {
|
|
37
31
|
"media-entities": "media",
|
|
38
|
-
"voice-entities": "voice"
|
|
39
|
-
"asset-entities": "asset"
|
|
32
|
+
"voice-entities": "voice"
|
|
40
33
|
};
|
|
41
34
|
/** A resource row's ID is derived from its locator, so one external resource is one entity. */
|
|
42
35
|
function assertResourceIdentity(table, id, value) {
|
|
43
36
|
const kind = resourceKinds[table];
|
|
44
37
|
if (kind && id !== resourceId(kind, value.system, value.key)) throw new Error("Resource identity does not match locator");
|
|
45
38
|
}
|
|
46
|
-
/** A relation row's ID is derived from its
|
|
39
|
+
/** A relation row's ID is derived from its two ends. */
|
|
47
40
|
function assertRelationIdentity(table, id, value) {
|
|
48
41
|
if (id !== relationId(table, value)) throw new Error("Relation ID does not match endpoints");
|
|
49
42
|
}
|
|
@@ -61,16 +54,19 @@ const optional$1 = (check) => ({
|
|
|
61
54
|
check,
|
|
62
55
|
optional: true
|
|
63
56
|
});
|
|
57
|
+
const reservedIds = [
|
|
58
|
+
"$cid",
|
|
59
|
+
"__proto__",
|
|
60
|
+
"constructor",
|
|
61
|
+
"prototype"
|
|
62
|
+
];
|
|
63
|
+
const id = (v) => nonempty(v) && !reservedIds.includes(v);
|
|
64
|
+
const cursor = (v) => v instanceof Cursor;
|
|
64
65
|
function isRecord(value) {
|
|
65
66
|
return value !== null && typeof value === "object" && !Array.isArray(value) && !(value instanceof Cursor);
|
|
66
67
|
}
|
|
67
68
|
function assertId(value) {
|
|
68
|
-
if (!
|
|
69
|
-
"$cid",
|
|
70
|
-
"__proto__",
|
|
71
|
-
"constructor",
|
|
72
|
-
"prototype"
|
|
73
|
-
].includes(value)) throw new Error("Expected a nonempty, non-reserved ID");
|
|
69
|
+
if (!id(value)) throw new Error("Expected a nonempty, non-reserved ID");
|
|
74
70
|
}
|
|
75
71
|
function validLanguage(value) {
|
|
76
72
|
if (typeof value !== "string") return false;
|
|
@@ -126,6 +122,30 @@ const seqMarkerFields = {
|
|
|
126
122
|
function assertSeqMarker(value) {
|
|
127
123
|
checkFields(value, seqMarkerFields);
|
|
128
124
|
}
|
|
125
|
+
const timeAnchorFields = {
|
|
126
|
+
to: required(id),
|
|
127
|
+
offsetMs: required(integer),
|
|
128
|
+
fallback_abs_ms: required(nonnegative)
|
|
129
|
+
};
|
|
130
|
+
/** A time anchor's own fields; what it points at is checked by `assertReferenceAdmitted`. */
|
|
131
|
+
function assertTimeAnchor(value) {
|
|
132
|
+
checkFields(value, timeAnchorFields);
|
|
133
|
+
}
|
|
134
|
+
const captionContentFields = {
|
|
135
|
+
audioScriptId: required(id),
|
|
136
|
+
start: required(cursor),
|
|
137
|
+
end: required(cursor)
|
|
138
|
+
};
|
|
139
|
+
function assertCaptionContent(value) {
|
|
140
|
+
checkFields(value, captionContentFields);
|
|
141
|
+
}
|
|
142
|
+
const phonemeFields = {
|
|
143
|
+
audioScriptId: required(id),
|
|
144
|
+
phonemeScript: optional$1(string)
|
|
145
|
+
};
|
|
146
|
+
function assertPhoneme(value) {
|
|
147
|
+
checkFields(value, phonemeFields);
|
|
148
|
+
}
|
|
129
149
|
const locator = {
|
|
130
150
|
system: required(oneOf("memota", "memota-speech")),
|
|
131
151
|
key: required(nonempty)
|
|
@@ -138,7 +158,9 @@ const entityFields = {
|
|
|
138
158
|
},
|
|
139
159
|
"clip-entities": {
|
|
140
160
|
seqMarker: required(object(seqMarkerFields)),
|
|
141
|
-
volume: optional$1(finite)
|
|
161
|
+
volume: optional$1(finite),
|
|
162
|
+
source: required(id),
|
|
163
|
+
anchor: optional$1(object(timeAnchorFields))
|
|
142
164
|
},
|
|
143
165
|
"media-entities": {
|
|
144
166
|
...locator,
|
|
@@ -151,7 +173,9 @@ const entityFields = {
|
|
|
151
173
|
body: required(string),
|
|
152
174
|
defaultLanguage: optional$1(validLanguage)
|
|
153
175
|
},
|
|
176
|
+
"phonetic-script-entities": { phoneme: required(object(phonemeFields)) },
|
|
154
177
|
"caption-entities": {
|
|
178
|
+
selection: optional$1(object(captionContentFields)),
|
|
155
179
|
text: optional$1(string),
|
|
156
180
|
style: optional$1(object(styleFields))
|
|
157
181
|
},
|
|
@@ -159,52 +183,26 @@ const entityFields = {
|
|
|
159
183
|
system: required(oneOf("voice-library")),
|
|
160
184
|
key: required(nonempty),
|
|
161
185
|
name: optional$1(string)
|
|
162
|
-
},
|
|
163
|
-
"asset-entities": {
|
|
164
|
-
...locator,
|
|
165
|
-
storageKey: optional$1(nonempty),
|
|
166
|
-
digest: optional$1(nonempty)
|
|
167
186
|
}
|
|
168
187
|
};
|
|
169
188
|
const entityTables = Object.keys(entityFields);
|
|
189
|
+
/**
|
|
190
|
+
* Fields stored as one encoded string: written whole, never merged field by field. A patch to one
|
|
191
|
+
* is merged into the current value first, then the whole value is written.
|
|
192
|
+
*/
|
|
193
|
+
const atomicFields = {
|
|
194
|
+
"clip-entities": ["seqMarker", "anchor"],
|
|
195
|
+
"phonetic-script-entities": ["phoneme"],
|
|
196
|
+
"caption-entities": ["selection"]
|
|
197
|
+
};
|
|
170
198
|
const relationFields = {
|
|
171
|
-
"clip-source-relations": {
|
|
172
|
-
clipId: required(nonempty),
|
|
173
|
-
sourceId: required(nonempty)
|
|
174
|
-
},
|
|
175
|
-
"time-anchor-relations": {
|
|
176
|
-
from: required(nonempty),
|
|
177
|
-
to: required(nonempty),
|
|
178
|
-
offsetMs: required(integer),
|
|
179
|
-
fallback_abs_ms: required(nonnegative)
|
|
180
|
-
},
|
|
181
|
-
"caption-content-relations": {
|
|
182
|
-
captionId: required(nonempty),
|
|
183
|
-
audioScriptId: required(nonempty),
|
|
184
|
-
start: required((v) => v instanceof Cursor),
|
|
185
|
-
end: required((v) => v instanceof Cursor)
|
|
186
|
-
},
|
|
187
|
-
"caption-alignment-relations": {
|
|
188
|
-
captionId: required(nonempty),
|
|
189
|
-
mediaId: required(nonempty),
|
|
190
|
-
startMs: required(nonnegative),
|
|
191
|
-
endMs: required(nonnegative)
|
|
192
|
-
},
|
|
193
199
|
"voice-timbre-relations": {
|
|
194
|
-
recordingId: required(
|
|
195
|
-
voiceId: required(
|
|
200
|
+
recordingId: required(id),
|
|
201
|
+
voiceId: required(id)
|
|
196
202
|
},
|
|
197
203
|
"derived-from-relations": {
|
|
198
|
-
entityId: required(
|
|
199
|
-
sourceId: required(
|
|
200
|
-
},
|
|
201
|
-
"from-asset-relations": {
|
|
202
|
-
audioScriptId: required(nonempty),
|
|
203
|
-
assetId: required(nonempty)
|
|
204
|
-
},
|
|
205
|
-
"to-asset-relations": {
|
|
206
|
-
audioScriptId: required(nonempty),
|
|
207
|
-
assetId: required(nonempty)
|
|
204
|
+
entityId: required(id),
|
|
205
|
+
sourceId: required(id)
|
|
208
206
|
}
|
|
209
207
|
};
|
|
210
208
|
const relationTables = Object.keys(relationFields);
|
|
@@ -222,12 +220,12 @@ function assertEntityPatch(table, patch, current) {
|
|
|
222
220
|
if (table === "audio-script-entities" && Object.hasOwn(patch, "body")) throw new Error("Edit body through the Text adapter");
|
|
223
221
|
if (table === "caption-entities" && current.style !== void 0 && Object.hasOwn(patch, "style") && patch.style === void 0) throw new Error("Reset individual style fields; retain the style Map");
|
|
224
222
|
if (table === "caption-entities" && current.text !== void 0 && patch.text !== void 0) throw new Error("Edit Caption text through the Text adapter");
|
|
225
|
-
const next =
|
|
223
|
+
const next = patchRow(table, current, patch);
|
|
226
224
|
for (const key of Object.keys(patch)) {
|
|
227
225
|
const field = entityFields[table][key];
|
|
228
226
|
if (!field || !field.optional && next[key] === void 0 || next[key] !== void 0 && !field.check(next[key])) throw new Error(`Invalid patch field: ${key}`);
|
|
229
227
|
}
|
|
230
|
-
if (table === "media-entities" || table === "voice-entities"
|
|
228
|
+
if (table === "media-entities" || table === "voice-entities") {
|
|
231
229
|
for (const key of [
|
|
232
230
|
"system",
|
|
233
231
|
"key",
|
|
@@ -243,18 +241,25 @@ function assertEntityPatch(table, patch, current) {
|
|
|
243
241
|
}
|
|
244
242
|
function assertRelation(table, value) {
|
|
245
243
|
checkFields(value, relationFields[table]);
|
|
246
|
-
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* The row after a patch. An atomic field merges into its current value, so a partial patch keeps
|
|
247
|
+
* the rest of it; an unreadable stored value is replaced instead, so writing a whole value repairs it.
|
|
248
|
+
*/
|
|
249
|
+
function patchRow(table, current, patch) {
|
|
250
|
+
const next = mergeFields(current, patch);
|
|
251
|
+
for (const key of atomicFields[table] ?? []) {
|
|
252
|
+
const old = current[key];
|
|
253
|
+
const value = patch[key];
|
|
254
|
+
if (value !== void 0 && isRecord(old) && old.invalid === true) next[key] = isRecord(value) ? mergeFields({}, value) : value;
|
|
255
|
+
}
|
|
256
|
+
return next;
|
|
247
257
|
}
|
|
248
258
|
/** Undefined deletes; nested patches retain sibling fields and an existing empty style Map. */
|
|
249
259
|
function mergeFields(target, patch) {
|
|
250
260
|
const result = { ...target };
|
|
251
261
|
for (const [key, value] of Object.entries(patch)) {
|
|
252
|
-
if (
|
|
253
|
-
"$cid",
|
|
254
|
-
"__proto__",
|
|
255
|
-
"constructor",
|
|
256
|
-
"prototype"
|
|
257
|
-
].includes(key)) throw new Error(`Reserved field: ${key}`);
|
|
262
|
+
if (reservedIds.includes(key)) throw new Error(`Reserved field: ${key}`);
|
|
258
263
|
if (value === void 0) delete result[key];
|
|
259
264
|
else result[key] = isRecord(value) ? mergeFields(isRecord(result[key]) ? result[key] : {}, value) : value;
|
|
260
265
|
}
|
|
@@ -1252,23 +1257,23 @@ function codec(encode, decode) {
|
|
|
1252
1257
|
}
|
|
1253
1258
|
};
|
|
1254
1259
|
}
|
|
1260
|
+
/** A Clip's time anchor; the Clip holding it is the anchored end. */
|
|
1255
1261
|
const timeAnchorCodec = codec((v) => {
|
|
1256
|
-
|
|
1262
|
+
assertTimeAnchor(v);
|
|
1257
1263
|
return JSON.stringify({
|
|
1258
|
-
from: v.from,
|
|
1259
1264
|
to: v.to,
|
|
1260
1265
|
offsetMs: v.offsetMs,
|
|
1261
1266
|
fallback_abs_ms: v.fallback_abs_ms
|
|
1262
1267
|
});
|
|
1263
1268
|
}, (raw) => {
|
|
1264
1269
|
const v = JSON.parse(raw);
|
|
1265
|
-
|
|
1270
|
+
assertTimeAnchor(v);
|
|
1266
1271
|
return v;
|
|
1267
1272
|
});
|
|
1273
|
+
/** A Caption's selection; Cursors are stored as their native bytes in unpadded base64url. */
|
|
1268
1274
|
const captionContentCodec = codec((v) => {
|
|
1269
|
-
|
|
1275
|
+
assertCaptionContent(v);
|
|
1270
1276
|
return JSON.stringify({
|
|
1271
|
-
captionId: v.captionId,
|
|
1272
1277
|
audioScriptId: v.audioScriptId,
|
|
1273
1278
|
start: bytesToBase64url(v.start.encode()),
|
|
1274
1279
|
end: bytesToBase64url(v.end.encode())
|
|
@@ -1281,9 +1286,21 @@ const captionContentCodec = codec((v) => {
|
|
|
1281
1286
|
start: Cursor.decode(base64urlToBytes(v.start)),
|
|
1282
1287
|
end: Cursor.decode(base64urlToBytes(v.end))
|
|
1283
1288
|
};
|
|
1284
|
-
|
|
1289
|
+
assertCaptionContent(value);
|
|
1285
1290
|
return value;
|
|
1286
1291
|
});
|
|
1292
|
+
/** A phonetic script's base script and pronunciation, stored as one string so they move together. */
|
|
1293
|
+
const phonemeCodec = codec((v) => {
|
|
1294
|
+
assertPhoneme(v);
|
|
1295
|
+
return JSON.stringify({
|
|
1296
|
+
audioScriptId: v.audioScriptId,
|
|
1297
|
+
...v.phonemeScript === void 0 ? {} : { phonemeScript: v.phonemeScript }
|
|
1298
|
+
});
|
|
1299
|
+
}, (raw) => {
|
|
1300
|
+
const v = JSON.parse(raw);
|
|
1301
|
+
assertPhoneme(v);
|
|
1302
|
+
return v;
|
|
1303
|
+
});
|
|
1287
1304
|
/** A Clip's sequence marker is stored as one string, so it is taken or replaced whole. */
|
|
1288
1305
|
const seqMarkerCodec = codec((v) => {
|
|
1289
1306
|
assertSeqMarker(v);
|
|
@@ -1304,13 +1321,19 @@ const optional = { required: false };
|
|
|
1304
1321
|
const mergeable = { mergeableMapChildContainers: true };
|
|
1305
1322
|
const strings = (keys) => Object.fromEntries(keys.map((key) => [key, schema.String()]));
|
|
1306
1323
|
const relation = (keys) => schema.LoroMapRecord(schema.LoroMap(strings(keys)), mergeable);
|
|
1307
|
-
const
|
|
1324
|
+
const transform = (codec) => ({
|
|
1308
1325
|
...codec,
|
|
1309
1326
|
isEqual: "encoded-value-equality",
|
|
1310
1327
|
validateEncodedType: true
|
|
1311
1328
|
});
|
|
1312
|
-
|
|
1313
|
-
|
|
1329
|
+
/** One field stored as one encoded string, so it is taken or replaced whole. */
|
|
1330
|
+
const atomicValue = (codec) => schema.String().transform(transform(codec));
|
|
1331
|
+
const optionalAtomicValue = (codec) => schema.String(optional).transform(transform(codec));
|
|
1332
|
+
/**
|
|
1333
|
+
* Only direct children inherit mergeable identity. Body and ordinary entity rows keep normal
|
|
1334
|
+
* identities, so deleting a row also drops what a peer wrote into it concurrently: a relation an
|
|
1335
|
+
* entity holds at most one of is a field of that row and never outlives it.
|
|
1336
|
+
*/
|
|
1314
1337
|
const medeoDslLoroShape = schema({
|
|
1315
1338
|
"timeline-entities": schema.LoroMapRecord(schema.LoroMap({})),
|
|
1316
1339
|
"track-entities": schema.LoroMapRecord(schema.LoroMap({
|
|
@@ -1319,7 +1342,9 @@ const medeoDslLoroShape = schema({
|
|
|
1319
1342
|
})),
|
|
1320
1343
|
"clip-entities": schema.LoroMapRecord(schema.LoroMap({
|
|
1321
1344
|
seqMarker: atomicValue(seqMarkerCodec),
|
|
1322
|
-
volume: schema.Number(optional)
|
|
1345
|
+
volume: schema.Number(optional),
|
|
1346
|
+
source: schema.String(),
|
|
1347
|
+
anchor: optionalAtomicValue(timeAnchorCodec)
|
|
1323
1348
|
})),
|
|
1324
1349
|
"media-entities": schema.LoroMapRecord(schema.LoroMap({
|
|
1325
1350
|
kind: schema.String(),
|
|
@@ -1333,7 +1358,9 @@ const medeoDslLoroShape = schema({
|
|
|
1333
1358
|
body: schema.LoroText(),
|
|
1334
1359
|
defaultLanguage: schema.String(optional)
|
|
1335
1360
|
})),
|
|
1361
|
+
"phonetic-script-entities": schema.LoroMapRecord(schema.LoroMap({ phoneme: atomicValue(phonemeCodec) })),
|
|
1336
1362
|
"caption-entities": schema.LoroMapRecord(schema.LoroMap({
|
|
1363
|
+
selection: optionalAtomicValue(captionContentCodec),
|
|
1337
1364
|
text: schema.LoroText(optional),
|
|
1338
1365
|
style: schema.LoroMap({
|
|
1339
1366
|
font: schema.LoroMap({
|
|
@@ -1359,26 +1386,9 @@ const medeoDslLoroShape = schema({
|
|
|
1359
1386
|
key: schema.String(),
|
|
1360
1387
|
name: schema.String(optional)
|
|
1361
1388
|
}), mergeable),
|
|
1362
|
-
"asset-entities": schema.LoroMapRecord(schema.LoroMap({
|
|
1363
|
-
system: schema.String(),
|
|
1364
|
-
key: schema.String(),
|
|
1365
|
-
storageKey: schema.String(optional),
|
|
1366
|
-
digest: schema.String(optional)
|
|
1367
|
-
}), mergeable),
|
|
1368
1389
|
"contain-relations": schema.LoroTree(schema.LoroMap({ entityId: schema.String() })),
|
|
1369
|
-
"clip-source-relations": relation(["clipId", "sourceId"]),
|
|
1370
|
-
"time-anchor-relations": atomic(timeAnchorCodec),
|
|
1371
|
-
"caption-content-relations": atomic(captionContentCodec),
|
|
1372
|
-
"caption-alignment-relations": schema.LoroMapRecord(schema.LoroMap({
|
|
1373
|
-
captionId: schema.String(),
|
|
1374
|
-
mediaId: schema.String(),
|
|
1375
|
-
startMs: schema.Number(),
|
|
1376
|
-
endMs: schema.Number()
|
|
1377
|
-
}), mergeable),
|
|
1378
1390
|
"voice-timbre-relations": relation(["recordingId", "voiceId"]),
|
|
1379
|
-
"derived-from-relations": relation(["entityId", "sourceId"])
|
|
1380
|
-
"from-asset-relations": relation(["audioScriptId", "assetId"]),
|
|
1381
|
-
"to-asset-relations": relation(["audioScriptId", "assetId"])
|
|
1391
|
+
"derived-from-relations": relation(["entityId", "sourceId"])
|
|
1382
1392
|
});
|
|
1383
1393
|
//#endregion
|
|
1384
1394
|
//#region ../medeo-dsl/src/schema/document-format.ts
|
|
@@ -1407,13 +1417,10 @@ function assertDslDocument(doc) {
|
|
|
1407
1417
|
}
|
|
1408
1418
|
//#endregion
|
|
1409
1419
|
//#region ../medeo-dsl/src/schema/text-styles.ts
|
|
1410
|
-
/** How rich-text marks extend at their edges: a language runs on into
|
|
1411
|
-
const textStyles = {
|
|
1412
|
-
language: { expand: "after" },
|
|
1413
|
-
pauseMs: { expand: "none" }
|
|
1414
|
-
};
|
|
1420
|
+
/** How rich-text marks extend at their edges: a language runs on into text typed after it. */
|
|
1421
|
+
const textStyles = { language: { expand: "after" } };
|
|
1415
1422
|
//#endregion
|
|
1416
1423
|
//#region src/dsl/projection/video-draft-types.ts
|
|
1417
1424
|
const DEFAULT_UNIT_TIME_MS = 33.333;
|
|
1418
1425
|
//#endregion
|
|
1419
|
-
export {
|
|
1426
|
+
export { assertId as $, hardenCidDescriptors as A, safeStringify as B, applySchemaToInferOptions as C, deepEqual as D, decodeNestedJsonValues as E, isObject as F, valuesEqual as G, stripUndefined as H, isObjectLike as I, getChildSchema as J, CID_KEY as K, isStateAndSchemaOfType as L, inferContainerTypeFromValue as M, insertChildToMap as N, defineCidProperty as O, isArrayLike as P, assertEntityPatch as Q, isTreeID as R, applyEncode as S, containerIdToContainerType as T, tryInferContainerType as U, schemaToContainerType as V, tryUpdateToContainer as W, assertCaptionContent as X, getMapFieldSchema as Y, assertEntity as Z, isLoroTreeSchema as _, documentFormat as a, isRecord as at, validateSchema as b, isInvalidAtomic as c, relationTables as ct, getDefaultValue as d, assertResourceIdentity as dt, assertPhoneme as et, isContainerSchema as f, defaultIdFactory as ft, isLoroMovableListSchema as g, isLoroMapSchema as h, tupleHash as ht, assertDslDocument as i, entityTables as it, hasTransform as j, getRootContainerByType as k, timeAnchorCodec as l, validLanguage as lt, isLoroListSchema as m, resourceId as mt, textStyles as n, assertTimeAnchor as nt, medeoDslLoroShape as o, mergeFields as ot, isListLikeSchema as p, relationId as pt, getChildContainerSchema as q, UnsupportedDocumentFormatError as r, atomicFields as rt, captionContentCodec as s, patchRow as st, DEFAULT_UNIT_TIME_MS as t, assertRelation as tt, schema as u, assertRelationIdentity as ut, isRootSchemaType as v, cidsEqual as w, applyDecode as x, schemaContainsIgnoreField as y, isValueOfContainerType as z };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mengine/medeo-client",
|
|
3
|
-
"version": "2.1.1-dsl.
|
|
3
|
+
"version": "2.1.1-dsl.3",
|
|
4
4
|
"license": "UNLICENSED",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -29,9 +29,9 @@
|
|
|
29
29
|
"@noble/hashes": "^2.4.0",
|
|
30
30
|
"immer": "^10.2.0",
|
|
31
31
|
"zod": "^4.4.3",
|
|
32
|
-
"@mengine/storage": "2.1.1-dsl.
|
|
33
|
-
"@mengine/sync": "2.1.1-dsl.
|
|
34
|
-
"@mengine/utils": "2.1.1-dsl.
|
|
32
|
+
"@mengine/storage": "2.1.1-dsl.3",
|
|
33
|
+
"@mengine/sync": "2.1.1-dsl.3",
|
|
34
|
+
"@mengine/utils": "2.1.1-dsl.3"
|
|
35
35
|
},
|
|
36
36
|
"devDependencies": {
|
|
37
37
|
"@types/node": "^25.9.1",
|
|
@@ -42,8 +42,8 @@
|
|
|
42
42
|
"typescript": "^6.0.3",
|
|
43
43
|
"vite-plugin-wasm": "^3.6.0",
|
|
44
44
|
"vite-plus": "^0.1.23",
|
|
45
|
-
"@mengine/
|
|
46
|
-
"@mengine/
|
|
45
|
+
"@mengine/medeo-dsl": "0.0.0",
|
|
46
|
+
"@mengine/idl-codegen": "0.0.0"
|
|
47
47
|
},
|
|
48
48
|
"peerDependencies": {
|
|
49
49
|
"loro-crdt": "^1.16.1"
|