@mengine/medeo-client 2.1.1-dsl.2 → 2.1.1-dsl.20
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 +13 -12
- package/dist/{dsl-EV6kCdr6.js → dsl-wdel0lX7.js} +1125 -531
- package/dist/dsl.d.ts +3 -3
- package/dist/dsl.js +3 -3
- package/dist/{index-D-6uebu4.d.ts → index-Bt3RhxV4.d.ts} +59 -56
- package/dist/{index-Cu7SdKN5.d.ts → index-CTai9I2R.d.ts} +208 -171
- package/dist/{index-D-3rdfUP.d.ts → index-CwZ0zt-E.d.ts} +1 -1
- package/dist/index.d.ts +5 -5
- package/dist/index.js +13 -7
- package/dist/legacy.d.ts +2 -2
- package/dist/legacy.js +1 -1
- package/dist/testing.d.ts +2 -2
- package/dist/testing.js +4 -7
- package/dist/{video-draft-types-c_shaRyq.d.ts → video-draft-types-BOdawmig.d.ts} +126 -55
- package/dist/{video-draft-types-TXRv3wS6.js → video-draft-types-DKo53mZ8.js} +212 -113
- package/package.json +4 -4
|
@@ -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,51 @@ 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 selectionFields = {
|
|
135
|
+
audioScriptId: required(id),
|
|
136
|
+
segmentId: required(id),
|
|
137
|
+
start: optional$1(cursor),
|
|
138
|
+
end: optional$1(cursor)
|
|
139
|
+
};
|
|
140
|
+
/** A whole segment, or a run inside it: both ends or neither. */
|
|
141
|
+
const segmentSelection = (fields) => (v) => object(fields)(v) && v.start === void 0 === (v.end === void 0);
|
|
142
|
+
function assertCaptionContent(value) {
|
|
143
|
+
if (!segmentSelection(selectionFields)(value)) throw new Error("Invalid segment selection");
|
|
144
|
+
}
|
|
145
|
+
const phonemeFields = {
|
|
146
|
+
...selectionFields,
|
|
147
|
+
phonemeScript: optional$1(string)
|
|
148
|
+
};
|
|
149
|
+
function assertPhoneme(value) {
|
|
150
|
+
if (!segmentSelection(phonemeFields)(value)) throw new Error("Invalid phoneme");
|
|
151
|
+
}
|
|
152
|
+
const segmentFields = {
|
|
153
|
+
id: required(id),
|
|
154
|
+
text: required(string),
|
|
155
|
+
startMs: optional$1(nonnegative),
|
|
156
|
+
endMs: optional$1(nonnegative)
|
|
157
|
+
};
|
|
158
|
+
/** A segment's time in the medium it was read off: both ends or neither, the end not before the start. */
|
|
159
|
+
const segment = (v) => {
|
|
160
|
+
if (!object(segmentFields)(v)) return false;
|
|
161
|
+
const { startMs, endMs } = v;
|
|
162
|
+
return startMs === void 0 ? endMs === void 0 : endMs !== void 0 && endMs >= startMs;
|
|
163
|
+
};
|
|
164
|
+
/** One segment as a script stores it; a segment added on its own is checked here, not with its row. */
|
|
165
|
+
function assertScriptSegment(value) {
|
|
166
|
+
if (!segment(value)) throw new Error("A segment is { id, text } with startMs and endMs both or neither, endMs not before startMs");
|
|
167
|
+
}
|
|
168
|
+
/** A script's segments: each with its own id, unique within the script. */
|
|
169
|
+
const segments = (v) => Array.isArray(v) && v.every(segment) && new Set(v.map((s) => s.id)).size === v.length;
|
|
129
170
|
const locator = {
|
|
130
171
|
system: required(oneOf("memota", "memota-speech")),
|
|
131
172
|
key: required(nonempty)
|
|
@@ -138,7 +179,9 @@ const entityFields = {
|
|
|
138
179
|
},
|
|
139
180
|
"clip-entities": {
|
|
140
181
|
seqMarker: required(object(seqMarkerFields)),
|
|
141
|
-
volume: optional$1(finite)
|
|
182
|
+
volume: optional$1(finite),
|
|
183
|
+
source: required(id),
|
|
184
|
+
anchor: optional$1(object(timeAnchorFields))
|
|
142
185
|
},
|
|
143
186
|
"media-entities": {
|
|
144
187
|
...locator,
|
|
@@ -148,10 +191,22 @@ const entityFields = {
|
|
|
148
191
|
digest: optional$1(nonempty)
|
|
149
192
|
},
|
|
150
193
|
"audio-script-entities": {
|
|
151
|
-
|
|
152
|
-
defaultLanguage: optional$1(validLanguage)
|
|
194
|
+
segments: required(segments),
|
|
195
|
+
defaultLanguage: optional$1(validLanguage),
|
|
196
|
+
system: optional$1(oneOf("memota")),
|
|
197
|
+
key: optional$1(nonempty),
|
|
198
|
+
storageKey: optional$1(nonempty),
|
|
199
|
+
digest: optional$1(nonempty)
|
|
200
|
+
},
|
|
201
|
+
"phonetic-script-entities": {
|
|
202
|
+
phoneme: optional$1(segmentSelection(phonemeFields)),
|
|
203
|
+
system: optional$1(oneOf("memota")),
|
|
204
|
+
key: optional$1(nonempty),
|
|
205
|
+
storageKey: optional$1(nonempty),
|
|
206
|
+
digest: optional$1(nonempty)
|
|
153
207
|
},
|
|
154
208
|
"caption-entities": {
|
|
209
|
+
selection: optional$1(segmentSelection(selectionFields)),
|
|
155
210
|
text: optional$1(string),
|
|
156
211
|
style: optional$1(object(styleFields))
|
|
157
212
|
},
|
|
@@ -159,57 +214,57 @@ const entityFields = {
|
|
|
159
214
|
system: required(oneOf("voice-library")),
|
|
160
215
|
key: required(nonempty),
|
|
161
216
|
name: optional$1(string)
|
|
162
|
-
},
|
|
163
|
-
"asset-entities": {
|
|
164
|
-
...locator,
|
|
165
|
-
storageKey: optional$1(nonempty),
|
|
166
|
-
digest: optional$1(nonempty)
|
|
167
217
|
}
|
|
168
218
|
};
|
|
169
219
|
const entityTables = Object.keys(entityFields);
|
|
220
|
+
/**
|
|
221
|
+
* Fields stored as one encoded string: written whole, never merged field by field. A patch to one
|
|
222
|
+
* is merged into the current value first, then the whole value is written.
|
|
223
|
+
*/
|
|
224
|
+
const atomicFields = {
|
|
225
|
+
"clip-entities": ["seqMarker", "anchor"],
|
|
226
|
+
"phonetic-script-entities": ["phoneme"],
|
|
227
|
+
"caption-entities": ["selection"]
|
|
228
|
+
};
|
|
170
229
|
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
230
|
"voice-timbre-relations": {
|
|
194
|
-
recordingId: required(
|
|
195
|
-
voiceId: required(
|
|
231
|
+
recordingId: required(id),
|
|
232
|
+
voiceId: required(id)
|
|
196
233
|
},
|
|
197
234
|
"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)
|
|
235
|
+
entityId: required(id),
|
|
236
|
+
sourceId: required(id),
|
|
237
|
+
sourceDigest: optional$1(nonempty)
|
|
208
238
|
}
|
|
209
239
|
};
|
|
210
240
|
const relationTables = Object.keys(relationFields);
|
|
241
|
+
const fileFields = [
|
|
242
|
+
"system",
|
|
243
|
+
"key",
|
|
244
|
+
"storageKey",
|
|
245
|
+
"digest"
|
|
246
|
+
];
|
|
247
|
+
const phonemeScriptOf = (row) => isRecord(row.phoneme) ? row.phoneme.phonemeScript : void 0;
|
|
248
|
+
/**
|
|
249
|
+
* A PhoneticScript as a write leaves it: written for a segment (D181), and its phonemeScript is raw
|
|
250
|
+
* content stored as its file (D178) — with text the row names that file and its digest, without
|
|
251
|
+
* text it has no file, and rewritten text is a new file.
|
|
252
|
+
*/
|
|
253
|
+
function assertPhoneticScript(before, after) {
|
|
254
|
+
if (!isRecord(after.phoneme)) throw new Error("A PhoneticScript is written for a segment of its base script");
|
|
255
|
+
if (phonemeScriptOf(after) === void 0) {
|
|
256
|
+
if (fileFields.some((field) => after[field] !== void 0)) throw new Error("A PhoneticScript without text has no file");
|
|
257
|
+
} else if (after.system === void 0 || after.key === void 0 || after.digest === void 0) throw new Error("A PhoneticScript stores its phonemeScript as a file: name it by system and key with its digest");
|
|
258
|
+
if (before !== void 0 && phonemeScriptOf(after) !== phonemeScriptOf(before) && after.digest !== void 0 && after.digest === before.digest) throw new Error("A rewritten phonemeScript is stored again: write its new file");
|
|
259
|
+
}
|
|
260
|
+
/** A transcript names the file it was read from by system and key together; its facts come with that name. */
|
|
261
|
+
function assertTranscriptFile(row) {
|
|
262
|
+
if (row.system === void 0 !== (row.key === void 0)) throw new Error("A transcript names its transcription file by system and key");
|
|
263
|
+
if (row.key === void 0 && (row.storageKey !== void 0 || row.digest !== void 0)) throw new Error("A script without a transcription file has no file facts");
|
|
264
|
+
}
|
|
211
265
|
function assertEntity(table, value) {
|
|
212
266
|
checkFields(value, entityFields[table]);
|
|
267
|
+
if (table === "audio-script-entities") assertTranscriptFile(value);
|
|
213
268
|
if (table === "media-entities") {
|
|
214
269
|
if (value.kind === "image" && value.durationMs !== void 0) throw new Error("Image has no intrinsic duration");
|
|
215
270
|
if (value.kind !== "image" && !positive(value.durationMs)) throw new Error("Finite media requires durationMs");
|
|
@@ -219,15 +274,19 @@ function assertEntity(table, value) {
|
|
|
219
274
|
/** Validate only touched fields, so an unrelated edit can proceed in a merged, invalid document. */
|
|
220
275
|
function assertEntityPatch(table, patch, current) {
|
|
221
276
|
if (!isRecord(patch)) throw new Error("Expected an entity patch");
|
|
222
|
-
if (table === "audio-script-entities" && Object.hasOwn(patch, "
|
|
277
|
+
if (table === "audio-script-entities" && Object.hasOwn(patch, "segments")) throw new Error("Change segments with segment mutations and edit their text through the Text adapter");
|
|
223
278
|
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
279
|
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 =
|
|
280
|
+
const next = patchRow(table, current, patch);
|
|
226
281
|
for (const key of Object.keys(patch)) {
|
|
227
282
|
const field = entityFields[table][key];
|
|
228
283
|
if (!field || !field.optional && next[key] === void 0 || next[key] !== void 0 && !field.check(next[key])) throw new Error(`Invalid patch field: ${key}`);
|
|
229
284
|
}
|
|
230
|
-
if (table === "
|
|
285
|
+
if (table === "audio-script-entities" && fileFields.some((field) => Object.hasOwn(patch, field))) {
|
|
286
|
+
if (current.key !== void 0 && (next.system !== current.system || next.key !== current.key)) throw new Error("A transcript's transcription file is fixed once named");
|
|
287
|
+
assertTranscriptFile(next);
|
|
288
|
+
}
|
|
289
|
+
if (table === "media-entities" || table === "voice-entities") {
|
|
231
290
|
for (const key of [
|
|
232
291
|
"system",
|
|
233
292
|
"key",
|
|
@@ -243,18 +302,25 @@ function assertEntityPatch(table, patch, current) {
|
|
|
243
302
|
}
|
|
244
303
|
function assertRelation(table, value) {
|
|
245
304
|
checkFields(value, relationFields[table]);
|
|
246
|
-
|
|
305
|
+
}
|
|
306
|
+
/**
|
|
307
|
+
* The row after a patch. An atomic field merges into its current value, so a partial patch keeps
|
|
308
|
+
* the rest of it; an unreadable stored value is replaced instead, so writing a whole value repairs it.
|
|
309
|
+
*/
|
|
310
|
+
function patchRow(table, current, patch) {
|
|
311
|
+
const next = mergeFields(current, patch);
|
|
312
|
+
for (const key of atomicFields[table] ?? []) {
|
|
313
|
+
const old = current[key];
|
|
314
|
+
const value = patch[key];
|
|
315
|
+
if (value !== void 0 && isRecord(old) && old.invalid === true) next[key] = isRecord(value) ? mergeFields({}, value) : value;
|
|
316
|
+
}
|
|
317
|
+
return next;
|
|
247
318
|
}
|
|
248
319
|
/** Undefined deletes; nested patches retain sibling fields and an existing empty style Map. */
|
|
249
320
|
function mergeFields(target, patch) {
|
|
250
321
|
const result = { ...target };
|
|
251
322
|
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}`);
|
|
323
|
+
if (reservedIds.includes(key)) throw new Error(`Reserved field: ${key}`);
|
|
258
324
|
if (value === void 0) delete result[key];
|
|
259
325
|
else result[key] = isRecord(value) ? mergeFields(isRecord(result[key]) ? result[key] : {}, value) : value;
|
|
260
326
|
}
|
|
@@ -1252,36 +1318,60 @@ function codec(encode, decode) {
|
|
|
1252
1318
|
}
|
|
1253
1319
|
};
|
|
1254
1320
|
}
|
|
1321
|
+
/** A Clip's time anchor; the Clip holding it is the anchored end. */
|
|
1255
1322
|
const timeAnchorCodec = codec((v) => {
|
|
1256
|
-
|
|
1323
|
+
assertTimeAnchor(v);
|
|
1257
1324
|
return JSON.stringify({
|
|
1258
|
-
from: v.from,
|
|
1259
1325
|
to: v.to,
|
|
1260
1326
|
offsetMs: v.offsetMs,
|
|
1261
1327
|
fallback_abs_ms: v.fallback_abs_ms
|
|
1262
1328
|
});
|
|
1263
1329
|
}, (raw) => {
|
|
1264
1330
|
const v = JSON.parse(raw);
|
|
1265
|
-
|
|
1331
|
+
assertTimeAnchor(v);
|
|
1266
1332
|
return v;
|
|
1267
1333
|
});
|
|
1334
|
+
/** A segment selection as stored: a run's Cursors as their native bytes in unpadded base64url. */
|
|
1335
|
+
function encodeSelection(v) {
|
|
1336
|
+
return {
|
|
1337
|
+
audioScriptId: v.audioScriptId,
|
|
1338
|
+
segmentId: v.segmentId,
|
|
1339
|
+
...v.start === void 0 || v.end === void 0 ? {} : {
|
|
1340
|
+
start: bytesToBase64url(v.start.encode()),
|
|
1341
|
+
end: bytesToBase64url(v.end.encode())
|
|
1342
|
+
}
|
|
1343
|
+
};
|
|
1344
|
+
}
|
|
1345
|
+
function decodeSelection(v) {
|
|
1346
|
+
if (!v || typeof v !== "object") throw new Error("Expected a segment selection");
|
|
1347
|
+
const { start, end, ...rest } = v;
|
|
1348
|
+
if (start === void 0 && end === void 0) return rest;
|
|
1349
|
+
if (typeof start !== "string" || typeof end !== "string") throw new Error("Expected encoded Cursor pair");
|
|
1350
|
+
return {
|
|
1351
|
+
...rest,
|
|
1352
|
+
start: Cursor.decode(base64urlToBytes(start)),
|
|
1353
|
+
end: Cursor.decode(base64urlToBytes(end))
|
|
1354
|
+
};
|
|
1355
|
+
}
|
|
1356
|
+
/** A Caption's selection. */
|
|
1268
1357
|
const captionContentCodec = codec((v) => {
|
|
1269
|
-
|
|
1358
|
+
assertCaptionContent(v);
|
|
1359
|
+
return JSON.stringify(encodeSelection(v));
|
|
1360
|
+
}, (raw) => {
|
|
1361
|
+
const value = decodeSelection(JSON.parse(raw));
|
|
1362
|
+
assertCaptionContent(value);
|
|
1363
|
+
return value;
|
|
1364
|
+
});
|
|
1365
|
+
/** A PhoneticScript's selection and what to synthesize for it, stored as one string so they move together. */
|
|
1366
|
+
const phonemeCodec = codec((v) => {
|
|
1367
|
+
assertPhoneme(v);
|
|
1270
1368
|
return JSON.stringify({
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
start: bytesToBase64url(v.start.encode()),
|
|
1274
|
-
end: bytesToBase64url(v.end.encode())
|
|
1369
|
+
...encodeSelection(v),
|
|
1370
|
+
...v.phonemeScript === void 0 ? {} : { phonemeScript: v.phonemeScript }
|
|
1275
1371
|
});
|
|
1276
1372
|
}, (raw) => {
|
|
1277
|
-
const
|
|
1278
|
-
|
|
1279
|
-
const value = {
|
|
1280
|
-
...v,
|
|
1281
|
-
start: Cursor.decode(base64urlToBytes(v.start)),
|
|
1282
|
-
end: Cursor.decode(base64urlToBytes(v.end))
|
|
1283
|
-
};
|
|
1284
|
-
assertRelation("caption-content-relations", value);
|
|
1373
|
+
const value = decodeSelection(JSON.parse(raw));
|
|
1374
|
+
assertPhoneme(value);
|
|
1285
1375
|
return value;
|
|
1286
1376
|
});
|
|
1287
1377
|
/** A Clip's sequence marker is stored as one string, so it is taken or replaced whole. */
|
|
@@ -1304,13 +1394,19 @@ const optional = { required: false };
|
|
|
1304
1394
|
const mergeable = { mergeableMapChildContainers: true };
|
|
1305
1395
|
const strings = (keys) => Object.fromEntries(keys.map((key) => [key, schema.String()]));
|
|
1306
1396
|
const relation = (keys) => schema.LoroMapRecord(schema.LoroMap(strings(keys)), mergeable);
|
|
1307
|
-
const
|
|
1397
|
+
const transform = (codec) => ({
|
|
1308
1398
|
...codec,
|
|
1309
1399
|
isEqual: "encoded-value-equality",
|
|
1310
1400
|
validateEncodedType: true
|
|
1311
1401
|
});
|
|
1312
|
-
|
|
1313
|
-
|
|
1402
|
+
/** One field stored as one encoded string, so it is taken or replaced whole. */
|
|
1403
|
+
const atomicValue = (codec) => schema.String().transform(transform(codec));
|
|
1404
|
+
const optionalAtomicValue = (codec) => schema.String(optional).transform(transform(codec));
|
|
1405
|
+
/**
|
|
1406
|
+
* Only direct children inherit mergeable identity. Text and ordinary entity rows keep normal
|
|
1407
|
+
* identities, so deleting a row also drops what a peer wrote into it concurrently: a relation an
|
|
1408
|
+
* entity holds at most one of is a field of that row and never outlives it.
|
|
1409
|
+
*/
|
|
1314
1410
|
const medeoDslLoroShape = schema({
|
|
1315
1411
|
"timeline-entities": schema.LoroMapRecord(schema.LoroMap({})),
|
|
1316
1412
|
"track-entities": schema.LoroMapRecord(schema.LoroMap({
|
|
@@ -1319,7 +1415,9 @@ const medeoDslLoroShape = schema({
|
|
|
1319
1415
|
})),
|
|
1320
1416
|
"clip-entities": schema.LoroMapRecord(schema.LoroMap({
|
|
1321
1417
|
seqMarker: atomicValue(seqMarkerCodec),
|
|
1322
|
-
volume: schema.Number(optional)
|
|
1418
|
+
volume: schema.Number(optional),
|
|
1419
|
+
source: schema.String(),
|
|
1420
|
+
anchor: optionalAtomicValue(timeAnchorCodec)
|
|
1323
1421
|
})),
|
|
1324
1422
|
"media-entities": schema.LoroMapRecord(schema.LoroMap({
|
|
1325
1423
|
kind: schema.String(),
|
|
@@ -1330,10 +1428,27 @@ const medeoDslLoroShape = schema({
|
|
|
1330
1428
|
digest: schema.String(optional)
|
|
1331
1429
|
}), mergeable),
|
|
1332
1430
|
"audio-script-entities": schema.LoroMapRecord(schema.LoroMap({
|
|
1333
|
-
|
|
1334
|
-
|
|
1431
|
+
segments: schema.LoroMovableList(schema.LoroMap({
|
|
1432
|
+
id: schema.String(),
|
|
1433
|
+
text: schema.LoroText(),
|
|
1434
|
+
startMs: schema.Number(optional),
|
|
1435
|
+
endMs: schema.Number(optional)
|
|
1436
|
+
}), (segment) => segment.id),
|
|
1437
|
+
defaultLanguage: schema.String(optional),
|
|
1438
|
+
system: schema.String(optional),
|
|
1439
|
+
key: schema.String(optional),
|
|
1440
|
+
storageKey: schema.String(optional),
|
|
1441
|
+
digest: schema.String(optional)
|
|
1442
|
+
})),
|
|
1443
|
+
"phonetic-script-entities": schema.LoroMapRecord(schema.LoroMap({
|
|
1444
|
+
phoneme: atomicValue(phonemeCodec),
|
|
1445
|
+
system: schema.String(optional),
|
|
1446
|
+
key: schema.String(optional),
|
|
1447
|
+
storageKey: schema.String(optional),
|
|
1448
|
+
digest: schema.String(optional)
|
|
1335
1449
|
})),
|
|
1336
1450
|
"caption-entities": schema.LoroMapRecord(schema.LoroMap({
|
|
1451
|
+
selection: optionalAtomicValue(captionContentCodec),
|
|
1337
1452
|
text: schema.LoroText(optional),
|
|
1338
1453
|
style: schema.LoroMap({
|
|
1339
1454
|
font: schema.LoroMap({
|
|
@@ -1359,26 +1474,13 @@ const medeoDslLoroShape = schema({
|
|
|
1359
1474
|
key: schema.String(),
|
|
1360
1475
|
name: schema.String(optional)
|
|
1361
1476
|
}), 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
1477
|
"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
1478
|
"voice-timbre-relations": relation(["recordingId", "voiceId"]),
|
|
1379
|
-
"derived-from-relations":
|
|
1380
|
-
|
|
1381
|
-
|
|
1479
|
+
"derived-from-relations": schema.LoroMapRecord(schema.LoroMap({
|
|
1480
|
+
entityId: schema.String(),
|
|
1481
|
+
sourceId: schema.String(),
|
|
1482
|
+
sourceDigest: schema.String(optional)
|
|
1483
|
+
}), mergeable)
|
|
1382
1484
|
});
|
|
1383
1485
|
//#endregion
|
|
1384
1486
|
//#region ../medeo-dsl/src/schema/document-format.ts
|
|
@@ -1407,13 +1509,10 @@ function assertDslDocument(doc) {
|
|
|
1407
1509
|
}
|
|
1408
1510
|
//#endregion
|
|
1409
1511
|
//#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
|
-
};
|
|
1512
|
+
/** How rich-text marks extend at their edges: a language runs on into text typed after it. */
|
|
1513
|
+
const textStyles = { language: { expand: "after" } };
|
|
1415
1514
|
//#endregion
|
|
1416
1515
|
//#region src/dsl/projection/video-draft-types.ts
|
|
1417
1516
|
const DEFAULT_UNIT_TIME_MS = 33.333;
|
|
1418
1517
|
//#endregion
|
|
1419
|
-
export {
|
|
1518
|
+
export { assertEntityPatch as $, getRootContainerByType as A, isValueOfContainerType as B, applyEncode as C, decodeNestedJsonValues as D, containerIdToContainerType as E, isArrayLike as F, tryUpdateToContainer as G, schemaToContainerType as H, isObject as I, getChildContainerSchema as J, valuesEqual as K, isObjectLike as L, hasTransform as M, inferContainerTypeFromValue as N, deepEqual as O, insertChildToMap as P, assertEntity as Q, isStateAndSchemaOfType as R, applyDecode as S, cidsEqual as T, stripUndefined as U, safeStringify as V, tryInferContainerType as W, getMapFieldSchema as X, getChildSchema as Y, assertCaptionContent as Z, isLoroMovableListSchema as _, resourceId as _t, documentFormat as a, assertTimeAnchor as at, schemaContainsIgnoreField as b, isInvalidAtomic as c, isRecord as ct, schema as d, relationTables as dt, assertId as et, getDefaultValue as f, validLanguage as ft, isLoroMapSchema as g, relationId as gt, isLoroListSchema as h, defaultIdFactory as ht, assertDslDocument as i, assertScriptSegment as it, hardenCidDescriptors as j, defineCidProperty as k, phonemeCodec as l, mergeFields as lt, isListLikeSchema as m, assertResourceIdentity as mt, textStyles as n, assertPhoneticScript as nt, medeoDslLoroShape as o, atomicFields as ot, isContainerSchema as p, assertRelationIdentity as pt, CID_KEY as q, UnsupportedDocumentFormatError as r, assertRelation as rt, captionContentCodec as s, entityTables as st, DEFAULT_UNIT_TIME_MS as t, assertPhoneme as tt, timeAnchorCodec as u, patchRow as ut, isLoroTreeSchema as v, tupleHash as vt, applySchemaToInferOptions as w, validateSchema as x, isRootSchemaType as y, isTreeID 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.20",
|
|
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.20",
|
|
33
|
+
"@mengine/sync": "2.1.1-dsl.20",
|
|
34
|
+
"@mengine/utils": "2.1.1-dsl.20"
|
|
35
35
|
},
|
|
36
36
|
"devDependencies": {
|
|
37
37
|
"@types/node": "^25.9.1",
|