@mengine/medeo-tool 2.0.1-alpha.12 → 2.0.1-alpha.13
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/{entity-contract-DNIIwpn1.d.mts → entity-contract-Dy0cozPt.d.mts} +5 -5
- package/dist/{entity-sandbox-DpIkxcei.mjs → entity-sandbox-CulJBYrI.mjs} +46 -32
- package/dist/entity-sandbox-CulJBYrI.mjs.map +1 -0
- package/dist/index.d.mts +1 -1
- package/dist/index.mjs +10 -5
- package/dist/index.mjs.map +1 -1
- package/dist/sandbox-api.d.mts +2 -2
- package/dist/worker-entry.d.mts +1 -1
- package/dist/worker-entry.mjs +33 -7
- package/dist/worker-entry.mjs.map +1 -1
- package/package.json +2 -2
- package/dist/entity-sandbox-DpIkxcei.mjs.map +0 -1
|
@@ -9,7 +9,7 @@ interface JsonObject {
|
|
|
9
9
|
type KnownEntityKind = 'axvideo' | 'timeline' | 'track' | 'clip' | 'asset' | 'video' | 'audio' | 'voice' | 'image' | 'sequence-marker' | 'viewport' | 'audio-script' | 'phonetic-script' | 'caption';
|
|
10
10
|
/** Asset identity, either an old physical-only row or a directly composed media variant. */
|
|
11
11
|
type ResourceEntityKind = 'image' | 'video' | 'audio';
|
|
12
|
-
type KnownRelationKind = 'timeline-track' | 'track-clip' | 'clip-marker' | 'marker-content' | 'axvideo-marker' | 'marker-timeline' | 'from-asset' | 'generated' | 'caption-alignment' | 'clip-anchor' | 'phonetic-script-render' | 'voice-timbre' | 'audio-script-source' | 'audio-script-marker';
|
|
12
|
+
type KnownRelationKind = 'timeline-track' | 'track-clip' | 'clip-marker' | 'marker-content' | 'axvideo-marker' | 'marker-timeline' | 'from-asset' | 'to-asset' | 'generated' | 'caption-alignment' | 'clip-anchor' | 'phonetic-script-render' | 'voice-timbre' | 'audio-script-source' | 'audio-script-marker';
|
|
13
13
|
type AuthorableRelationKind = KnownRelationKind;
|
|
14
14
|
/**
|
|
15
15
|
* Content states how long it runs and nothing else: where it is taken from and
|
|
@@ -58,7 +58,7 @@ interface EntityPayloadByKind {
|
|
|
58
58
|
role?: string;
|
|
59
59
|
};
|
|
60
60
|
clip: JsonObject;
|
|
61
|
-
/** Where the bytes live. Reached from other entities by from-asset. */
|
|
61
|
+
/** Where the bytes live. Reached from other entities by from-asset or to-asset. */
|
|
62
62
|
asset: AssetPayload;
|
|
63
63
|
video: OwnDurationPayload;
|
|
64
64
|
/** Every playable sound, including video original audio and synthesized voiceovers. */
|
|
@@ -152,8 +152,8 @@ type CreateEntityInput = { [K in KnownEntityKind]: {
|
|
|
152
152
|
*
|
|
153
153
|
* Naming it here is the only way to say where an entity's bytes come from:
|
|
154
154
|
* the Asset row and the `from-asset` Relation that reaches it are assembled
|
|
155
|
-
* by the host, never authored. Only a kind that declares `
|
|
156
|
-
*
|
|
155
|
+
* by the host, never authored. Only a kind that declares `Asset` — video,
|
|
156
|
+
* audio, image, caption, audio-script, phonetic-script — accepts one.
|
|
157
157
|
*/
|
|
158
158
|
asset?: {
|
|
159
159
|
system: 'memota' | 'memota-speech';
|
|
@@ -252,4 +252,4 @@ interface BusinessRelationFacade {
|
|
|
252
252
|
}
|
|
253
253
|
//#endregion
|
|
254
254
|
export { UpdateEntityInput as C, UnlinkRelationInput as S, LinkRelationInput as _, DeleteEntityInput as a, SandboxEntity as b, EntitySandboxCheckpoint as c, JsonObject as d, JsonPrimitive as f, LinkGeneratedRelationInput as g, KnownRelationKind as h, CreateEntityInput as i, EntityStoreSnapshot as l, KnownEntityKind as m, BusinessEntityFacade as n, EntityCommand as o, JsonValue as p, BusinessRelationFacade as r, EntityPlanState as s, AuthorableRelationKind as t, EntityUpdateInput as u, RelationUpdateInput as v, SandboxRelation as x, ResourceEntityKind as y };
|
|
255
|
-
//# sourceMappingURL=entity-contract-
|
|
255
|
+
//# sourceMappingURL=entity-contract-Dy0cozPt.d.mts.map
|
|
@@ -15,8 +15,9 @@ import { applyFieldChanges, assertCanonicalEditorResources, assertFieldChanges,
|
|
|
15
15
|
* `Voice` declares no `Sequence` — it is a timbre identity, not playable
|
|
16
16
|
* content; a rendered voiceover is an `audio` entity.
|
|
17
17
|
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
18
|
+
* `Asset` says only that an entity of that kind may be bound to stored bytes,
|
|
19
|
+
* and asks nothing of its payload. Which bytes, and which way the content
|
|
20
|
+
* flowed, is the `from-asset` / `to-asset` Relation's answer alone.
|
|
20
21
|
*/
|
|
21
22
|
const ENTITY_KINDS = Object.freeze({
|
|
22
23
|
axvideo: Object.freeze([
|
|
@@ -33,27 +34,27 @@ const ENTITY_KINDS = Object.freeze({
|
|
|
33
34
|
"Sequence",
|
|
34
35
|
"Visual",
|
|
35
36
|
"Audible",
|
|
36
|
-
"
|
|
37
|
+
"Asset"
|
|
37
38
|
]),
|
|
38
39
|
audio: Object.freeze([
|
|
39
40
|
"Sequence",
|
|
40
41
|
"Audible",
|
|
41
|
-
"
|
|
42
|
+
"Asset"
|
|
42
43
|
]),
|
|
43
44
|
image: Object.freeze([
|
|
44
45
|
"Sequence",
|
|
45
46
|
"Visual",
|
|
46
|
-
"
|
|
47
|
+
"Asset"
|
|
47
48
|
]),
|
|
48
49
|
voice: Object.freeze(["Container", "IPBound"]),
|
|
49
50
|
"sequence-marker": Object.freeze([]),
|
|
50
51
|
viewport: Object.freeze([]),
|
|
51
|
-
"audio-script": Object.freeze(["SegmentText", "
|
|
52
|
-
"phonetic-script": Object.freeze(["SegmentText"]),
|
|
52
|
+
"audio-script": Object.freeze(["SegmentText", "Asset"]),
|
|
53
|
+
"phonetic-script": Object.freeze(["SegmentText", "Asset"]),
|
|
53
54
|
caption: Object.freeze([
|
|
54
55
|
"SegmentText",
|
|
55
56
|
"Sequence",
|
|
56
|
-
"
|
|
57
|
+
"Asset"
|
|
57
58
|
])
|
|
58
59
|
});
|
|
59
60
|
/** What a known kind declares; extension kinds declare nothing through this table. */
|
|
@@ -347,15 +348,6 @@ function entityToRow(entity) {
|
|
|
347
348
|
//#endregion
|
|
348
349
|
//#region ../medeo-dsl/src/invariants.ts
|
|
349
350
|
/** An entity is made from at most one Asset; two would leave its bytes ambiguous. */
|
|
350
|
-
function validateAssetSource(entity, index) {
|
|
351
|
-
if (entity.current().entityKind === "asset") return [];
|
|
352
|
-
if ([...index.relationsOf(entity)].filter((relation) => relation.kind === "from-asset").length <= 1) return [];
|
|
353
|
-
return [{
|
|
354
|
-
code: "asset_source_cardinality",
|
|
355
|
-
entityId: entity.entityId,
|
|
356
|
-
message: `Entity "${entity.entityId}" is made from more than one Asset`
|
|
357
|
-
}];
|
|
358
|
-
}
|
|
359
351
|
/** Validates one complete set of entities and its authoritative Relation rows. */
|
|
360
352
|
function validateEntityRelationSet(entityRefs, index, options) {
|
|
361
353
|
const { entities, issues } = collectRelatedEntities(entityRefs, index);
|
|
@@ -377,7 +369,6 @@ function validateEntityRelationSet(entityRefs, index, options) {
|
|
|
377
369
|
const current = entity.current();
|
|
378
370
|
const entityIssues = validateEntity(entity, entityIds);
|
|
379
371
|
issues.push(...entityIssues);
|
|
380
|
-
issues.push(...validateAssetSource(entity, index));
|
|
381
372
|
if (entityIssues.some((issue) => issue.code === "invalid_entity_payload")) continue;
|
|
382
373
|
if (current.entityKind === "sequence-marker") {
|
|
383
374
|
const marker = entity;
|
|
@@ -1021,10 +1012,10 @@ const markerContentRelationSpec = Object.freeze({
|
|
|
1021
1012
|
/**
|
|
1022
1013
|
* `from-asset(entity, asset)` — the entity was made from that stored resource.
|
|
1023
1014
|
*
|
|
1024
|
-
* Eligibility is declared: the non-Asset endpoint must carry the `
|
|
1025
|
-
*
|
|
1026
|
-
*
|
|
1027
|
-
*
|
|
1015
|
+
* Eligibility is declared: the non-Asset endpoint must carry the `Asset` kind,
|
|
1016
|
+
* which says an entity of that kind can be bound to stored bytes and asks
|
|
1017
|
+
* nothing of its payload. The bytes stay named in exactly one place, so no
|
|
1018
|
+
* entity copies the locator into its own row and one Asset can back several
|
|
1028
1019
|
* entities.
|
|
1029
1020
|
*/
|
|
1030
1021
|
const fromAssetRelationSpec = Object.freeze({
|
|
@@ -1032,6 +1023,26 @@ const fromAssetRelationSpec = Object.freeze({
|
|
|
1032
1023
|
validateEndpoints: (endpoints) => hasExactlyOneAsset(endpoints),
|
|
1033
1024
|
validateMetadata: isAssetBinding
|
|
1034
1025
|
});
|
|
1026
|
+
/**
|
|
1027
|
+
* `to-asset(entity, asset)` — the entity's content was written out to that
|
|
1028
|
+
* stored resource.
|
|
1029
|
+
*
|
|
1030
|
+
* The mirror of `from-asset`, and deliberately a separate relation kind rather
|
|
1031
|
+
* than a second declared kind: eligibility is the same either way, but which
|
|
1032
|
+
* way the content flowed is what says whether an entity may still be edited.
|
|
1033
|
+
* An entity reached by `from-asset` was produced from bytes it does not own,
|
|
1034
|
+
* so changing it would contradict them. An entity with a `to-asset` owns its
|
|
1035
|
+
* content and the resource is a snapshot of it, which is what goes stale and
|
|
1036
|
+
* gets written again. A transcript that is later edited carries both at once,
|
|
1037
|
+
* and only the distinct relation kinds keep them apart.
|
|
1038
|
+
*
|
|
1039
|
+
* Writing a snapshot is host assembly; nothing a script authors creates one.
|
|
1040
|
+
*/
|
|
1041
|
+
const toAssetRelationSpec = Object.freeze({
|
|
1042
|
+
kind: "to-asset",
|
|
1043
|
+
validateEndpoints: (endpoints) => hasExactlyOneAsset(endpoints),
|
|
1044
|
+
validateMetadata: isAssetBinding
|
|
1045
|
+
});
|
|
1035
1046
|
/** `generated(output, input)` means endpoint 0 was generated from endpoint 1. */
|
|
1036
1047
|
const generatedRelationSpec = Object.freeze({
|
|
1037
1048
|
kind: "generated",
|
|
@@ -1094,6 +1105,7 @@ const builtInRelationSpecs = Object.freeze([
|
|
|
1094
1105
|
axVideoMarkerRelationSpec,
|
|
1095
1106
|
markerTimelineRelationSpec,
|
|
1096
1107
|
fromAssetRelationSpec,
|
|
1108
|
+
toAssetRelationSpec,
|
|
1097
1109
|
generatedRelationSpec,
|
|
1098
1110
|
captionAlignmentRelationSpec,
|
|
1099
1111
|
clipAnchorRelationSpec,
|
|
@@ -1122,12 +1134,12 @@ function hasMarkerAndSequence(endpoints) {
|
|
|
1122
1134
|
const second = endpoints[1].current();
|
|
1123
1135
|
return first.entityKind === "sequence-marker" && hasSequence(second) || second.entityKind === "sequence-marker" && hasSequence(first);
|
|
1124
1136
|
}
|
|
1125
|
-
/** Exactly one end is the Asset, so the other end is unambiguously the
|
|
1137
|
+
/** Exactly one end is the Asset, so the other end is unambiguously the bound entity. */
|
|
1126
1138
|
function hasExactlyOneAsset(endpoints) {
|
|
1127
1139
|
const kinds = endpoints.map((endpoint) => endpoint.current().entityKind);
|
|
1128
1140
|
const assets = kinds.map((kind) => kind === "asset");
|
|
1129
1141
|
if (assets[0] === assets[1]) return false;
|
|
1130
|
-
return declaresKind(assets[0] ? kinds[1] : kinds[0], "
|
|
1142
|
+
return declaresKind(assets[0] ? kinds[1] : kinds[0], "Asset");
|
|
1131
1143
|
}
|
|
1132
1144
|
function isGeneratedMedia(entity) {
|
|
1133
1145
|
return entity.entityKind === "video" || entity.entityKind === "image" || entity.entityKind === "audio";
|
|
@@ -1467,6 +1479,8 @@ function errorMessage(error) {
|
|
|
1467
1479
|
//#region src/sandbox/business-facades.ts
|
|
1468
1480
|
/** Resource IDs are entity data; physical storage paths remain host-owned. */
|
|
1469
1481
|
const infrastructureFields = new Set(["storageKey", "assetContentHash"]);
|
|
1482
|
+
/** Both directions of an Asset binding are host assembly, whoever asks. */
|
|
1483
|
+
const assetBindingKinds = new Set(["from-asset", "to-asset"]);
|
|
1470
1484
|
function businessEntity(entity) {
|
|
1471
1485
|
return {
|
|
1472
1486
|
...entity,
|
|
@@ -1479,12 +1493,12 @@ function businessState(state) {
|
|
|
1479
1493
|
revision: state.revision,
|
|
1480
1494
|
audioScriptEntityId: state.audioScriptEntityId,
|
|
1481
1495
|
entities: state.entities.filter((row) => !assets.has(row.entity_id)).map(businessEntity),
|
|
1482
|
-
relations: state.relations.filter((row) => row.relation_kind
|
|
1496
|
+
relations: state.relations.filter((row) => !assetBindingKinds.has(row.relation_kind) && !assets.has(row.endpoint_0_entity_id) && !assets.has(row.endpoint_1_entity_id))
|
|
1483
1497
|
};
|
|
1484
1498
|
}
|
|
1485
|
-
/** Only a kind that declares
|
|
1499
|
+
/** Only a kind that declares Asset can be bound to stored bytes. */
|
|
1486
1500
|
function assetLocator(value, entityKind) {
|
|
1487
|
-
if (!declaresKind(entityKind, "
|
|
1501
|
+
if (!declaresKind(entityKind, "Asset")) throw new Error(`Entity kind "${entityKind}" cannot be made from an Asset`);
|
|
1488
1502
|
if (!isJsonObject(value) || value.system !== "memota" && value.system !== "memota-speech") throw new Error("asset.system must be \"memota\" or \"memota-speech\"");
|
|
1489
1503
|
if (typeof value.key !== "string" || value.key.trim() !== value.key || value.key === "") throw new Error("asset.key must be a trimmed, non-empty resource id");
|
|
1490
1504
|
return {
|
|
@@ -1504,7 +1518,7 @@ function businessFacades(entities, relations, onDirectScriptWrite) {
|
|
|
1504
1518
|
for (const id of payload.baseEntityIds) if (typeof id === "string") assertBusiness(id);
|
|
1505
1519
|
}
|
|
1506
1520
|
};
|
|
1507
|
-
const visibleRelation = (relation) => relation.relation_kind
|
|
1521
|
+
const visibleRelation = (relation) => !assetBindingKinds.has(relation.relation_kind) && !isAsset(relation.endpoint_0_entity_id) && !isAsset(relation.endpoint_1_entity_id);
|
|
1508
1522
|
return {
|
|
1509
1523
|
entities: {
|
|
1510
1524
|
list: () => entities.list().filter((entity) => entity.entity_kind !== "asset").map(businessEntity),
|
|
@@ -1558,7 +1572,7 @@ function businessFacades(entities, relations, onDirectScriptWrite) {
|
|
|
1558
1572
|
list: () => relations.list().filter(visibleRelation),
|
|
1559
1573
|
of: (id, kind) => relations.of(id, kind).filter(visibleRelation),
|
|
1560
1574
|
link: (input) => {
|
|
1561
|
-
if (input.relation_kind
|
|
1575
|
+
if (assetBindingKinds.has(input.relation_kind)) throw new Error("Asset bindings are managed by host assembly");
|
|
1562
1576
|
assertBusiness(input.endpoint_0_entity_id);
|
|
1563
1577
|
assertBusiness(input.endpoint_1_entity_id);
|
|
1564
1578
|
return relations.link(input);
|
|
@@ -1647,7 +1661,7 @@ var EntitySandbox = class {
|
|
|
1647
1661
|
decodeEntityRelationRows(rows, numericMarkerComparators);
|
|
1648
1662
|
if (this.state.revision !== 0 || rows.entities.length !== 0 || this.commands.length !== 0) {
|
|
1649
1663
|
assertCanonicalEditorResources(rows);
|
|
1650
|
-
readDocumentAudioScript({
|
|
1664
|
+
if (this.state.audioScriptEntityId !== null) readDocumentAudioScript({
|
|
1651
1665
|
rows,
|
|
1652
1666
|
audioScriptEntityId: this.state.audioScriptEntityId
|
|
1653
1667
|
});
|
|
@@ -1913,7 +1927,7 @@ var EntitySandbox = class {
|
|
|
1913
1927
|
}
|
|
1914
1928
|
for (const row of rows.relations) {
|
|
1915
1929
|
if (this.state.relations.some((relation) => relation.relation_id === row.relationId)) continue;
|
|
1916
|
-
if (row.relationKind !== "timeline-track" && row.relationKind !== "voice-timbre" && row.relationKind !== "from-asset") throw new Error(`Unexpected resource Relation ${row.relationKind}`);
|
|
1930
|
+
if (row.relationKind !== "timeline-track" && row.relationKind !== "voice-timbre" && row.relationKind !== "from-asset" && row.relationKind !== "to-asset") throw new Error(`Unexpected resource Relation ${row.relationKind}`);
|
|
1917
1931
|
this.link({
|
|
1918
1932
|
relation_id: row.relationId,
|
|
1919
1933
|
relation_kind: row.relationKind,
|
|
@@ -2219,4 +2233,4 @@ const numericMarkerComparators = { compareMarkerPoints: (_marker, _range, left,
|
|
|
2219
2233
|
//#endregion
|
|
2220
2234
|
export { createEntityId as a, businessState as i, toDslRows as n, createRelationId as o, businessFacades as r, isMediaAssetVariantKind as s, EntitySandbox as t };
|
|
2221
2235
|
|
|
2222
|
-
//# sourceMappingURL=entity-sandbox-
|
|
2236
|
+
//# sourceMappingURL=entity-sandbox-CulJBYrI.mjs.map
|