@mengine/medeo-client 3.0.0 → 3.0.1-dsl.0

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 CHANGED
@@ -163,3 +163,7 @@ Caption text is matched forward in `speech.caption_ids` order. A failed exact ma
163
163
  `/relay` and `/testing` remain host/test-only entrypoints. The private DSL package is bundled into the client distribution.
164
164
 
165
165
  See [DSL index](../../docs/projects/medeo-dsl/README.md), [capability mapping and cutover](../../docs/projects/medeo-dsl/rfc/03-editor-and-document-cutover.md) and [known limits](../../docs/projects/medeo-dsl/known-unfixed.md). K04 audit segmentation is unchanged; one native commit can contain multiple Changes.
166
+
167
+ The row-owned atomic binding stores the selection, pronunciation and file facts together. Public Action methods and domain snapshots retain their shape; the native Loro field layout requires matching SDK/server versions. Old field-per-fact DSL documents require explicit migration and are refused by the new reader and relay. See [the cutover contract](../../docs/projects/medeo-dsl/rfc/06-phonetic-binding-cutover.md).
168
+
169
+ The public `/schemas` subpath retains the input contracts used by FE semantic-op dispatch. These live in `src/schemas` and do not expose a retired editor or storage writer (D201).
@@ -145,8 +145,12 @@ function assertCaptionContent(value) {
145
145
  }
146
146
  const phonemeFields = {
147
147
  ...selectionFields,
148
- phonemeScript: optional$1(string)
148
+ phonemeScript: optional$1(nonempty)
149
149
  };
150
+ /** D190: omitted pronunciation uses the selected source words; an authored pronunciation is never empty. */
151
+ function assertPhonemeScript(value) {
152
+ if (value !== void 0 && !nonempty(value)) throw new Error("phonemeScript must be nonempty when provided");
153
+ }
150
154
  function assertPhoneme(value) {
151
155
  if (!segmentSelection(phonemeFields)(value)) throw new Error("Invalid phoneme");
152
156
  }
@@ -638,6 +642,63 @@ function assertContainTree(rows, tree) {
638
642
  }
639
643
  }
640
644
  //#endregion
645
+ //#region ../medeo-dsl/src/clip-playback.ts
646
+ const playable = {
647
+ video_clip: ["video", "image"],
648
+ speech: ["audio", "voiceover-recording"],
649
+ caption: ["caption"],
650
+ bgm: ["audio"]
651
+ };
652
+ function supportsClipSpeed(source) {
653
+ return isRecord(source) && [
654
+ "video",
655
+ "audio",
656
+ "voiceover-recording"
657
+ ].includes(String(source.kind));
658
+ }
659
+ /** Content-kind admission is shared by Actions, script compilation and the final transaction. */
660
+ function assertClipSourceRole(role, kind) {
661
+ if (!playable[role]?.includes(kind)) throw new Error(`A ${role} Track does not play ${kind}`);
662
+ }
663
+ function assertClipWindow(window, duration) {
664
+ if (!Number.isSafeInteger(window.inMs) || !Number.isSafeInteger(window.outMs) || window.inMs < 0 || window.outMs <= window.inMs) throw new Error("Invalid Clip window");
665
+ if (duration !== void 0 && window.outMs > duration) throw new Error(`Invalid Clip window: ${window.outMs}ms is past its content's ${duration}ms`);
666
+ }
667
+ /** Check authored playback facts; a remote merge is still read by projection with diagnostics. */
668
+ function assertClipPlayback(clip, source, role) {
669
+ if (!isRecord(source)) throw new Error("Invalid Clip source");
670
+ const kind = source.kind ?? "caption";
671
+ const marker = clip.seqMarker;
672
+ assertSeqMarker(marker);
673
+ if (marker.outMs <= marker.inMs) throw new Error("Clip window must be nonempty");
674
+ if (role !== void 0) assertClipSourceRole(role, kind);
675
+ if (marker.rate !== void 0 && !supportsClipSpeed(source)) throw new Error("Only video and sound change speed");
676
+ const duration = typeof source.durationMs === "number" ? source.durationMs : void 0;
677
+ if (marker.fillTimeline) {
678
+ if (kind !== "audio") throw new Error("Only BGM audio fills the Timeline");
679
+ if (marker.inMs !== 0 || marker.rate !== void 0 && marker.rate !== 1 || marker.outMs !== duration) throw new Error(`A Clip that fills the Timeline plays its whole source at 1×: inMs 0, outMs ${duration ?? "its length"}, no rate; the Timeline's end cuts it`);
680
+ }
681
+ assertClipWindow(marker);
682
+ }
683
+ function clipParents(tree) {
684
+ const result = /* @__PURE__ */ new Map();
685
+ for (const track of tree) for (const clip of track.children ?? []) result.set(clip.entityId, track.entityId);
686
+ return result;
687
+ }
688
+ /** A new parent or Track role revalidates that binding, without blocking unrelated merged rows. */
689
+ function assertClipPlacements(before, after, previousTree, nextTree) {
690
+ const previous = clipParents(previousTree);
691
+ for (const [id, parent] of clipParents(nextTree)) {
692
+ const track = after["track-entities"][parent];
693
+ const previousTrack = before["track-entities"][parent];
694
+ if (previous.get(id) === parent && (track === void 0 || isRecord(track) && isRecord(previousTrack) && previousTrack.role === track.role)) continue;
695
+ const role = requireEntity(after, "track-entities", parent).role;
696
+ const clip = after["clip-entities"][id];
697
+ if (!clip) continue;
698
+ assertClipPlayback(clip, after["media-entities"][clip.source] ?? after["caption-entities"][clip.source], role);
699
+ }
700
+ }
701
+ //#endregion
641
702
  //#region ../medeo-dsl/src/plan.ts
642
703
  /** Include removed nested keys: a partial merge alone would conceal a deleted required field. */
643
704
  function replacementPatch(before, after) {
@@ -653,6 +714,7 @@ function replacementPatch(before, after) {
653
714
  */
654
715
  function assertChangeAdmitted(change) {
655
716
  const { before, after } = change;
717
+ const parents = change.containment ? clipParents(change.containment.after) : void 0;
656
718
  for (const [table, rows] of change.entities) for (const [id, fields] of rows) {
657
719
  const value = after[table][id];
658
720
  if (value === void 0) continue;
@@ -667,25 +729,17 @@ function assertChangeAdmitted(change) {
667
729
  }
668
730
  const row = value;
669
731
  for (const field of referenceFields[table] ?? []) if (!old || fields.has(field)) assertReferenceAdmitted(after, table, id, field, row[field]);
670
- const marker = row.seqMarker;
671
- if (table === "clip-entities" && (!old || fields.has("seqMarker")) && marker?.outMs <= marker?.inMs) throw new Error("Clip window must be nonempty");
672
- if (table === "clip-entities" && (!old || fields.has("seqMarker") || fields.has("source"))) assertFillsWithWholeSource(after, marker, row.source);
732
+ if (table === "clip-entities" && (!old || fields.has("seqMarker") || fields.has("source"))) {
733
+ const parent = parents?.get(id);
734
+ assertClipPlayback(row, after["media-entities"][row.source] ?? after["caption-entities"][row.source], parent === void 0 ? void 0 : requireEntity(after, "track-entities", parent).role);
735
+ }
673
736
  }
674
737
  if (change.entities.has("timeline-entities") && Object.keys(after["timeline-entities"]).length > 1) throw new Error("Document already has a Timeline");
675
738
  for (const [table, ids] of change.relations) for (const id of ids) {
676
739
  const value = after[table][id];
677
740
  if (value !== void 0) assertRelationAdmitted(after, table, id, value);
678
741
  }
679
- }
680
- /**
681
- * A Clip that fills the Timeline plays its whole source at 1×: the player loops a short source and
682
- * cuts a long one at the Timeline's end (RFC 02 §3.3), so a window or speed of its own has no meaning.
683
- */
684
- function assertFillsWithWholeSource(rows, marker, source) {
685
- if (marker?.fillTimeline !== true) return;
686
- const media = typeof source === "string" ? rows["media-entities"][source] : void 0;
687
- const durationMs = typeof media?.durationMs === "number" ? media.durationMs : void 0;
688
- if (marker.inMs !== 0 || marker.rate !== void 0 && marker.rate !== 1 || durationMs !== void 0 && marker.outMs !== durationMs) throw new Error(`A Clip that fills the Timeline plays its whole source at 1×: inMs 0, outMs ${durationMs ?? "its length"}, no rate; the Timeline's end cuts it`);
742
+ if (change.containment) assertClipPlacements(before, after, change.containment.before, change.containment.after);
689
743
  }
690
744
  //#endregion
691
745
  //#region src/dsl/projection/types.ts
@@ -921,15 +975,11 @@ function requireSource(s, id, role) {
921
975
  return media;
922
976
  }
923
977
  function assertMediaRole(media, role) {
924
- if (!media || !{
925
- video_clip: ["video", "image"],
926
- speech: ["audio", "voiceover-recording"],
927
- bgm: ["audio"],
928
- caption: []
929
- }[role].includes(media.kind)) throw new Error(`Source is not allowed on ${role} Track`);
978
+ if (!media) throw new Error(`Missing source for ${role} Track`);
979
+ assertClipSourceRole(role, media.kind);
930
980
  }
931
981
  function validateWindow(window, duration) {
932
- if (!Number.isSafeInteger(window.inMs) || !Number.isSafeInteger(window.outMs) || window.inMs < 0 || window.outMs <= window.inMs || duration !== void 0 && window.outMs > duration) throw new Error("Invalid Clip window");
982
+ assertClipWindow(window, duration);
933
983
  }
934
984
  function trackOf(s, id) {
935
985
  requireClip(s, id);
@@ -1217,14 +1267,15 @@ function replaceSourcePlan(s, clipId, input) {
1217
1267
  patch: {
1218
1268
  seqMarker: {
1219
1269
  inMs: input.inMs,
1220
- outMs: input.outMs
1270
+ outMs: input.outMs,
1271
+ ...supportsClipSpeed(source.source) ? {} : { rate: void 0 }
1221
1272
  },
1222
1273
  source: source.id
1223
1274
  }
1224
1275
  }];
1225
1276
  }
1226
1277
  //#endregion
1227
- //#region ../../node_modules/.pnpm/loro-mirror@2.3.3_patch_hash=a551a0e4aa4f61ec88b0d9e970a183b5ee2955107a2e0bb03b378bc510c2f395_loro-crdt@1.16.4/node_modules/loro-mirror/dist/schema/resolver.js
1278
+ //#region ../../node_modules/.pnpm/loro-mirror@2.3.3_patch_hash=a551a0e4aa4f61ec88b0d9e970a183b5ee2955107a2e0bb03b378bc510c2f395_loro-crdt@1.16.1/node_modules/loro-mirror/dist/schema/resolver.js
1228
1279
  function getMapFieldSchema(schema, key) {
1229
1280
  if (!schema) return void 0;
1230
1281
  if (Object.prototype.hasOwnProperty.call(schema.definition, key)) return schema.definition[key];
@@ -1254,10 +1305,10 @@ function getChildContainerSchema(schema, childKey) {
1254
1305
  }
1255
1306
  }
1256
1307
  //#endregion
1257
- //#region ../../node_modules/.pnpm/loro-mirror@2.3.3_patch_hash=a551a0e4aa4f61ec88b0d9e970a183b5ee2955107a2e0bb03b378bc510c2f395_loro-crdt@1.16.4/node_modules/loro-mirror/dist/constants.js
1308
+ //#region ../../node_modules/.pnpm/loro-mirror@2.3.3_patch_hash=a551a0e4aa4f61ec88b0d9e970a183b5ee2955107a2e0bb03b378bc510c2f395_loro-crdt@1.16.1/node_modules/loro-mirror/dist/constants.js
1258
1309
  const CID_KEY = "$cid";
1259
1310
  //#endregion
1260
- //#region ../../node_modules/.pnpm/loro-mirror@2.3.3_patch_hash=a551a0e4aa4f61ec88b0d9e970a183b5ee2955107a2e0bb03b378bc510c2f395_loro-crdt@1.16.4/node_modules/loro-mirror/dist/core/container-inference.js
1311
+ //#region ../../node_modules/.pnpm/loro-mirror@2.3.3_patch_hash=a551a0e4aa4f61ec88b0d9e970a183b5ee2955107a2e0bb03b378bc510c2f395_loro-crdt@1.16.1/node_modules/loro-mirror/dist/core/container-inference.js
1261
1312
  function isPlainObjectValue(value) {
1262
1313
  return typeof value === "object" && value !== null && !Array.isArray(value) && !(value instanceof Date) && !(value instanceof RegExp) && typeof value !== "function";
1263
1314
  }
@@ -1296,7 +1347,7 @@ function getContainerValueKind(value) {
1296
1347
  return "other";
1297
1348
  }
1298
1349
  //#endregion
1299
- //#region ../../node_modules/.pnpm/loro-mirror@2.3.3_patch_hash=a551a0e4aa4f61ec88b0d9e970a183b5ee2955107a2e0bb03b378bc510c2f395_loro-crdt@1.16.4/node_modules/loro-mirror/dist/core/utils.js
1350
+ //#region ../../node_modules/.pnpm/loro-mirror@2.3.3_patch_hash=a551a0e4aa4f61ec88b0d9e970a183b5ee2955107a2e0bb03b378bc510c2f395_loro-crdt@1.16.1/node_modules/loro-mirror/dist/core/utils.js
1300
1351
  /**
1301
1352
  * Utility functions for Loro Mirror core
1302
1353
  */
@@ -1720,7 +1771,7 @@ function safeStringify(value, indent = 2) {
1720
1771
  return JSON.stringify(replacer(value), null, indent);
1721
1772
  }
1722
1773
  //#endregion
1723
- //#region ../../node_modules/.pnpm/loro-mirror@2.3.3_patch_hash=a551a0e4aa4f61ec88b0d9e970a183b5ee2955107a2e0bb03b378bc510c2f395_loro-crdt@1.16.4/node_modules/loro-mirror/dist/schema/validators.js
1774
+ //#region ../../node_modules/.pnpm/loro-mirror@2.3.3_patch_hash=a551a0e4aa4f61ec88b0d9e970a183b5ee2955107a2e0bb03b378bc510c2f395_loro-crdt@1.16.1/node_modules/loro-mirror/dist/schema/validators.js
1724
1775
  const schemaValidationCache = /* @__PURE__ */ new WeakMap();
1725
1776
  function isCacheableValue(value) {
1726
1777
  return typeof value === "object" && value !== null;
@@ -2000,7 +2051,7 @@ function getDefaultValue(schema) {
2000
2051
  }
2001
2052
  }
2002
2053
  //#endregion
2003
- //#region ../../node_modules/.pnpm/loro-mirror@2.3.3_patch_hash=a551a0e4aa4f61ec88b0d9e970a183b5ee2955107a2e0bb03b378bc510c2f395_loro-crdt@1.16.4/node_modules/loro-mirror/dist/schema/index.js
2054
+ //#region ../../node_modules/.pnpm/loro-mirror@2.3.3_patch_hash=a551a0e4aa4f61ec88b0d9e970a183b5ee2955107a2e0bb03b378bc510c2f395_loro-crdt@1.16.1/node_modules/loro-mirror/dist/schema/index.js
2004
2055
  /**
2005
2056
  * Create a schema definition
2006
2057
  */
@@ -2765,7 +2816,7 @@ const produce = new Immer2({
2765
2816
  useStrictShallowCopy: true
2766
2817
  }).produce;
2767
2818
  //#endregion
2768
- //#region ../../node_modules/.pnpm/loro-mirror@2.3.3_patch_hash=a551a0e4aa4f61ec88b0d9e970a183b5ee2955107a2e0bb03b378bc510c2f395_loro-crdt@1.16.4/node_modules/loro-mirror/dist/core/debounce-timer.js
2819
+ //#region ../../node_modules/.pnpm/loro-mirror@2.3.3_patch_hash=a551a0e4aa4f61ec88b0d9e970a183b5ee2955107a2e0bb03b378bc510c2f395_loro-crdt@1.16.1/node_modules/loro-mirror/dist/core/debounce-timer.js
2769
2820
  /**
2770
2821
  * Deadline-based debounce timer.
2771
2822
  *
@@ -2823,7 +2874,7 @@ var DebounceTimer = class {
2823
2874
  }
2824
2875
  };
2825
2876
  //#endregion
2826
- //#region ../../node_modules/.pnpm/loro-mirror@2.3.3_patch_hash=a551a0e4aa4f61ec88b0d9e970a183b5ee2955107a2e0bb03b378bc510c2f395_loro-crdt@1.16.4/node_modules/loro-mirror/dist/core/ephemeral.js
2877
+ //#region ../../node_modules/.pnpm/loro-mirror@2.3.3_patch_hash=a551a0e4aa4f61ec88b0d9e970a183b5ee2955107a2e0bb03b378bc510c2f395_loro-crdt@1.16.1/node_modules/loro-mirror/dist/core/ephemeral.js
2827
2878
  /**
2828
2879
  * Manages ephemeral patches: storage, composition, path resolution, and finalization.
2829
2880
  */
@@ -3098,7 +3149,7 @@ var EphemeralPatchManager = class {
3098
3149
  }
3099
3150
  };
3100
3151
  //#endregion
3101
- //#region ../../node_modules/.pnpm/loro-mirror@2.3.3_patch_hash=a551a0e4aa4f61ec88b0d9e970a183b5ee2955107a2e0bb03b378bc510c2f395_loro-crdt@1.16.4/node_modules/loro-mirror/dist/core/tree-utils.js
3152
+ //#region ../../node_modules/.pnpm/loro-mirror@2.3.3_patch_hash=a551a0e4aa4f61ec88b0d9e970a183b5ee2955107a2e0bb03b378bc510c2f395_loro-crdt@1.16.1/node_modules/loro-mirror/dist/core/tree-utils.js
3102
3153
  function normalizeTreeJson(input, options) {
3103
3154
  if (!Array.isArray(input)) return [];
3104
3155
  return input.map((node) => normalizeTreeNode(node, options));
@@ -3121,7 +3172,7 @@ function isPlainRecord(value) {
3121
3172
  return typeof value === "object" && value !== null && !Array.isArray(value);
3122
3173
  }
3123
3174
  //#endregion
3124
- //#region ../../node_modules/.pnpm/loro-mirror@2.3.3_patch_hash=a551a0e4aa4f61ec88b0d9e970a183b5ee2955107a2e0bb03b378bc510c2f395_loro-crdt@1.16.4/node_modules/loro-mirror/dist/core/loroEventApply.js
3175
+ //#region ../../node_modules/.pnpm/loro-mirror@2.3.3_patch_hash=a551a0e4aa4f61ec88b0d9e970a183b5ee2955107a2e0bb03b378bc510c2f395_loro-crdt@1.16.1/node_modules/loro-mirror/dist/core/loroEventApply.js
3125
3176
  function isJSONObject(v) {
3126
3177
  return typeof v === "object" && v !== null && !Array.isArray(v);
3127
3178
  }
@@ -3543,7 +3594,7 @@ function isIgnoredByAncestor(id, ignoreSet, getContainerById) {
3543
3594
  return false;
3544
3595
  }
3545
3596
  //#endregion
3546
- //#region ../../node_modules/.pnpm/loro-mirror@2.3.3_patch_hash=a551a0e4aa4f61ec88b0d9e970a183b5ee2955107a2e0bb03b378bc510c2f395_loro-crdt@1.16.4/node_modules/loro-mirror/dist/core/diff.js
3597
+ //#region ../../node_modules/.pnpm/loro-mirror@2.3.3_patch_hash=a551a0e4aa4f61ec88b0d9e970a183b5ee2955107a2e0bb03b378bc510c2f395_loro-crdt@1.16.1/node_modules/loro-mirror/dist/core/diff.js
3547
3598
  /**
3548
3599
  * Finds the longest increasing subsequence of a sequence of numbers
3549
3600
  * @param sequence The sequence of numbers
@@ -4274,7 +4325,7 @@ function diffMap(doc, oldState, newState, containerId, schema, inferOptions) {
4274
4325
  return changes;
4275
4326
  }
4276
4327
  //#endregion
4277
- //#region ../../node_modules/.pnpm/loro-mirror@2.3.3_patch_hash=a551a0e4aa4f61ec88b0d9e970a183b5ee2955107a2e0bb03b378bc510c2f395_loro-crdt@1.16.4/node_modules/loro-mirror/dist/core/mirror.js
4328
+ //#region ../../node_modules/.pnpm/loro-mirror@2.3.3_patch_hash=a551a0e4aa4f61ec88b0d9e970a183b5ee2955107a2e0bb03b378bc510c2f395_loro-crdt@1.16.1/node_modules/loro-mirror/dist/core/mirror.js
4278
4329
  /**
4279
4330
  * Mirror core functionality for bidirectional sync between app state and Loro CRDT
4280
4331
  */
@@ -5845,6 +5896,30 @@ const phonemeCodec = codec((v) => {
5845
5896
  assertPhoneme(value);
5846
5897
  return value;
5847
5898
  });
5899
+ /** A row-owned binding couples the selection, pronunciation and its immutable file in one LWW value. */
5900
+ const phoneticBindingCodec = codec((value) => {
5901
+ assertEntity("phonetic-script-entities", value);
5902
+ assertPhoneticScript(void 0, { ...value });
5903
+ return JSON.stringify({
5904
+ phoneme: JSON.parse(phonemeCodec.encode(value.phoneme)),
5905
+ system: value.system,
5906
+ key: value.key,
5907
+ storageKey: value.storageKey,
5908
+ digest: value.digest
5909
+ });
5910
+ }, (raw) => {
5911
+ const value = JSON.parse(raw);
5912
+ if (!isRecord(value)) throw new Error("Expected a PhoneticScript binding");
5913
+ const phoneme = phonemeCodec.decode(JSON.stringify(value.phoneme));
5914
+ if (isInvalidAtomic(phoneme)) throw new Error(phoneme.reason);
5915
+ const binding = {
5916
+ ...value,
5917
+ phoneme
5918
+ };
5919
+ assertEntity("phonetic-script-entities", binding);
5920
+ assertPhoneticScript(void 0, binding);
5921
+ return binding;
5922
+ });
5848
5923
  /** A Clip's sequence marker is stored as one string, so it is taken or replaced whole. */
5849
5924
  const seqMarkerCodec = codec((v) => {
5850
5925
  assertSeqMarker(v);
@@ -5911,13 +5986,7 @@ const medeoDslLoroShape = schema({
5911
5986
  storageKey: schema.String(optional),
5912
5987
  digest: schema.String(optional)
5913
5988
  })),
5914
- "phonetic-script-entities": schema.LoroMapRecord(schema.LoroMap({
5915
- phoneme: atomicValue(phonemeCodec),
5916
- system: schema.String(optional),
5917
- key: schema.String(optional),
5918
- storageKey: schema.String(optional),
5919
- digest: schema.String(optional)
5920
- })),
5989
+ "phonetic-script-entities": schema.LoroMapRecord(schema.LoroMap({ binding: atomicValue(phoneticBindingCodec) })),
5921
5990
  "caption-entities": schema.LoroMapRecord(schema.LoroMap({
5922
5991
  selection: optionalAtomicValue(captionContentCodec),
5923
5992
  text: schema.LoroText(optional),
@@ -5969,20 +6038,78 @@ function documentFormat(doc) {
5969
6038
  }
5970
6039
  var UnsupportedDocumentFormatError = class extends Error {
5971
6040
  code = "unsupported_document_format";
5972
- constructor() {
5973
- super("Expected a new DSL document; legacy, mixed or uninitialized content is not supported");
6041
+ constructor(message = "Expected a new DSL document; legacy, mixed or uninitialized content is not supported") {
6042
+ super(message);
5974
6043
  this.name = "UnsupportedDocumentFormatError";
5975
6044
  }
5976
6045
  };
5977
6046
  /** Run after initial import, before opening an editable handle. Empty new documents still have one Timeline. */
5978
6047
  function assertDslDocument(doc) {
5979
6048
  if (documentFormat(doc) !== "dsl" || doc.getMap("timeline-entities").keys().length !== 1) throw new UnsupportedDocumentFormatError();
6049
+ assertPhoneticBindingFormat(doc);
6050
+ }
6051
+ /** This storage cutover has no field-per-fact reader or mixed-version write path. */
6052
+ function assertPhoneticBindingFormat(doc) {
6053
+ if (!Object.hasOwn(doc.getShallowValue(), "phonetic-script-entities")) return;
6054
+ const table = doc.getMap("phonetic-script-entities");
6055
+ for (const id of table.keys()) {
6056
+ const row = table.get(id);
6057
+ if (!isContainer(row) || row.kind() !== "Map" || row.keys().some((key) => key !== "binding") || typeof row.get("binding") !== "string") throw new UnsupportedDocumentFormatError("PhoneticScript storage requires one atomic binding; old DSL documents need explicit migration");
6058
+ }
5980
6059
  }
5981
6060
  //#endregion
5982
6061
  //#region ../medeo-dsl/src/schema/text-styles.ts
5983
6062
  /** How rich-text marks extend at their edges: a language runs on into text typed after it. */
5984
6063
  const textStyles = { language: { expand: "after" } };
5985
6064
  //#endregion
6065
+ //#region src/dsl/document/phonetic-binding.ts
6066
+ /**
6067
+ * Actions and the transaction guard keep their domain-shaped rows. The native schema stores
6068
+ * a PhoneticScript's facts under one binding; this lens applies draft edits to that single value.
6069
+ * It never reads the retired field-per-fact representation.
6070
+ */
6071
+ function logicalDslState(native) {
6072
+ const phonetics = new Proxy({}, {
6073
+ get: (_target, key) => typeof key === "string" ? native["phonetic-script-entities"]?.[key]?.binding : void 0,
6074
+ set: (_target, key, value) => {
6075
+ if (typeof key !== "string") throw new Error("Invalid PhoneticScript key");
6076
+ native["phonetic-script-entities"] ??= {};
6077
+ const existing = native["phonetic-script-entities"][key];
6078
+ if (existing) existing.binding = value;
6079
+ else native["phonetic-script-entities"][key] = { binding: value };
6080
+ return true;
6081
+ },
6082
+ deleteProperty: (_target, key) => {
6083
+ if (typeof key === "string") delete native["phonetic-script-entities"][key];
6084
+ return true;
6085
+ },
6086
+ ownKeys: () => Object.keys(native["phonetic-script-entities"] ?? {}),
6087
+ has: (_target, key) => typeof key === "string" && Object.hasOwn(native["phonetic-script-entities"] ?? {}, key),
6088
+ getOwnPropertyDescriptor: (_target, key) => typeof key === "string" && Object.hasOwn(native["phonetic-script-entities"] ?? {}, key) ? {
6089
+ configurable: true,
6090
+ enumerable: true,
6091
+ writable: true,
6092
+ value: native["phonetic-script-entities"][key].binding
6093
+ } : void 0
6094
+ });
6095
+ const view = {};
6096
+ for (const key of new Set([
6097
+ ...Object.keys(native),
6098
+ ...entityTables,
6099
+ ...relationTables,
6100
+ "contain-relations"
6101
+ ])) Object.defineProperty(view, key, {
6102
+ configurable: true,
6103
+ enumerable: true,
6104
+ get: () => key === "phonetic-script-entities" ? phonetics : Reflect.get(native, key),
6105
+ set: (value) => {
6106
+ if (key === "phonetic-script-entities") throw new Error("Cannot replace the PhoneticScript table");
6107
+ Reflect.set(native, key, value);
6108
+ }
6109
+ });
6110
+ return view;
6111
+ }
6112
+ //#endregion
5986
6113
  //#region src/dsl/document/read.ts
5987
6114
  /** Looking up a missing root must not create it. */
5988
6115
  function rootMap(doc, name) {
@@ -6066,6 +6193,7 @@ function referenceTargets(table, row) {
6066
6193
  }
6067
6194
  }
6068
6195
  function readMedeoDsl(doc, state) {
6196
+ const logical = logicalDslState(state);
6069
6197
  const diagnostics = [];
6070
6198
  const diagnostic = (code, path, error) => diagnostics.push({
6071
6199
  code,
@@ -6075,7 +6203,7 @@ function readMedeoDsl(doc, state) {
6075
6203
  const entities = Object.fromEntries(entityTables.map((t) => [t, {}]));
6076
6204
  const relations = Object.fromEntries(relationTables.map((t) => [t, {}]));
6077
6205
  const richText = {};
6078
- for (const table of entityTables) for (const [id, raw] of Object.entries(state[table] ?? {})) try {
6206
+ for (const table of entityTables) for (const [id, raw] of Object.entries(logical[table] ?? {})) try {
6079
6207
  assertId(id);
6080
6208
  let v = raw;
6081
6209
  for (const field of optionalReferences[table] ?? []) {
@@ -6581,7 +6709,11 @@ function validateDraft(tracked) {
6581
6709
  before,
6582
6710
  after: state,
6583
6711
  entities: tracked.entities,
6584
- relations: tracked.relations
6712
+ relations: tracked.relations,
6713
+ containment: {
6714
+ before: plainTree(before["contain-relations"] ?? []),
6715
+ after: plainTree(state["contain-relations"] ?? [])
6716
+ }
6585
6717
  });
6586
6718
  if (!tracked.treeChanged) return;
6587
6719
  const oldNodes = nodeIndex(before["contain-relations"] ?? []);
@@ -6658,8 +6790,11 @@ function runTransaction(doc, mirror, edit, options, setReject) {
6658
6790
  const steps = [];
6659
6791
  const newCaptionTexts = /* @__PURE__ */ new Map();
6660
6792
  const bodies = /* @__PURE__ */ new Map();
6661
- const before = mirror.getState();
6662
- mirror.setState((raw) => {
6793
+ const nativeBefore = mirror.getState();
6794
+ const before = logicalDslState(nativeBefore);
6795
+ const phoneticRows = /* @__PURE__ */ new Map();
6796
+ mirror.setState((native) => {
6797
+ const raw = logicalDslState(native);
6663
6798
  const tracked = new TransactionDraft(raw, before);
6664
6799
  setReject((error) => tracked.fail(error));
6665
6800
  /** The current text of a Caption's own Text or a script segment, as this transaction leaves it so far. */
@@ -6851,6 +6986,15 @@ function runTransaction(doc, mirror, edit, options, setReject) {
6851
6986
  for (const step of steps) if (step.type === "text") if ("captionId" in step.target) requireEntity(raw, "caption-entities", step.target.captionId);
6852
6987
  else requireSegment(raw, step.target.audioScriptId, step.target.segmentId);
6853
6988
  assertSelectionsAfter(raw, before, tracked, steps);
6989
+ const changedPhonetics = new Set([...tracked.entities.get("phonetic-script-entities")?.keys() ?? [], ...steps.flatMap((step) => step.type !== "text" && step.table === "phonetic-script-entities" ? [step.id] : [])]);
6990
+ for (const id of changedPhonetics) {
6991
+ const row = raw["phonetic-script-entities"][id];
6992
+ phoneticRows.set(id, row ? copyInput(row) : void 0);
6993
+ if (!row) continue;
6994
+ const old = nativeBefore["phonetic-script-entities"][id];
6995
+ if (old) native["phonetic-script-entities"][id] = old;
6996
+ else delete native["phonetic-script-entities"][id];
6997
+ }
6854
6998
  for (const [id, fields] of tracked.entities.get("caption-entities") ?? []) {
6855
6999
  const row = raw["caption-entities"][id];
6856
7000
  if (fields.has("text") && row?.text !== void 0 && before["caption-entities"][id]?.text === void 0) {
@@ -6859,6 +7003,7 @@ function runTransaction(doc, mirror, edit, options, setReject) {
6859
7003
  }
6860
7004
  }
6861
7005
  for (const table of Object.keys(selectionField)) {
7006
+ if (table === "phonetic-script-entities") continue;
6862
7007
  const field = selectionField[table];
6863
7008
  for (const [id, fields] of tracked.entities.get(table) ?? []) {
6864
7009
  const row = raw[table][id];
@@ -6898,6 +7043,8 @@ function runTransaction(doc, mirror, edit, options, setReject) {
6898
7043
  applyTextEdits(text, step.edits);
6899
7044
  continue;
6900
7045
  }
7046
+ const phonetic = step.table === "phonetic-script-entities" ? phoneticRows.get(step.id) : void 0;
7047
+ if (step.table === "phonetic-script-entities" && !phonetic) continue;
6901
7048
  const row = entityRow(doc, step.table, step.id);
6902
7049
  const field = selectionField[step.table];
6903
7050
  if (step.type === "range") {
@@ -6916,12 +7063,21 @@ function runTransaction(doc, mirror, edit, options, setReject) {
6916
7063
  if (!value.start || !value.end) throw new Error("Unable to construct prepared range");
6917
7064
  }
6918
7065
  if (step.phonemeScript !== void 0) value.phonemeScript = step.phonemeScript;
6919
- if (!row) throw new Error("Unable to construct prepared range");
6920
- row.set(field, codecs[step.table].encode(value));
6921
- } else if (step.value !== void 0) {
7066
+ if (phonetic) phonetic.phoneme = value;
7067
+ else {
7068
+ if (!row) throw new Error("Unable to construct prepared range");
7069
+ row.set(field, codecs[step.table].encode(value));
7070
+ }
7071
+ } else if (step.value !== void 0) if (phonetic) phonetic.phoneme = step.value;
7072
+ else {
6922
7073
  if (!row) throw new Error("Prepared selection owner is missing");
6923
7074
  row.set(field, codecs[step.table].encode(step.value));
6924
- } else if (row?.get(field) !== void 0) row.delete(field);
7075
+ }
7076
+ else if (row?.get(field) !== void 0) row.delete(field);
7077
+ }
7078
+ for (const [id, binding] of phoneticRows) {
7079
+ if (!binding) continue;
7080
+ (entityRow(doc, "phonetic-script-entities", id) ?? doc.getMap("phonetic-script-entities").setContainer(id, new LoroMap())).set("binding", phoneticBindingCodec.encode(binding));
6925
7081
  }
6926
7082
  }
6927
7083
  });
@@ -6968,7 +7124,7 @@ function assertSelectionsAfter(raw, before, tracked, steps) {
6968
7124
  if (!row) continue;
6969
7125
  const phoneme = phonemes.has(id) ? phonemes.get(id) : row.phoneme;
6970
7126
  const old = before["phonetic-script-entities"][id];
6971
- assertPhoneticScript(old, {
7127
+ assertPhoneticScript(old ? { ...old } : void 0, {
6972
7128
  ...row,
6973
7129
  phoneme: isRecord(phoneme) ? phoneme : void 0
6974
7130
  });
@@ -7007,6 +7163,7 @@ var MedeoDsl = class MedeoDsl {
7007
7163
  "tracks",
7008
7164
  "part_library"
7009
7165
  ].some((key) => Object.hasOwn(roots, key))) throw new Error("Expected a new DSL document, not a legacy content model");
7166
+ assertPhoneticBindingFormat(doc);
7010
7167
  doc.configTextStyle(textStyles);
7011
7168
  this.mirror = new Mirror({
7012
7169
  doc,
@@ -7070,6 +7227,11 @@ var MedeoDsl = class MedeoDsl {
7070
7227
  editCaptionText(captionId, expectedBodyId, edits, audit) {
7071
7228
  this.transact((tx) => tx.editCaptionText(captionId, edits, expectedBodyId), audit);
7072
7229
  }
7230
+ /** Check the session's write capability before an Action starts host-owned resource I/O. */
7231
+ assertWritable() {
7232
+ this.assertReadyToWrite();
7233
+ this.options.mutationGuard?.run(() => {});
7234
+ }
7073
7235
  /**
7074
7236
  * A run inside one segment as native Cursors. Local ranges exclude insertions at their start and
7075
7237
  * include insertions at their end. K01 remains deferred.
@@ -7102,13 +7264,7 @@ var MedeoDsl = class MedeoDsl {
7102
7264
  if (this.ownsDoc) this.doc.free();
7103
7265
  }
7104
7266
  mutate(run) {
7105
- this.assertActive();
7106
- if (this.writing || this.notifying) {
7107
- const error = /* @__PURE__ */ new Error("A DSL mutation is already running");
7108
- this.rejectTransaction?.(error);
7109
- throw error;
7110
- }
7111
- if (this.doc.getPendingTxnLength() !== 0) throw new Error("Commit external native edits before a DSL mutation");
7267
+ this.assertReadyToWrite();
7112
7268
  const apply = () => {
7113
7269
  this.writing = true;
7114
7270
  try {
@@ -7123,6 +7279,15 @@ var MedeoDsl = class MedeoDsl {
7123
7279
  if (this.options.mutationGuard) this.options.mutationGuard.run(apply);
7124
7280
  else apply();
7125
7281
  }
7282
+ assertReadyToWrite() {
7283
+ this.assertActive();
7284
+ if (this.writing || this.notifying) {
7285
+ const error = /* @__PURE__ */ new Error("A DSL mutation is already running");
7286
+ this.rejectTransaction?.(error);
7287
+ throw error;
7288
+ }
7289
+ if (this.doc.getPendingTxnLength() !== 0) throw new Error("Commit external native edits before a DSL mutation");
7290
+ }
7126
7291
  notify() {
7127
7292
  if (this.disposed || !this.dirty) return;
7128
7293
  this.dirty = false;
@@ -7847,8 +8012,12 @@ var DslEditor = class {
7847
8012
  * it; an unchanged one keeps its file. Recordings already spoken from it keep their derived-from.
7848
8013
  */
7849
8014
  async setPhoneticScript(input, options) {
8015
+ this.dsl.assertWritable();
7850
8016
  const id = input.phoneticScriptId ?? this.idFactory("phonetic-script");
7851
8017
  const range = segmentRange(input);
8018
+ assertId(id);
8019
+ assertSegmentRange(range, requireSegment(this.dsl.getSnapshot(), range.audioScriptId, range.segmentId).text);
8020
+ assertPhonemeScript(input.phonemeScript);
7852
8021
  const file = input.phonemeScript === void 0 ? {} : await this.fileFor(id, input.phonemeScript);
7853
8022
  const exists = Object.hasOwn(this.dsl.getSnapshot()["phonetic-script-entities"], id);
7854
8023
  const cleared = {
@@ -8072,7 +8241,8 @@ function toVideoDraft(snapshot) {
8072
8241
  } }
8073
8242
  } };
8074
8243
  } else if (track.role === "speech") {
8075
- if (!source || source.kind !== "voiceover-recording" || source.system !== "memota-speech") fail("Legacy Speech requires a memota-speech VoiceoverRecording");
8244
+ if (!source || source.kind !== "audio" && source.kind !== "voiceover-recording") fail("Speech playback requires an Audio or VoiceoverRecording");
8245
+ if (source.system !== "memota" && !(source.kind === "voiceover-recording" && source.system === "memota-speech")) fail("Speech playback requires a Memota audio file");
8076
8246
  requireWholeAudio(source, clip, fail);
8077
8247
  if (clip.fillTimeline) fail("Legacy Speech cannot express timeline fill");
8078
8248
  const voices = Object.values(snapshot["voice-timbre-relations"]).filter((r) => r.recordingId === sourceId).flatMap((r) => snapshot["voice-entities"][r.voiceId] ? [snapshot["voice-entities"][r.voiceId]] : []);
@@ -8089,7 +8259,7 @@ function toVideoDraft(snapshot) {
8089
8259
  audio_storage_key: source.storageKey,
8090
8260
  origin_speech_id: source.key,
8091
8261
  volume: clip.volume,
8092
- audio_script: spokenText(snapshot, sourceId),
8262
+ audio_script: source.kind === "voiceover-recording" ? spokenText(snapshot, sourceId) : "",
8093
8263
  voice: voice ? {
8094
8264
  id: voice.key,
8095
8265
  name: voice.name
@@ -8524,4 +8694,4 @@ function importedScript(id, text, mintSegment) {
8524
8694
  };
8525
8695
  }
8526
8696
  //#endregion
8527
- export { defaultIdFactory as _, DslEditor as a, tupleHash as b, UnsupportedDocumentFormatError as c, medeoDslLoroShape as d, captionContentCodec as f, DslProjectionError as g, resolveDslLayout as h, createInitialMedeoDsl as i, assertDslDocument as l, timeAnchorCodec as m, toVideoDraft as n, toAudioScriptViews as o, isInvalidAtomic as p, DEFAULT_UNIT_TIME_MS as r, MedeoDsl as s, fromVideoDraft as t, documentFormat as u, relationId as v, resourceId as y };
8697
+ export { DslProjectionError as _, DslEditor as a, resourceId as b, UnsupportedDocumentFormatError as c, documentFormat as d, medeoDslLoroShape as f, resolveDslLayout as g, timeAnchorCodec as h, createInitialMedeoDsl as i, assertDslDocument as l, isInvalidAtomic as m, toVideoDraft as n, toAudioScriptViews as o, captionContentCodec as p, DEFAULT_UNIT_TIME_MS as r, MedeoDsl as s, fromVideoDraft as t, assertPhoneticBindingFormat as u, defaultIdFactory as v, tupleHash as x, relationId as y };
package/dist/dsl.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- import { $ as ClipPlacement, A as VoiceoverResult, At as RichTextSnapshot, B as isInvalidAtomic, Bt as Voice, C as DslProjectionError, Ct as MedeoDslSnapshot, D as UploadText, Dt as RelationTables, E as DslEditor, Et as RelationTable, F as UnsupportedDocumentFormatError, Ft as TextMutation, G as CommitOptions, H as MedeoDslDraft, I as assertDslDocument, It as TextRun, J as AddDslClipsInput, K as OpActor, L as documentFormat, Lt as TimeAnchor, M as MedeoDsl, Mt as TextDelta, N as DslAudit, Nt as TextEdit, O as ApplyVoiceoverInput, Ot as RelationValues, P as DslTransaction, Pt as TextMark, Q as ReplaceDslClipsInput, R as InvalidAtomic, Rt as Track, S as DslLayout, St as FieldPatch, T as createInitialMedeoDsl, Tt as RelationMutation, U as MedeoDslLoroState, V as timeAnchorCodec, W as medeoDslLoroShape, X as DslClipUpdate, Y as ClipSource, Z as MoveDslClipsInput, _ as VideoDraftTimeline, _t as DslMutation, a as toAudioScriptViews, at as tupleHash, b as resolveDslLayout, bt as EntityTables, c as CaptionDisplayCue, ct as CaptionContent, d as VideoDraft, dt as CaptionText, et as DslIdFactory, f as VideoDraftCaptionStyle, ft as Clip, g as VideoDraftPartUnion, gt as DslDiagnostic, h as VideoDraftInput, ht as DeepReadonly, i as AudioScriptView, it as resourceId, j as CreateMedeoDslOptions, jt as StoredFile, k as VoiceoverCaptionInput, kt as ResourceSystem, l as DEFAULT_UNIT_TIME_MS, lt as CaptionSelection, m as VideoDraftContentPartUnion, mt as ContainNode, n as ImportedVideoDraft, nt as defaultIdFactory, o as VideoDraftProjection, ot as AudioScript, p as VideoDraftContent, pt as ContainMutation, q as AddDslClip, r as fromVideoDraft, rt as relationId, s as toVideoDraft, st as Caption, t as FromVideoDraftOptions, tt as EntityIdKind, u as VideoCreationSettings, ut as CaptionStyle, v as VideoDraftTrack, vt as EntityMutation, w as DslTrackLayout, wt as Media, x as DslClipLayout, xt as EntityValues, y as VideoDraftTrackItem, yt as EntityTable, z as captionContentCodec, zt as TrackRole } from "./index-DuM7I4Ku.js";
1
+ import { $ as ClipPlacement, A as VoiceoverResult, At as RichTextSnapshot, B as isInvalidAtomic, Bt as Voice, C as DslProjectionError, Ct as MedeoDslSnapshot, D as UploadText, Dt as RelationTables, E as DslEditor, Et as RelationTable, F as UnsupportedDocumentFormatError, Ft as TextMutation, G as CommitOptions, H as MedeoDslDraft, I as assertDslDocument, It as TextRun, J as AddDslClipsInput, K as OpActor, L as documentFormat, Lt as TimeAnchor, M as MedeoDsl, Mt as TextDelta, N as DslAudit, Nt as TextEdit, O as ApplyVoiceoverInput, Ot as RelationValues, P as DslTransaction, Pt as TextMark, Q as ReplaceDslClipsInput, R as InvalidAtomic, Rt as Track, S as DslLayout, St as FieldPatch, T as createInitialMedeoDsl, Tt as RelationMutation, U as MedeoDslLoroState, V as timeAnchorCodec, W as medeoDslLoroShape, X as DslClipUpdate, Y as ClipSource, Z as MoveDslClipsInput, _ as VideoDraftTimeline, _t as DslMutation, a as toAudioScriptViews, at as tupleHash, b as resolveDslLayout, bt as EntityTables, c as CaptionDisplayCue, ct as CaptionContent, d as VideoDraft, dt as CaptionText, et as DslIdFactory, f as VideoDraftCaptionStyle, ft as Clip, g as VideoDraftPartUnion, gt as DslDiagnostic, h as VideoDraftInput, ht as DeepReadonly, i as AudioScriptView, it as resourceId, j as CreateMedeoDslOptions, jt as StoredFile, k as VoiceoverCaptionInput, kt as ResourceSystem, l as DEFAULT_UNIT_TIME_MS, lt as CaptionSelection, m as VideoDraftContentPartUnion, mt as ContainNode, n as ImportedVideoDraft, nt as defaultIdFactory, o as VideoDraftProjection, ot as AudioScript, p as VideoDraftContent, pt as ContainMutation, q as AddDslClip, r as fromVideoDraft, rt as relationId, s as toVideoDraft, st as Caption, t as FromVideoDraftOptions, tt as EntityIdKind, u as VideoCreationSettings, ut as CaptionStyle, v as VideoDraftTrack, vt as EntityMutation, w as DslTrackLayout, wt as Media, x as DslClipLayout, xt as EntityValues, y as VideoDraftTrackItem, yt as EntityTable, z as captionContentCodec, zt as TrackRole } from "./index-B1Lqm_Z1.js";
2
2
  export { type AddDslClip, type AddDslClipsInput, type ApplyVoiceoverInput, type AudioScript, type AudioScriptView, type Caption, type CaptionContent, type CaptionDisplayCue, type CaptionSelection, type CaptionStyle, type CaptionText, type Clip, type ClipPlacement, type ClipSource, type CommitOptions, type ContainMutation, type ContainNode, type CreateMedeoDslOptions, DEFAULT_UNIT_TIME_MS, type DeepReadonly, type DslAudit, type DslClipLayout, type DslClipUpdate, type DslDiagnostic, DslEditor, type DslIdFactory, type DslLayout, type DslMutation, DslProjectionError, type DslTrackLayout, type DslTransaction, type EntityIdKind, type EntityMutation, type EntityTable, type EntityTables, type EntityValues, type FieldPatch, type FromVideoDraftOptions, type ImportedVideoDraft, type InvalidAtomic, MedeoDsl, type MedeoDslDraft, type MedeoDslLoroState, type MedeoDslSnapshot, type Media, type MoveDslClipsInput, type OpActor, type RelationMutation, type RelationTable, type RelationTables, type RelationValues, type ReplaceDslClipsInput, type ResourceSystem, type RichTextSnapshot, type StoredFile, type TextDelta, type TextEdit, type TextMark, type TextMutation, type TextRun, type TimeAnchor, type Track, type TrackRole, UnsupportedDocumentFormatError, type UploadText, type VideoCreationSettings, type VideoDraft, type VideoDraftCaptionStyle, type VideoDraftContent, type VideoDraftContentPartUnion, type VideoDraftInput, type VideoDraftPartUnion, type VideoDraftProjection, type VideoDraftTimeline, type VideoDraftTrack, type VideoDraftTrackItem, type Voice, type VoiceoverCaptionInput, type VoiceoverResult, assertDslDocument, captionContentCodec, createInitialMedeoDsl, defaultIdFactory, documentFormat, fromVideoDraft, isInvalidAtomic, medeoDslLoroShape, relationId, resolveDslLayout, resourceId, timeAnchorCodec, toAudioScriptViews, toVideoDraft, tupleHash };
package/dist/dsl.js CHANGED
@@ -1,2 +1,2 @@
1
- import { _ as defaultIdFactory, a as DslEditor, b as tupleHash, c as UnsupportedDocumentFormatError, d as medeoDslLoroShape, f as captionContentCodec, g as DslProjectionError, h as resolveDslLayout, i as createInitialMedeoDsl, l as assertDslDocument, m as timeAnchorCodec, n as toVideoDraft, o as toAudioScriptViews, p as isInvalidAtomic, r as DEFAULT_UNIT_TIME_MS, s as MedeoDsl, t as fromVideoDraft, u as documentFormat, v as relationId, y as resourceId } from "./dsl-Cpk8WxcZ.js";
1
+ import { _ as DslProjectionError, a as DslEditor, b as resourceId, c as UnsupportedDocumentFormatError, d as documentFormat, f as medeoDslLoroShape, g as resolveDslLayout, h as timeAnchorCodec, i as createInitialMedeoDsl, l as assertDslDocument, m as isInvalidAtomic, n as toVideoDraft, o as toAudioScriptViews, p as captionContentCodec, r as DEFAULT_UNIT_TIME_MS, s as MedeoDsl, t as fromVideoDraft, v as defaultIdFactory, x as tupleHash, y as relationId } from "./dsl-BngbR2zr.js";
2
2
  export { DEFAULT_UNIT_TIME_MS, DslEditor, DslProjectionError, MedeoDsl, UnsupportedDocumentFormatError, assertDslDocument, captionContentCodec, createInitialMedeoDsl, defaultIdFactory, documentFormat, fromVideoDraft, isInvalidAtomic, medeoDslLoroShape, relationId, resolveDslLayout, resourceId, timeAnchorCodec, toAudioScriptViews, toVideoDraft, tupleHash };