@mengine/medeo-tool 1.3.1-alpha.9 → 1.4.1-alpha.1

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.
@@ -1,11 +1,5 @@
1
1
  //#region src/sandbox/generated/entity-edit-sandbox-api.d.ts
2
2
  /** @generated by gen:sandbox-dts. Entity-native editor contract; DO NOT EDIT. */
3
- interface AudioMediaAssetFact {
4
- readonly assetId: string;
5
- readonly kind: 'audio';
6
- readonly durationMs: number;
7
- readonly storageKey: string;
8
- }
9
3
  interface BoundedDerivedSequencePayload extends JsonObject {
10
4
  extent: {
11
5
  kind: 'bounded';
@@ -25,6 +19,32 @@ interface BoundedNativeSequencePayload extends JsonObject {
25
19
  sampling: 'native';
26
20
  coordinateSpace: JsonValue;
27
21
  }
22
+ /** Business editing surface. Infrastructure Assets are assembled by the host. */
23
+ interface BusinessEntityFacade {
24
+ list(): SandboxEntity[];
25
+ get(entityId: string): SandboxEntity | null;
26
+ readCaptionContent(entityId: string): ComposedScriptContent;
27
+ readPhoneticScriptContent(entityId: string): ComposedPhoneticContent;
28
+ create(input: Exclude<CreateEntityInput, {
29
+ entity_kind: 'asset';
30
+ }>): string;
31
+ update(input: UpdateEntityInput): void;
32
+ declareFields(input: UpdateEntityInput): void;
33
+ delete(input: DeleteEntityInput): void;
34
+ }
35
+ /** Physical Asset bindings are maintained outside the sandbox. */
36
+ interface BusinessRelationFacade {
37
+ list(): SandboxRelation[];
38
+ of(entityId: string, relationKind?: KnownRelationKind): SandboxRelation[];
39
+ link(input: Exclude<LinkRelationInput, {
40
+ relation_kind: 'physical-asset';
41
+ }>): string;
42
+ linkGenerated(input: LinkGeneratedRelationInput): string;
43
+ linkClipAnchor(input: LinkClipAnchorRelationInput): string;
44
+ linkPhoneticScriptRender(input: LinkPhoneticScriptRenderRelationInput): string;
45
+ linkAudioScriptSource(input: LinkAudioScriptSourceRelationInput): string;
46
+ unlink(input: UnlinkRelationInput): void;
47
+ }
28
48
  interface CaptionFontDescriptor {
29
49
  readonly system: 'font-library';
30
50
  readonly key: string;
@@ -114,28 +134,6 @@ interface DeleteVoiceoverInput {
114
134
  readonly voiceoverClipEntityIds: readonly string[];
115
135
  }
116
136
  type EmptyRelationKind = 'timeline-track' | 'track-clip' | 'clip-marker' | 'marker-content' | 'axvideo-marker' | 'marker-timeline' | 'audio-script-marker';
117
- interface EntityFacade {
118
- /** Read complete assembled fields; returned objects are snapshots. Use update to persist edits. */
119
- list(): SandboxEntity[];
120
- get(entityId: string): SandboxEntity | null;
121
- /** Find document resources by external Memota asset id, including directly composed media variants. */
122
- findByAssetId(assetId: string): SandboxEntity<ResourceEntityKind>[];
123
- /** Assemble selected Caption text; missing composition is an error. */
124
- readCaptionContent(entityId: string): ComposedScriptContent;
125
- /** Assemble base text and pronunciation fields before generating Voice. */
126
- readPhoneticScriptContent(entityId: string): ComposedPhoneticContent;
127
- create(input: CreateEntityInput): string;
128
- /** Patch assembled fields, routing inherited fields to their declaring entity. */
129
- update(input: UpdateEntityInput): void;
130
- /** Explicitly declare own fields, overriding unambiguous bases without modifying them. Ordinary edits use update. */
131
- declareFields(input: UpdateEntityInput): void;
132
- /** Delete an Entity only after all of its incident Relations have been explicitly unlinked. */
133
- delete(input: DeleteEntityInput): void;
134
- /** Get or create one typed Asset by factual external id and return its single content identity. Never creates a Clip. */
135
- ensureMedia(fact: MediaAssetFact): {
136
- contentEntityId: string;
137
- };
138
- }
139
137
  type EntityId = string;
140
138
  interface EntityPayloadByKind {
141
139
  axvideo: BoundedDerivedSequencePayload;
@@ -145,12 +143,10 @@ interface EntityPayloadByKind {
145
143
  role?: string;
146
144
  };
147
145
  clip: JsonObject;
148
- /** Physical resource fields; never a copy of Caption content. */
149
- asset: JsonObject;
150
- video: BoundedNativeSequencePayload & MediaAssetPayload;
151
- audio: BoundedNativeSequencePayload & MediaAssetPayload;
152
- voice: BoundedNativeSequencePayload & MediaAssetPayload;
153
- image: UnboundedConstantSequencePayload & MediaAssetPayload;
146
+ video: BoundedNativeSequencePayload;
147
+ audio: BoundedNativeSequencePayload;
148
+ voice: BoundedNativeSequencePayload;
149
+ image: UnboundedConstantSequencePayload;
154
150
  'sequence-marker': JsonObject & {
155
151
  sourceRange: {
156
152
  start: number;
@@ -191,19 +187,18 @@ interface EntityPayloadByKind {
191
187
  };
192
188
  }
193
189
  interface EntityStoreSnapshot {
190
+ /** Causal compiler baseline; required for publishing edits. */
191
+ loroSnapshot?: string;
194
192
  revision: number;
195
- /** Fixed identity outlet of the project's one AudioScript; null only before initialization (revision 0). */
193
+ /** Current AudioScript version attached to the project; initialized projects always attach a script, possibly empty. */
196
194
  audioScriptEntityId: string | null;
197
195
  entities: SandboxEntity[];
198
196
  relations: SandboxRelation[];
199
197
  }
200
- interface ImageMediaAssetFact {
201
- readonly assetId: string;
202
- readonly kind: 'image';
203
- readonly storageKey?: string;
204
- }
205
198
  interface InsertCaptionClipInput {
206
199
  readonly timelineEntityId: string;
200
+ /** Existing generation identity for newly materialized Caption content, distinct from its Clip. */
201
+ readonly captionEntityId?: string;
207
202
  /** Stable placed caption identity, distinct from the Caption content identity. */
208
203
  readonly captionClipEntityId?: string;
209
204
  /** Existing bases composed by this variant; includes an AudioScript text owner. */
@@ -224,21 +219,6 @@ interface InsertClipInput {
224
219
  readonly targetRange?: SequenceRange<number>;
225
220
  readonly clipPayload?: JsonObject;
226
221
  }
227
- interface InsertMediaClipInput {
228
- readonly timelineEntityId: string;
229
- readonly clipEntityId?: string;
230
- readonly media: VisualMediaAssetFact;
231
- /** Source/display window in whole milliseconds. Images use this as their finite display span. */
232
- readonly sourceRange: SequenceRange<number>;
233
- readonly placement: ClipPlacement;
234
- readonly volume?: number;
235
- }
236
- interface InsertMediaClipsInput {
237
- readonly timelineEntityId: string;
238
- readonly clips: readonly ReplacementMediaClipInput[];
239
- /** One placement decision for the whole input-ordered block. */
240
- readonly insertion: MediaClipInsertion;
241
- }
242
222
  interface InsertPlacedClipInput {
243
223
  readonly trackEntityId: string;
244
224
  readonly contentEntityId: string;
@@ -254,8 +234,8 @@ interface JsonObject {
254
234
  }
255
235
  type JsonPrimitive = string | number | boolean | null;
256
236
  type JsonValue = JsonPrimitive | JsonObject | JsonValue[];
257
- type KnownEntityKind = 'axvideo' | 'timeline' | 'track' | 'clip' | 'asset' | 'video' | 'audio' | 'voice' | 'image' | 'sequence-marker' | 'viewport' | 'audio-script' | 'phonetic-script' | 'caption';
258
- type KnownRelationKind = 'timeline-track' | 'track-clip' | 'clip-marker' | 'marker-content' | 'axvideo-marker' | 'marker-timeline' | 'physical-asset' | 'generated' | 'caption-alignment' | 'clip-anchor' | 'phonetic-script-render' | 'audio-script-source' | 'audio-script-marker';
237
+ type KnownEntityKind = 'axvideo' | 'timeline' | 'track' | 'clip' | 'video' | 'audio' | 'voice' | 'image' | 'sequence-marker' | 'viewport' | 'audio-script' | 'phonetic-script' | 'caption';
238
+ type KnownRelationKind = 'timeline-track' | 'track-clip' | 'clip-marker' | 'marker-content' | 'axvideo-marker' | 'marker-timeline' | 'generated' | 'caption-alignment' | 'clip-anchor' | 'phonetic-script-render' | 'audio-script-source' | 'audio-script-marker';
259
239
  interface LinearClipSpeed {
260
240
  readonly kind: 'linear';
261
241
  readonly rate: number;
@@ -306,25 +286,6 @@ type LinkRelationInput = (LinkRelationBase & {
306
286
  alignment: JsonValue;
307
287
  };
308
288
  });
309
- /** Facts resolved from media storage. A trim window never substitutes for intrinsic duration. */
310
- type MediaAssetFact = ImageMediaAssetFact | VideoMediaAssetFact | AudioMediaAssetFact | VoiceMediaAssetFact;
311
- type MediaAssetPayload = JsonObject & {
312
- external: {
313
- system: 'memota' | 'memota-speech';
314
- key: string;
315
- };
316
- storageKey?: string;
317
- };
318
- type MediaClipInsertion = {
319
- readonly kind: 'before';
320
- readonly clipEntityId: string;
321
- } | {
322
- readonly kind: 'after';
323
- readonly clipEntityId: string;
324
- } | {
325
- readonly kind: 'firstStart';
326
- readonly startMs: number;
327
- };
328
289
  interface MoveClipInput {
329
290
  readonly clipEntityId: string;
330
291
  readonly trackEntityId: string;
@@ -351,23 +312,6 @@ interface PatchCaptionStyleInput {
351
312
  readonly timelineEntityId: string;
352
313
  readonly style: CaptionStyleFields;
353
314
  }
354
- interface RelationFacade {
355
- list(): SandboxRelation[];
356
- /** Incident lookup is endpoint-agnostic; persisted endpoint positions stay unchanged. */
357
- of(entityId: string, relationKind?: KnownRelationKind): SandboxRelation[];
358
- /** Link existing entities through ordinary associations; variant bases are stored directly on the variant. */
359
- link(input: LinkRelationInput): string;
360
- /** Author ordered generated(output,input); generic link() deliberately rejects this kind. */
361
- linkGenerated(input: LinkGeneratedRelationInput): string;
362
- /** Author ordered clip-anchor(child,host) without positional endpoint ambiguity. */
363
- linkClipAnchor(input: LinkClipAnchorRelationInput): string;
364
- /** Author ordered phonetic-script-render(output,script) without positional endpoint ambiguity. */
365
- linkPhoneticScriptRender(input: LinkPhoneticScriptRenderRelationInput): string;
366
- /** Author ordered audio-script-source(script,source) without positional endpoint ambiguity. */
367
- linkAudioScriptSource(input: LinkAudioScriptSourceRelationInput): string;
368
- /** Remove a Relation by identity; endpoint replacement is an explicit unlink plus link. */
369
- unlink(input: UnlinkRelationInput): void;
370
- }
371
315
  interface ReplaceClipContentInput {
372
316
  readonly clipEntityId: string;
373
317
  /** Existing Sequence media Entity id. Asset ids and URLs are not content ids. */
@@ -377,25 +321,6 @@ interface ReplaceClipContentInput {
377
321
  readonly targetRange?: SequenceRange<number>;
378
322
  readonly timeRemapping?: JsonValue;
379
323
  }
380
- interface ReplaceMediaClipInput {
381
- readonly clipEntityId: string;
382
- readonly media: VisualMediaAssetFact;
383
- readonly sourceRange: SequenceRange<number>;
384
- }
385
- interface ReplaceSequentialClipsInput {
386
- readonly timelineEntityId: string;
387
- readonly oldClipEntityIds: readonly string[];
388
- readonly newClips: readonly ReplacementMediaClipInput[];
389
- readonly onAnchored: 'remap' | 'cascade';
390
- }
391
- interface ReplacementMediaClipInput {
392
- readonly clipEntityId?: string;
393
- readonly media: VisualMediaAssetFact;
394
- readonly sourceRange: SequenceRange<number>;
395
- readonly volume?: number;
396
- }
397
- /** Asset identity, either an old physical-only row or a directly composed media variant. */
398
- type ResourceEntityKind = 'image' | 'video' | 'audio' | 'voice';
399
324
  interface SandboxEntity<K extends KnownEntityKind = KnownEntityKind> {
400
325
  entity_id: string;
401
326
  entity_kind: K;
@@ -430,12 +355,6 @@ type SequentialClipAnchor = {
430
355
  } | {
431
356
  readonly position: 'trackStart';
432
357
  };
433
- interface SetBgmInput {
434
- readonly timelineEntityId: string;
435
- readonly bgmClipEntityId: string;
436
- readonly media: AudioMediaAssetFact;
437
- readonly volume: number;
438
- }
439
358
  interface SetCaptionVisibilityInput {
440
359
  readonly timelineEntityId: string;
441
360
  readonly hidden: boolean;
@@ -491,63 +410,6 @@ interface UpdateEntityInput {
491
410
  entity_id: string;
492
411
  payload: JsonObject;
493
412
  }
494
- interface VideoMediaAssetFact {
495
- readonly assetId: string;
496
- readonly kind: 'video';
497
- readonly durationMs: number;
498
- readonly storageKey?: string;
499
- }
500
- type VisualMediaAssetFact = ImageMediaAssetFact | VideoMediaAssetFact;
501
- interface VoiceDescriptor {
502
- readonly system: 'voice-library';
503
- readonly key: string;
504
- readonly name?: string;
505
- }
506
- interface VoiceMediaAssetFact {
507
- /** Stable external speech result id, independent of the placed Clip id. */
508
- readonly assetId: string;
509
- readonly kind: 'voice';
510
- readonly durationMs: number;
511
- readonly storageKey: string;
512
- /** Present for synthesized voice, absent for original recorded audio. */
513
- readonly voice?: VoiceDescriptor;
514
- }
515
- interface VoiceoverCaptionFact {
516
- /** Stable placed caption identity supplied by the materialized side effect. */
517
- readonly captionClipEntityId: string;
518
- /** Directly held bases; includes the AudioScript used by the Voice. */
519
- readonly baseEntityIds: readonly string[];
520
- /** Ordered selection of AudioScript segments; caption text is never passed inline. */
521
- readonly selections: readonly CaptionSegmentSelection[];
522
- readonly startMs: number;
523
- readonly durationMs: number;
524
- readonly style?: CaptionStyleFields;
525
- }
526
- type VoiceoverTakeInput = {
527
- readonly timelineEntityId: string; /** Stable placed speech identity, distinct from media.assetId. */
528
- readonly voiceoverClipEntityId: string;
529
- readonly media: VoiceMediaAssetFact; /** Existing pronunciation variant; its composed AudioScript stays the text owner. */
530
- readonly phoneticScriptEntityId: string;
531
- readonly volume: number;
532
- readonly captions: readonly VoiceoverCaptionFact[];
533
- } & ({
534
- readonly placement: ClipPlacement;
535
- readonly hostClipEntityId?: never;
536
- readonly anchorOffset?: never;
537
- } | {
538
- readonly placement?: never;
539
- readonly hostClipEntityId: string;
540
- readonly anchorOffset: number;
541
- });
542
- interface VoiceoverTakeResult {
543
- readonly voiceoverClipEntityId: string;
544
- readonly voiceEntityId: string;
545
- /** The pronunciation variant the Voice was rendered from. */
546
- readonly phoneticScriptEntityId: string;
547
- /** The base-text owner resolved from the PhoneticScript baseEntityIds. */
548
- readonly audioScriptEntityId: string;
549
- readonly captionClipEntityIds: readonly string[];
550
- }
551
413
  /** Timeline writes accept existing media Entity ids, never Memota asset ids or URLs. */
552
414
  interface EditApi {
553
415
  insertClip(input: InsertClipInput): ClipEntityId;
@@ -557,21 +419,15 @@ interface EditApi {
557
419
  moveSequentialClips(input: MoveSequentialClipsInput): void;
558
420
  moveClip(input: MoveClipInput): void;
559
421
  replaceClipContent(input: ReplaceClipContentInput): void;
560
- insertMediaClip(input: InsertMediaClipInput): ClipEntityId;
561
- insertMediaClips(input: InsertMediaClipsInput): readonly ClipEntityId[];
562
- replaceMediaClip(input: ReplaceMediaClipInput): void;
563
422
  setClipVolume(input: SetClipVolumeInput): void;
564
423
  setClipSpeed(input: SetClipSpeedInput): void;
565
424
  trimClip(input: TrimClipInput): void;
566
- replaceSequentialClips(input: ReplaceSequentialClipsInput): readonly ClipEntityId[];
567
425
  deleteClip(input: DeleteClipInput): void;
568
426
  deleteClipTree(input: DeleteClipTreeInput): void;
569
427
  updateClip(input: UpdateClipInput): void;
570
- upsertVoiceoverTake(input: VoiceoverTakeInput): VoiceoverTakeResult;
571
428
  moveVoiceover(input: MoveVoiceoverInput): void;
572
429
  moveClipsToStarts(input: MoveClipsToStartsInput): void;
573
430
  deleteVoiceover(input: DeleteVoiceoverInput): void;
574
- setBgm(input: SetBgmInput): ClipEntityId;
575
431
  deleteBgm(input: DeleteBgmInput): void;
576
432
  setCaptionVisibility(input: SetCaptionVisibilityInput): void;
577
433
  patchCaptionStyle(input: PatchCaptionStyleInput): void;
@@ -587,11 +443,11 @@ interface SandboxCheckpoint {
587
443
  }
588
444
  declare const edit: EditApi;
589
445
  declare const timeline: TimelineApi;
590
- declare const entities: EntityFacade;
591
- declare const relations: RelationFacade;
446
+ declare const entities: BusinessEntityFacade;
447
+ declare const relations: BusinessRelationFacade;
592
448
  declare function checkpoint(): SandboxCheckpoint;
593
449
  declare function rollbackTo(cp: SandboxCheckpoint): void;
594
450
  declare const inputs: Readonly<Record<string, unknown>>;
595
451
  //#endregion
596
- export type { AudioMediaAssetFact, BoundedDerivedSequencePayload, BoundedNativeSequencePayload, CaptionFontDescriptor, CaptionSegmentSelection, CaptionStyleFields, CaptionTextRange, CaptionTextSelection, ClipEntityId, ClipPlacement, ComposedPhoneticContent, ComposedScriptContent, CreateEntityInput, DeleteBgmInput, DeleteClipInput, DeleteClipTreeInput, DeleteEntityInput, DeleteVoiceoverInput, EditApi, EmptyRelationKind, EntityFacade, EntityId, EntityPayloadByKind, EntityStoreSnapshot, ImageMediaAssetFact, InsertCaptionClipInput, InsertClipInput, InsertMediaClipInput, InsertMediaClipsInput, InsertPlacedClipInput, JsonObject, JsonPrimitive, JsonValue, KnownEntityKind, KnownRelationKind, LinearClipSpeed, LinkAudioScriptSourceRelationInput, LinkClipAnchorRelationInput, LinkGeneratedRelationInput, LinkPhoneticScriptRenderRelationInput, LinkRelationInput, MediaAssetFact, MediaAssetPayload, MediaClipInsertion, MoveClipInput, MoveClipsToStartsInput, MoveSequentialClipsInput, MoveVoiceoverInput, PatchCaptionStyleInput, RelationFacade, ReplaceClipContentInput, ReplaceMediaClipInput, ReplaceSequentialClipsInput, ReplacementMediaClipInput, ResourceEntityKind, SandboxCheckpoint, SandboxEntity, SandboxRelation, ScriptTextSegment, SequenceDuration, SequenceRange, SequentialClipAnchor, SetBgmInput, SetCaptionVisibilityInput, SetClipPlacementInput, SetClipSpeedInput, SetClipVolumeInput, StoredEntityPayload, TimelineApi, TrimClipInput, UnboundedConstantSequencePayload, UnlinkRelationInput, UpdateClipInput, UpdateClipMarkerInput, UpdateEntityInput, VideoMediaAssetFact, VisualMediaAssetFact, VoiceDescriptor, VoiceMediaAssetFact, VoiceoverCaptionFact, VoiceoverTakeInput, VoiceoverTakeResult, checkpoint, edit, entities, inputs, relations, rollbackTo, timeline };
452
+ export type { BoundedDerivedSequencePayload, BoundedNativeSequencePayload, BusinessEntityFacade, BusinessRelationFacade, CaptionFontDescriptor, CaptionSegmentSelection, CaptionStyleFields, CaptionTextRange, CaptionTextSelection, ClipEntityId, ClipPlacement, ComposedPhoneticContent, ComposedScriptContent, CreateEntityInput, DeleteBgmInput, DeleteClipInput, DeleteClipTreeInput, DeleteEntityInput, DeleteVoiceoverInput, EditApi, EmptyRelationKind, EntityId, EntityPayloadByKind, EntityStoreSnapshot, InsertCaptionClipInput, InsertClipInput, InsertPlacedClipInput, JsonObject, JsonPrimitive, JsonValue, KnownEntityKind, KnownRelationKind, LinearClipSpeed, LinkAudioScriptSourceRelationInput, LinkClipAnchorRelationInput, LinkGeneratedRelationInput, LinkPhoneticScriptRenderRelationInput, LinkRelationInput, MoveClipInput, MoveClipsToStartsInput, MoveSequentialClipsInput, MoveVoiceoverInput, PatchCaptionStyleInput, ReplaceClipContentInput, SandboxCheckpoint, SandboxEntity, SandboxRelation, ScriptTextSegment, SequenceDuration, SequenceRange, SequentialClipAnchor, SetCaptionVisibilityInput, SetClipPlacementInput, SetClipSpeedInput, SetClipVolumeInput, StoredEntityPayload, TimelineApi, TrimClipInput, UnboundedConstantSequencePayload, UnlinkRelationInput, UpdateClipInput, UpdateClipMarkerInput, UpdateEntityInput, checkpoint, edit, entities, inputs, relations, rollbackTo, timeline };
597
453
  //# sourceMappingURL=sandbox-api.d.mts.map
@@ -183,7 +183,7 @@ function createId(value, label) {
183
183
  //#region ../medeo-dsl/src/json-values.ts
184
184
  const nativeObjectConstructorSource = Function.prototype.toString.call(Object);
185
185
  function isJsonObject(value) {
186
- return isJsonValue(value, /* @__PURE__ */ new Set()) && !Array.isArray(value) && value !== null;
186
+ return typeof value === "object" && value !== null && !Array.isArray(value) && isJsonValue(value, /* @__PURE__ */ new Set());
187
187
  }
188
188
  function isJsonValue(value, ancestors) {
189
189
  if (value === null || typeof value === "string" || typeof value === "boolean") return true;
@@ -422,9 +422,8 @@ function entityToRow(entity) {
422
422
  //#region ../medeo-dsl/src/invariants.ts
423
423
  /**
424
424
  * Media variants own their Asset identity directly: a `physical-asset`
425
- * Relation may not bind them. Caption text is composed domain content, so
426
- * Captions do not bind Physical Assets either; the Relation vocabulary stays
427
- * open for future physical pipeline semantics.
425
+ * Relation may not bind them. Caption may associate with a physical text Asset
426
+ * independently of its AudioScript composition.
428
427
  */
429
428
  function validateMediaAssetIdentity(entity, index) {
430
429
  return [...index.relationsOf(entity)].filter((relation) => relation.kind === "physical-asset").map(() => ({
@@ -583,11 +582,6 @@ function validatePhoneticComposition(phonetic, index, entities = []) {
583
582
  }
584
583
  function validateVariantComposition(variant, index, entities, kind) {
585
584
  const issues = [];
586
- if (kind === "caption" && ofKind([...index.relationsOf(variant)], "physical-asset").length > 0) issues.push({
587
- code: "caption_physical_asset_forbidden",
588
- entityId: variant.entityId,
589
- message: "Caption text is composed from its AudioScript; Caption must not bind a Physical Asset"
590
- });
591
585
  const rows = {
592
586
  entities: collectRelatedEntities([...entities, variant], index).entities.map((ref) => entityToRow(ref.current())),
593
587
  relations: []
@@ -1523,6 +1517,98 @@ function errorMessage(error) {
1523
1517
  return error instanceof Error ? error.message : String(error);
1524
1518
  }
1525
1519
  //#endregion
1520
+ //#region src/sandbox/business-facades.ts
1521
+ /** Resource IDs are entity data; physical storage paths remain host-owned. */
1522
+ const infrastructureFields = new Set(["storageKey"]);
1523
+ function businessEntity(entity) {
1524
+ return {
1525
+ ...entity,
1526
+ payload: Object.fromEntries(Object.entries(entity.payload).filter(([key]) => !infrastructureFields.has(key)))
1527
+ };
1528
+ }
1529
+ function businessState(state) {
1530
+ const assets = new Set(state.entities.filter((row) => row.entity_kind === "asset").map((row) => row.entity_id));
1531
+ return {
1532
+ revision: state.revision,
1533
+ audioScriptEntityId: state.audioScriptEntityId,
1534
+ entities: state.entities.filter((row) => !assets.has(row.entity_id)).map(businessEntity),
1535
+ relations: state.relations.filter((row) => row.relation_kind !== "physical-asset" && !assets.has(row.endpoint_0_entity_id) && !assets.has(row.endpoint_1_entity_id))
1536
+ };
1537
+ }
1538
+ /** Keep infrastructure rows available to assembly without exposing their management to scripts. */
1539
+ function businessFacades(entities, relations) {
1540
+ const isAsset = (id) => entities.get(id)?.entity_kind === "asset";
1541
+ const assertBusiness = (id) => {
1542
+ if (isAsset(id)) throw new Error("Asset entities are managed by host assembly");
1543
+ };
1544
+ const assertBusinessPayload = (payload) => {
1545
+ for (const field of infrastructureFields) if (Object.hasOwn(payload, field)) throw new Error(`Field ${field} is managed by host assembly`);
1546
+ if (Array.isArray(payload.baseEntityIds)) {
1547
+ for (const id of payload.baseEntityIds) if (typeof id === "string") assertBusiness(id);
1548
+ }
1549
+ };
1550
+ const visibleRelation = (relation) => relation.relation_kind !== "physical-asset" && !isAsset(relation.endpoint_0_entity_id) && !isAsset(relation.endpoint_1_entity_id);
1551
+ return {
1552
+ entities: {
1553
+ list: () => entities.list().filter((entity) => entity.entity_kind !== "asset").map(businessEntity),
1554
+ get: (id) => {
1555
+ const entity = entities.get(id);
1556
+ return entity && !isAsset(id) ? businessEntity(entity) : null;
1557
+ },
1558
+ create: (input) => {
1559
+ if (input.entity_kind === "asset") throw new Error("Asset entities are managed by host assembly");
1560
+ assertBusinessPayload(input.payload);
1561
+ return entities.create(input);
1562
+ },
1563
+ update: (input) => {
1564
+ assertBusiness(input.entity_id);
1565
+ assertBusinessPayload(input.payload);
1566
+ entities.update(input);
1567
+ },
1568
+ declareFields: (input) => {
1569
+ assertBusiness(input.entity_id);
1570
+ assertBusinessPayload(input.payload);
1571
+ entities.declareFields(input);
1572
+ },
1573
+ delete: (input) => {
1574
+ assertBusiness(input.entity_id);
1575
+ const incident = relations.of(input.entity_id);
1576
+ if (incident.some(visibleRelation)) {
1577
+ entities.delete(input);
1578
+ return;
1579
+ }
1580
+ for (const relation of incident) relations.unlink({ relation_id: relation.relation_id });
1581
+ entities.delete(input);
1582
+ },
1583
+ readCaptionContent: (input) => entities.readCaptionContent(input),
1584
+ readPhoneticScriptContent: (input) => entities.readPhoneticScriptContent(input)
1585
+ },
1586
+ relations: {
1587
+ list: () => relations.list().filter(visibleRelation),
1588
+ of: (id, kind) => relations.of(id, kind).filter(visibleRelation),
1589
+ link: (input) => {
1590
+ if (input.relation_kind === "physical-asset") throw new Error("Asset bindings are managed by host assembly");
1591
+ assertBusiness(input.endpoint_0_entity_id);
1592
+ assertBusiness(input.endpoint_1_entity_id);
1593
+ return relations.link(input);
1594
+ },
1595
+ unlink: (input) => {
1596
+ const relation = relations.list().find((row) => row.relation_id === input.relation_id);
1597
+ if (relation && !visibleRelation(relation)) throw new Error("Asset bindings are managed by host assembly");
1598
+ relations.unlink(input);
1599
+ },
1600
+ linkGenerated: (input) => {
1601
+ assertBusiness(input.output_entity_id);
1602
+ assertBusiness(input.input_entity_id);
1603
+ return relations.linkGenerated(input);
1604
+ },
1605
+ linkClipAnchor: (input) => relations.linkClipAnchor(input),
1606
+ linkPhoneticScriptRender: (input) => relations.linkPhoneticScriptRender(input),
1607
+ linkAudioScriptSource: (input) => relations.linkAudioScriptSource(input)
1608
+ }
1609
+ };
1610
+ }
1611
+ //#endregion
1526
1612
  //#region src/entity/entity-sandbox.ts
1527
1613
  const ASSET_SOURCE_KEY = "external";
1528
1614
  const MEMOTA_SYSTEM = "memota";
@@ -2386,6 +2472,6 @@ function replayJournalSync(adapter, journal) {
2386
2472
  }
2387
2473
  }
2388
2474
  //#endregion
2389
- export { createRelationId as a, renderPreview as c, createEntityId as i, renderCompactProjection as l, EntitySandbox as n, isMediaAssetVariantKind as o, toDslRows as r, collectAffectedPartIds as s, EditSandboxSession as t };
2475
+ export { createEntityId as a, collectAffectedPartIds as c, businessState as i, renderPreview as l, EntitySandbox as n, createRelationId as o, businessFacades as r, isMediaAssetVariantKind as s, EditSandboxSession as t, renderCompactProjection as u };
2390
2476
 
2391
- //# sourceMappingURL=script-session-B2UQ1W_y.mjs.map
2477
+ //# sourceMappingURL=script-session-AukLN7x7.mjs.map