@mengine/medeo-client 1.3.1-alpha.8 → 1.3.1-alpha.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -221,13 +221,12 @@ declare function selectAudioScriptSegments(script: EntityRow<'audio-script'>, se
221
221
  //#region src/entity-editor/entity-graph-http-client.d.ts
222
222
  /** One authenticated revision of the authoritative Medeo Entity graph. */
223
223
  interface EntityGraphState {
224
- readonly audioScriptEntityId: string | null;
224
+ /** Fixed identity of the project's one AudioScript; present in every readable state. */
225
+ readonly audioScriptEntityId: string;
225
226
  readonly revision: number;
226
227
  readonly rows: EntityRelationRows;
227
228
  }
228
229
  interface EntityGraphCommitOptions {
229
- /** Omitted preserves the baseline association; null explicitly clears it. */
230
- readonly audioScriptEntityId?: string | null;
231
230
  /** Server version vector proving the explicit legacy-to-Entity migration baseline. */
232
231
  readonly migrationBaseVv?: string;
233
232
  }
@@ -245,7 +244,7 @@ declare class EntityGraphHttpClient {
245
244
  * transient create-then-remove never becomes a request to delete unknown
246
245
  * persisted data.
247
246
  */
248
- commit(baseState: EntityGraphState, nextRows: EntityRelationRows, options?: EntityGraphCommitOptions): Promise<EntityGraphState>;
247
+ commit(baseState: Pick<EntityGraphState, 'revision' | 'rows'>, nextRows: EntityRelationRows, options?: EntityGraphCommitOptions): Promise<EntityGraphState>;
249
248
  private request;
250
249
  }
251
250
  //#endregion
@@ -590,6 +589,8 @@ interface EditorFoundation {
590
589
  readonly rows: EntityRelationRows;
591
590
  readonly timelineEntityId: string;
592
591
  readonly trackEntityIds: Readonly<Record<EditorTrackRole, string>>;
592
+ /** Fixed identity of the project's one AudioScript, minted here when missing. */
593
+ readonly audioScriptEntityId: string;
593
594
  }
594
595
  /**
595
596
  * MEngine editor policy, not a cardinality restriction on the general-purpose DSL.
@@ -661,8 +662,22 @@ interface DocumentAudioScript {
661
662
  readonly entityId: string;
662
663
  readonly segments: readonly (ScriptTextSegment & Readonly<Record<string, unknown>>)[];
663
664
  }
664
- /** Resolve only the document association; other AudioScripts remain independent resources. */
665
- declare function readDocumentAudioScript(state: Pick<EntityGraphState, 'rows' | 'audioScriptEntityId'>): DocumentAudioScript | null;
665
+ /** Any state that carries the fixed document AudioScript identity outlet. */
666
+ interface DocumentAudioScriptState {
667
+ readonly rows: EntityRelationRows;
668
+ /**
669
+ * Fixed identity minted atomically at project initialization. `null` is only
670
+ * the internal revision-0 "not yet initialized" representation; normal
671
+ * projects never carry the legacy null association.
672
+ */
673
+ readonly audioScriptEntityId: string | null;
674
+ }
675
+ /**
676
+ * Strict read of the project's one fixed AudioScript. The identity is immutable
677
+ * for the project's lifetime; this never selects, clears, or switches a
678
+ * script. Uninitialized or dangling identities are projection errors.
679
+ */
680
+ declare function readDocumentAudioScript(state: DocumentAudioScriptState): DocumentAudioScript;
666
681
  //#endregion
667
682
  //#region src/editor/journal.d.ts
668
683
  /**
@@ -1568,4 +1583,4 @@ declare function hostForAbsMs(ranges: MainClipRange[], absMs: number): MainClipR
1568
1583
  */
1569
1584
  declare function relativePositionForAbs(ranges: MainClipRange[], absMs: number): TrackItemTimePosition;
1570
1585
  //#endregion
1571
- export { type Aggregation, type AssembledCaptionContent, type AssembledPhoneticScriptContent, type Attachment, type AudioMediaAssetFact, type BgmPart, type CaptionContentInput, type CaptionPart, type CaptionSegmentSelection, type CaptionStyle, type ClipEntityId, type ClipPlacement, type CommitOptions, DEFAULT_UNIT_TIME_MS, type DeleteBgmInput, type DeleteClipInput, type DeleteClipTreeInput, type DeleteVoiceoverInput, type DerivedItemPosition, type DocStorageLike, type DocVersionMark, type DocumentAudioScript, ENTITY_TIMELINE_PROJECTION_SCHEMA_VERSION, type EditorFoundation, type EntityGraphCommitOptions, EntityGraphHttpClient, type EntityGraphState, EntityTimelineEditor, type EntityTimelineIdFactory, EntityTimelineProjectionError, type EntityTimelineTrackRole, IMPLEMENTED_SEMANTIC_OP_KINDS, type ImageMediaAssetFact, type ImplementedSemanticOpKind, type ImportedMediaAsset, type InitialDocumentFacts, type InsertCaptionClipInput, type InsertClipInput, type InsertMediaClipInput, type InsertMediaClipsInput, type InsertPlacedClipInput, type JournalEntry, LANE_KINDS_IN_STACK_ORDER, type LaneKind, type LinearClipSpeed, type MainClipRange, type MakeEmptyPart, ManualSyncDoc, type ManualSyncDocOptions, MedeoHttpDocStorage, type MedeoHttpDocStorageOptions, type MediaAssetFact, type MediaClipInsertion, MemoryDocStorage, MengineAckFailedError, type MengineAuditEntry, type MengineAuditResponse, MengineDocSession, type MengineDocSessionOptions, type MengineDocSessionUpdateEvent, type MengineDocumentVersion, type MengineEventStreamOptions, MengineHttpClient, type MengineHttpClientOptions, MengineHttpRequestError, MenginePushRejectedError, type MenginePushResponse, type MenginePushUpdateResponse, type MengineRejectedResponse, type MengineSnapshotResponse, type MengineSseUpdateEvent, type MengineSyncResponse, type MengineUpdateMeta, MirrorVideoDocumentAdapter, type MirrorVideoDocumentOptions, type MoveClipInput, type MoveClipsToStartsInput, type MoveSequentialClipsInput, type MoveVoiceoverInput, type OpActor, type PartAggregation, type PartIdFactory, type PartKind, type PartUnion, type PatchCaptionStyleInput, PlainMemoryAdapter, type PlainMemoryAdapterOptions, type PlannedSemanticOpKind, type PullFailureReason, type PullResult, type PushOutcome, type PushOutcomeKind, type PushResult, type PushResultKind, type ReplaceClipContentInput, type ReplaceMediaClipInput, type ReplaceSequentialClipsInput, type ReplacementMediaClipInput, type ResolvedEntityClipPlacement, type ResolvedEntityTimelineLayout, type ResolvedEntityTrack, SchemaValidator, ScriptCompositionError, type SemanticDocumentAdapter, SemanticEditor, type SemanticOpInput, type SemanticOpKind, type SemanticOpName, type SequentialClipAnchor, type SetBgmInput, type SetCaptionVisibilityInput, type SetClipPlacementInput, type SetClipSpeedInput, type SetClipVolumeInput, type SnapshotReadable, type SolvedVideoDocument, type SpeechHostMap, type SpeechPart, type SpeedShift, TIMELINE_SKELETON_DURATION_MS, type Timeline, type TimelineDoc, type TimelineItem, type Track, type TrackDraft, type TrackItem, type TrackItemDraft, type TrackItemTimePosition, type TransactAudit, type TrimClipInput, type UpdateClipInput, type UpdateClipMarkerInput, VIDEO_DOCUMENT_SCHEMA_VERSION, ValidationError, type VideoClipPart, type VideoDocument, type VideoDocumentDraft, type VideoDocumentMirrorSchema, type VideoDocumentSchemaVersion, VideoDocumentValidationError, type VideoDocumentValidationIssue, type VideoDocumentValidationIssueCode, type VideoDraft, type CaptionPart$1 as VideoDraftCaptionPart, type VideoDraftPartUnion, type Timeline$1 as VideoDraftTimeline, type Track$1 as VideoDraftTrack, type TrackItem$1 as VideoDraftTrackItem, type VideoMediaAssetFact, type VisualMediaAssetFact, type VoiceMediaAssetFact, type VoiceoverCaptionFact, type VoiceoverTakeInput, type VoiceoverTakeResult, type WaitForServerAckOptions, applyEntityTimelineProjection, arrangeMainTrackSeamlessly, assembleCaptionContent, assembleEntityContent, assemblePhoneticScriptContent, assembleScriptText, assertCanonicalEditorResources, assertEntityProjectionPreservesLegacy, assertMediaAssetWritePolicy, assertValidVideoDocument, base64ToBytes, buildInitialVideoDocument, buildSpeechHostMap, bytesToBase64, cascadeAfterVideoClipChanges, createEditSandbox, createMirrorVideoDocument, createMirrorVideoDocumentAdapter, createPlainMemoryAdapter, decodeDocVersionMark, derivePositionFromAbs, effectiveVideoClipDurationMs, encodeDocVersionMark, ensureEditorFoundation, ensureLaneTrack, fillMainTrackTimeGaps, findComposedAudioScript, findLaneTrack, fromVideoDocument, generatePartId, getAt, hostForAbsMs, importMediaAsset, isEmptyVideoClip, isImplementedSemanticOpKind, isMap, laneTrackId, mainTrackRanges, migrateLegacyTimelineToEntities, partDurationMs, partUnionSchema, prepareCaptionContent, projectEntityTimeline, readDocumentAudioScript, readMainTrackItems, readMengineEventStream, readPart, readPartDurationMs, readVideoDocumentFromDraft, reassignSpeechesToVideoClipsByTime, recalculateTimelineDuration, relativePositionForAbs, replayJournal, resolveAllSpeechOverlaps, resolveEntityTimelineLayout, resolveSpeechOverlapByShiftingVideos, safeDurationMs, index_d_exports as schemas, selectAudioScriptSegments, snapshotToPlain, solveVideoDocument, speedOf, syncAggregatedClipsTimePosition, toVideoDocument, validateVideoDocument, videoDocumentMirrorSchema, videoDocumentSchema, writeVideoDocumentToDraft };
1586
+ export { type Aggregation, type AssembledCaptionContent, type AssembledPhoneticScriptContent, type Attachment, type AudioMediaAssetFact, type BgmPart, type CaptionContentInput, type CaptionPart, type CaptionSegmentSelection, type CaptionStyle, type ClipEntityId, type ClipPlacement, type CommitOptions, DEFAULT_UNIT_TIME_MS, type DeleteBgmInput, type DeleteClipInput, type DeleteClipTreeInput, type DeleteVoiceoverInput, type DerivedItemPosition, type DocStorageLike, type DocVersionMark, type DocumentAudioScript, type DocumentAudioScriptState, ENTITY_TIMELINE_PROJECTION_SCHEMA_VERSION, type EditorFoundation, type EntityGraphCommitOptions, EntityGraphHttpClient, type EntityGraphState, EntityTimelineEditor, type EntityTimelineIdFactory, EntityTimelineProjectionError, type EntityTimelineTrackRole, IMPLEMENTED_SEMANTIC_OP_KINDS, type ImageMediaAssetFact, type ImplementedSemanticOpKind, type ImportedMediaAsset, type InitialDocumentFacts, type InsertCaptionClipInput, type InsertClipInput, type InsertMediaClipInput, type InsertMediaClipsInput, type InsertPlacedClipInput, type JournalEntry, LANE_KINDS_IN_STACK_ORDER, type LaneKind, type LinearClipSpeed, type MainClipRange, type MakeEmptyPart, ManualSyncDoc, type ManualSyncDocOptions, MedeoHttpDocStorage, type MedeoHttpDocStorageOptions, type MediaAssetFact, type MediaClipInsertion, MemoryDocStorage, MengineAckFailedError, type MengineAuditEntry, type MengineAuditResponse, MengineDocSession, type MengineDocSessionOptions, type MengineDocSessionUpdateEvent, type MengineDocumentVersion, type MengineEventStreamOptions, MengineHttpClient, type MengineHttpClientOptions, MengineHttpRequestError, MenginePushRejectedError, type MenginePushResponse, type MenginePushUpdateResponse, type MengineRejectedResponse, type MengineSnapshotResponse, type MengineSseUpdateEvent, type MengineSyncResponse, type MengineUpdateMeta, MirrorVideoDocumentAdapter, type MirrorVideoDocumentOptions, type MoveClipInput, type MoveClipsToStartsInput, type MoveSequentialClipsInput, type MoveVoiceoverInput, type OpActor, type PartAggregation, type PartIdFactory, type PartKind, type PartUnion, type PatchCaptionStyleInput, PlainMemoryAdapter, type PlainMemoryAdapterOptions, type PlannedSemanticOpKind, type PullFailureReason, type PullResult, type PushOutcome, type PushOutcomeKind, type PushResult, type PushResultKind, type ReplaceClipContentInput, type ReplaceMediaClipInput, type ReplaceSequentialClipsInput, type ReplacementMediaClipInput, type ResolvedEntityClipPlacement, type ResolvedEntityTimelineLayout, type ResolvedEntityTrack, SchemaValidator, ScriptCompositionError, type SemanticDocumentAdapter, SemanticEditor, type SemanticOpInput, type SemanticOpKind, type SemanticOpName, type SequentialClipAnchor, type SetBgmInput, type SetCaptionVisibilityInput, type SetClipPlacementInput, type SetClipSpeedInput, type SetClipVolumeInput, type SnapshotReadable, type SolvedVideoDocument, type SpeechHostMap, type SpeechPart, type SpeedShift, TIMELINE_SKELETON_DURATION_MS, type Timeline, type TimelineDoc, type TimelineItem, type Track, type TrackDraft, type TrackItem, type TrackItemDraft, type TrackItemTimePosition, type TransactAudit, type TrimClipInput, type UpdateClipInput, type UpdateClipMarkerInput, VIDEO_DOCUMENT_SCHEMA_VERSION, ValidationError, type VideoClipPart, type VideoDocument, type VideoDocumentDraft, type VideoDocumentMirrorSchema, type VideoDocumentSchemaVersion, VideoDocumentValidationError, type VideoDocumentValidationIssue, type VideoDocumentValidationIssueCode, type VideoDraft, type CaptionPart$1 as VideoDraftCaptionPart, type VideoDraftPartUnion, type Timeline$1 as VideoDraftTimeline, type Track$1 as VideoDraftTrack, type TrackItem$1 as VideoDraftTrackItem, type VideoMediaAssetFact, type VisualMediaAssetFact, type VoiceMediaAssetFact, type VoiceoverCaptionFact, type VoiceoverTakeInput, type VoiceoverTakeResult, type WaitForServerAckOptions, applyEntityTimelineProjection, arrangeMainTrackSeamlessly, assembleCaptionContent, assembleEntityContent, assemblePhoneticScriptContent, assembleScriptText, assertCanonicalEditorResources, assertEntityProjectionPreservesLegacy, assertMediaAssetWritePolicy, assertValidVideoDocument, base64ToBytes, buildInitialVideoDocument, buildSpeechHostMap, bytesToBase64, cascadeAfterVideoClipChanges, createEditSandbox, createMirrorVideoDocument, createMirrorVideoDocumentAdapter, createPlainMemoryAdapter, decodeDocVersionMark, derivePositionFromAbs, effectiveVideoClipDurationMs, encodeDocVersionMark, ensureEditorFoundation, ensureLaneTrack, fillMainTrackTimeGaps, findComposedAudioScript, findLaneTrack, fromVideoDocument, generatePartId, getAt, hostForAbsMs, importMediaAsset, isEmptyVideoClip, isImplementedSemanticOpKind, isMap, laneTrackId, mainTrackRanges, migrateLegacyTimelineToEntities, partDurationMs, partUnionSchema, prepareCaptionContent, projectEntityTimeline, readDocumentAudioScript, readMainTrackItems, readMengineEventStream, readPart, readPartDurationMs, readVideoDocumentFromDraft, reassignSpeechesToVideoClipsByTime, recalculateTimelineDuration, relativePositionForAbs, replayJournal, resolveAllSpeechOverlaps, resolveEntityTimelineLayout, resolveSpeechOverlapByShiftingVideos, safeDurationMs, index_d_exports as schemas, selectAudioScriptSegments, snapshotToPlain, solveVideoDocument, speedOf, syncAggregatedClipsTimePosition, toVideoDocument, validateVideoDocument, videoDocumentMirrorSchema, videoDocumentSchema, writeVideoDocumentToDraft };
package/dist/index.js CHANGED
@@ -218,13 +218,18 @@ function findSseFrameBoundary(buffer) {
218
218
  }
219
219
  //#endregion
220
220
  //#region src/entity-editor/document-audio-script.ts
221
- /** Resolve only the document association; other AudioScripts remain independent resources. */
221
+ /**
222
+ * Strict read of the project's one fixed AudioScript. The identity is immutable
223
+ * for the project's lifetime; this never selects, clears, or switches a
224
+ * script. Uninitialized or dangling identities are projection errors.
225
+ */
222
226
  function readDocumentAudioScript(state) {
223
227
  const id = state.audioScriptEntityId;
224
- if (id === null) return null;
225
- if (typeof id !== "string" || id.trim() !== id || !id) throw new EntityTimelineProjectionError("Invalid document AudioScript association");
228
+ if (id === null) throw new EntityTimelineProjectionError("Document AudioScript is not initialized");
229
+ if (typeof id !== "string" || id.trim() !== id || !id) throw new EntityTimelineProjectionError("Invalid document AudioScript identity");
230
+ if (state.rows.entities.filter((row) => row.entityKind === "audio-script").length !== 1) throw new EntityTimelineProjectionError("Editor requires exactly one AudioScript");
226
231
  const entity = state.rows.entities.find((row) => row.entityId === id);
227
- if (!entity || entity.entityKind !== "audio-script") throw new EntityTimelineProjectionError(`Document AudioScript ${id} must name an AudioScript in this document`);
232
+ if (!entity || entity.entityKind !== "audio-script") throw new EntityTimelineProjectionError(`Document AudioScript ${id} must name the project AudioScript`);
228
233
  const content = assembleEntityContent(state.rows, createEntityId(id));
229
234
  assembleScriptText(content);
230
235
  return {
@@ -251,6 +256,7 @@ const roleOrder$1 = {
251
256
  */
252
257
  function assertCanonicalEditorResources(rows) {
253
258
  if (rows.entities.filter((row) => row.entityKind === "timeline").length > 1) throw new EntityTimelineProjectionError("Editor requires exactly one Timeline");
259
+ if (rows.entities.filter((row) => row.entityKind === "audio-script").length !== 1) throw new EntityTimelineProjectionError("Editor requires exactly one AudioScript");
254
260
  for (const role of Object.keys(roleOrder$1)) if (rows.entities.filter((row) => row.entityKind === "track" && row.payload.role === role).length > 1) throw new EntityTimelineProjectionError(`Editor has multiple ${role} Tracks`);
255
261
  const identityByAssetKey = /* @__PURE__ */ new Map();
256
262
  const kinds = new Map(rows.entities.map((row) => [String(row.entityId), row.entityKind]));
@@ -312,6 +318,26 @@ function memotaLocator(value) {
312
318
  key: value.key
313
319
  };
314
320
  }
321
+ /**
322
+ * The project AudioScript is a fixed structure identity, like the Timeline: at
323
+ * most one may exist, and a missing one is minted atomically with empty
324
+ * segments. Project initialization and legacy migration share this single
325
+ * path, so both produce the same canonical structure.
326
+ */
327
+ function ensureDocumentAudioScript(entities, idFactory) {
328
+ const scripts = entities.filter((row) => row.entityKind === "audio-script");
329
+ if (scripts.length > 1) throw new EntityTimelineProjectionError("Editor requires exactly one AudioScript");
330
+ const existing = scripts[0];
331
+ if (existing !== void 0) return existing.entityId;
332
+ const id = createEntityId(idFactory("entity"));
333
+ if (entities.some((row) => row.entityId === id)) throw new Error(`Duplicate entity id ${id}`);
334
+ entities.push({
335
+ entityId: id,
336
+ entityKind: "audio-script",
337
+ payload: { segments: [] }
338
+ });
339
+ return id;
340
+ }
315
341
  /** Add only missing editor structure; existing identities and owned settings are never replaced. */
316
342
  function ensureEditorFoundation(initial, idFactory = (prefix) => `${prefix}_${globalThis.crypto.randomUUID()}`, timelinePayload = {}) {
317
343
  const entities = structuredClone([...initial.entities]);
@@ -364,13 +390,15 @@ function ensureEditorFoundation(initial, idFactory = (prefix) => `${prefix}_${gl
364
390
  }
365
391
  trackEntityIds[role] = track.entityId;
366
392
  }
393
+ const audioScriptEntityId = ensureDocumentAudioScript(entities, idFactory);
367
394
  return {
368
395
  rows: {
369
396
  entities,
370
397
  relations
371
398
  },
372
399
  timelineEntityId: timeline.entityId,
373
- trackEntityIds
400
+ trackEntityIds,
401
+ audioScriptEntityId
374
402
  };
375
403
  }
376
404
  //#endregion
@@ -406,10 +434,11 @@ var EntityGraphHttpClient = class {
406
434
  const baseRows = cloneAndValidateRows(baseState.rows);
407
435
  const next = cloneAndValidateRows(nextRows);
408
436
  assertMediaAssetWritePolicy(baseRows, next);
409
- const audioScriptEntityId = options.audioScriptEntityId === void 0 ? baseState.audioScriptEntityId : options.audioScriptEntityId;
437
+ const scriptRows = next.entities.filter((row) => row.entityKind === "audio-script");
438
+ if (scriptRows.length !== 1) throw new EntityTimelineProjectionError("Editor requires exactly one AudioScript");
410
439
  readDocumentAudioScript({
411
440
  rows: next,
412
- audioScriptEntityId
441
+ audioScriptEntityId: scriptRows[0].entityId
413
442
  });
414
443
  const nextEntityIds = new Set(next.entities.map((row) => row.entityId));
415
444
  const nextRelationIds = new Set(next.relations.map((row) => row.relationId));
@@ -419,7 +448,6 @@ var EntityGraphHttpClient = class {
419
448
  method: "POST",
420
449
  body: JSON.stringify({
421
450
  expected_revision: baseState.revision,
422
- audio_script_entity_id: audioScriptEntityId,
423
451
  rows: toWireRows(next),
424
452
  deleted_entity_ids: deletedEntityIds,
425
453
  deleted_relation_ids: deletedRelationIds,
@@ -442,12 +470,11 @@ function parseWireState(value, expectedDocId) {
442
470
  if (value.doc_id !== expectedDocId) throw new Error(`Entity-state response doc_id mismatch: expected "${expectedDocId}"`);
443
471
  const rows = parseWireRows(value.rows);
444
472
  decodeEntityRelationRows(rows, numericMarkerValidation$1);
445
- const audioScriptEntityId = value.audio_script_entity_id;
446
- if (audioScriptEntityId !== null && typeof audioScriptEntityId !== "string") throw new Error("Invalid document AudioScript association");
473
+ if (typeof value.audio_script_entity_id !== "string") throw new Error("Document AudioScript is not initialized");
447
474
  const state = {
448
475
  revision: value.revision,
449
476
  rows,
450
- audioScriptEntityId
477
+ audioScriptEntityId: value.audio_script_entity_id
451
478
  };
452
479
  readDocumentAudioScript(state);
453
480
  return state;
@@ -1961,6 +1988,7 @@ function migrateLegacyTimelineToEntities(legacy, assetFacts, initial = {
1961
1988
  const normalizedMainRanges = overlapNormalizedMainRanges(legacy);
1962
1989
  let entities = structuredClone([...initial.entities]);
1963
1990
  let relations = structuredClone([...initial.relations]);
1991
+ const documentAudioScriptEntityId = ensureDocumentAudioScript(entities, idFactory);
1964
1992
  const addEntity = (kind, payload, existingId) => {
1965
1993
  const entityId = createEntityId(existingId ?? idFactory("entity"));
1966
1994
  if (entities.some((row) => row.entityId === entityId)) fail(`Migration entity identity collision: ${entityId}`);
@@ -2023,7 +2051,7 @@ function migrateLegacyTimelineToEntities(legacy, assetFacts, initial = {
2023
2051
  relations = [...imported.rows.relations];
2024
2052
  contentByPartId.set(item.part_id, imported.contentEntityId);
2025
2053
  const phonemeScript = speech.phoneme_script;
2026
- ensureSpeechPhoneticChain(imported.contentEntityId, speech.audio_script, typeof phonemeScript === "string" ? phonemeScript : void 0, entities, relations, addEntity, link);
2054
+ ensureSpeechPhoneticChain(imported.contentEntityId, speech.audio_script, typeof phonemeScript === "string" ? phonemeScript : void 0, entities, relations, addEntity, link, documentAudioScriptEntityId);
2027
2055
  } else if (part.bgm !== void 0) {
2028
2056
  const bgm = part.bgm;
2029
2057
  const fact = facts.get(bgm.origin_media_id);
@@ -2469,7 +2497,7 @@ function requirePart(document, partId) {
2469
2497
  if (part === void 0) fail(`Placed Clip ${partId} has no part payload`);
2470
2498
  return part;
2471
2499
  }
2472
- function ensureSpeechPhoneticChain(voiceEntityId, audioScriptText, phonemeScript, entities, relations, addEntity, link) {
2500
+ function ensureSpeechPhoneticChain(voiceEntityId, audioScriptText, phonemeScript, entities, relations, addEntity, link, documentAudioScriptEntityId) {
2473
2501
  const renders = relations.filter((relation) => relation.relationKind === "phonetic-script-render" && relation.endpoint0EntityId === voiceEntityId);
2474
2502
  if (renders.length > 1) fail(`Voice ${voiceEntityId} has ambiguous PhoneticScript provenance`);
2475
2503
  if (renders.length === 1) {
@@ -2479,11 +2507,23 @@ function ensureSpeechPhoneticChain(voiceEntityId, audioScriptText, phonemeScript
2479
2507
  if (script === void 0 || scriptText(script) !== audioScriptText) fail(`Voice ${voiceEntityId} conflicts with the legacy Speech script`);
2480
2508
  return phonetic.entityId;
2481
2509
  }
2510
+ const script = entities.find((entity) => entity.entityId === documentAudioScriptEntityId);
2511
+ if (script === void 0 || script.entityKind !== "audio-script") fail("Migration must carry the fixed document AudioScript");
2512
+ const segments = script.payload.segments;
2513
+ if (Array.isArray(segments) && segments.length > 0) {
2514
+ if (scriptText(script) !== audioScriptText) fail(`Voice ${voiceEntityId} conflicts with the project AudioScript text`);
2515
+ } else {
2516
+ const scriptIndex = entities.findIndex((row) => row.entityId === script.entityId);
2517
+ entities[scriptIndex] = {
2518
+ ...script,
2519
+ payload: { segments: [{
2520
+ segmentId: "segment-0",
2521
+ text: audioScriptText
2522
+ }] }
2523
+ };
2524
+ }
2482
2525
  const phonetic = addEntity("phonetic-script", {
2483
- baseEntityIds: [addEntity("audio-script", { segments: [{
2484
- segmentId: "segment-0",
2485
- text: audioScriptText
2486
- }] })],
2526
+ baseEntityIds: [script.entityId],
2487
2527
  ...phonemeScript === void 0 ? {} : { phonemeScript }
2488
2528
  });
2489
2529
  link("phonetic-script-render", voiceEntityId, phonetic);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mengine/medeo-client",
3
- "version": "1.3.1-alpha.8",
3
+ "version": "1.3.1-alpha.9",
4
4
  "license": "UNLICENSED",
5
5
  "repository": {
6
6
  "type": "git",
@@ -27,9 +27,9 @@
27
27
  "immer": "^10.2.0",
28
28
  "loro-mirror": "^2.2.0",
29
29
  "zod": "^4.4.3",
30
- "@mengine/storage": "1.3.1-alpha.8",
31
- "@mengine/sync": "1.3.1-alpha.8",
32
- "@mengine/utils": "1.3.1-alpha.8"
30
+ "@mengine/storage": "1.3.1-alpha.9",
31
+ "@mengine/sync": "1.3.1-alpha.9",
32
+ "@mengine/utils": "1.3.1-alpha.9"
33
33
  },
34
34
  "devDependencies": {
35
35
  "@types/node": "^25.9.1",