@hyperframes/studio 0.8.46 → 0.8.47
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/assets/hyperframes-player-B7fsB8C2.js +464 -0
- package/dist/assets/{index-BywY2OM3.js → index-B50xQfSG.js} +1 -1
- package/dist/assets/{index-DCblmTX3.js → index-j-4hCrZU.js} +130 -130
- package/dist/assets/{index-CbchRscN.js → index-ryL7q1QG.js} +1 -1
- package/dist/index.d.ts +17 -3
- package/dist/index.html +1 -1
- package/dist/index.js +103 -40
- package/dist/index.js.map +1 -1
- package/package.json +7 -7
- package/src/components/editor/manualEditingAvailability.test.ts +19 -0
- package/src/components/editor/manualEditingAvailability.ts +9 -5
- package/src/hooks/useSdkSession.lifecycle.test.tsx +76 -0
- package/src/hooks/useSdkSession.ts +21 -3
- package/src/player/hooks/useTimelinePlayer.ts +9 -4
- package/src/player/hooks/useTimelineSyncCallbacks.ts +7 -4
- package/src/player/store/playerStore.test.ts +42 -0
- package/src/player/store/playerStore.ts +9 -7
- package/src/player/store/readinessSlice.ts +42 -0
- package/src/utils/sdkCutover.gate.test.ts +5 -4
- package/src/utils/sdkCutover.test.ts +42 -0
- package/src/utils/sdkCutover.ts +46 -25
- package/src/utils/sdkCutoverEligibility.ts +21 -0
- package/src/utils/sdkCutoverPolicy.test.ts +11 -2
- package/src/utils/sdkCutoverPolicy.ts +5 -3
- package/src/utils/sdkEditTransaction.decline.test.ts +46 -0
- package/src/utils/sdkEditTransaction.ts +22 -1
- package/src/utils/sdkResolverShadow.ts +10 -1
- package/dist/assets/hyperframes-player-CsV2rywT.js +0 -464
package/dist/index.d.ts
CHANGED
|
@@ -4,6 +4,7 @@ import { ReactNode, RefObject } from 'react';
|
|
|
4
4
|
import { RuntimeTimelineClipIdentity } from '@hyperframes/core';
|
|
5
5
|
import { GsapAnimation, PropertyGroupName } from '@hyperframes/core/gsap-parser';
|
|
6
6
|
import * as zustand from 'zustand';
|
|
7
|
+
import { StoreApi } from 'zustand';
|
|
7
8
|
import { MusicBeatAnalysis } from '@hyperframes/core/beats';
|
|
8
9
|
import { GsapAnimation as GsapAnimation$1, ArcPathSegment } from '@hyperframes/parsers/gsap-parser';
|
|
9
10
|
|
|
@@ -250,6 +251,20 @@ interface ThumbnailSlice {
|
|
|
250
251
|
bumpThumbnailContentRevision: () => void;
|
|
251
252
|
}
|
|
252
253
|
|
|
254
|
+
/** Gates `timelineReady` on the composition's declared readiness inputs
|
|
255
|
+
* (media, for now) instead of just a known duration. The generation counter
|
|
256
|
+
* is module-scope, not store state: it guards an in-flight settlement, not
|
|
257
|
+
* something a component reads, so bumping it shouldn't trigger a render. */
|
|
258
|
+
|
|
259
|
+
interface PlaybackReadinessSlice {
|
|
260
|
+
timelineReady: boolean;
|
|
261
|
+
setTimelineReady: (ready: boolean) => void;
|
|
262
|
+
/** Sets timelineReady once doc's readiness inputs settle, or immediately
|
|
263
|
+
* if doc is null. A wait a later call supersedes never wins the race. */
|
|
264
|
+
requestTimelineReady: (doc: Document | null) => void;
|
|
265
|
+
}
|
|
266
|
+
declare function createPlaybackReadinessSlice(set: StoreApi<PlaybackReadinessSlice>["setState"]): PlaybackReadinessSlice;
|
|
267
|
+
|
|
253
268
|
type TimeListener = (time: number) => void;
|
|
254
269
|
declare const liveTime: {
|
|
255
270
|
notify: (time: number) => void;
|
|
@@ -382,11 +397,11 @@ type TimelineTool = "select" | "razor";
|
|
|
382
397
|
interface SelectElementOptions {
|
|
383
398
|
preserveSet?: boolean;
|
|
384
399
|
}
|
|
385
|
-
|
|
400
|
+
type PlayerStoreSlices = KeyframeSlice & AutomationSelectionSlice & ThumbnailSlice & EditingModeSlice & ReturnType<typeof createPlaybackReadinessSlice>;
|
|
401
|
+
interface PlayerState extends PlayerStoreSlices {
|
|
386
402
|
isPlaying: boolean;
|
|
387
403
|
currentTime: number;
|
|
388
404
|
duration: number;
|
|
389
|
-
timelineReady: boolean;
|
|
390
405
|
/** Increments exactly once when the Studio switches to a different project. */
|
|
391
406
|
timelineSessionEpoch: number;
|
|
392
407
|
/** Project owning the current timeline session; null outside a project-scoped reset. */
|
|
@@ -452,7 +467,6 @@ interface PlayerState extends KeyframeSlice, AutomationSelectionSlice, Thumbnail
|
|
|
452
467
|
setAudioMuted: (muted: boolean) => void;
|
|
453
468
|
setAudioVolume: (volume: number) => void;
|
|
454
469
|
setLoopEnabled: (enabled: boolean) => void;
|
|
455
|
-
setTimelineReady: (ready: boolean) => void;
|
|
456
470
|
setBeatDragging: (dragging: boolean) => void;
|
|
457
471
|
setElements: (elements: TimelineElement[]) => void;
|
|
458
472
|
setSelectedElementId: (id: string | null, options?: SelectElementOptions) => void;
|
package/dist/index.html
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" />
|
|
6
6
|
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
|
7
7
|
<title>HyperFrames Studio</title>
|
|
8
|
-
<script type="module" crossorigin src="/assets/index-
|
|
8
|
+
<script type="module" crossorigin src="/assets/index-j-4hCrZU.js"></script>
|
|
9
9
|
<link rel="stylesheet" crossorigin href="/assets/index-DnRfAiK2.css">
|
|
10
10
|
</head>
|
|
11
11
|
<body>
|
package/dist/index.js
CHANGED
|
@@ -1594,6 +1594,30 @@ function createThumbnailSlice(set) {
|
|
|
1594
1594
|
};
|
|
1595
1595
|
}
|
|
1596
1596
|
|
|
1597
|
+
// src/player/store/readinessSlice.ts
|
|
1598
|
+
import { settleCompositionReadiness } from "@hyperframes/core/composition-readiness";
|
|
1599
|
+
var timelineReadyGeneration = 0;
|
|
1600
|
+
function resetPlaybackReadinessState() {
|
|
1601
|
+
timelineReadyGeneration++;
|
|
1602
|
+
return { timelineReady: false };
|
|
1603
|
+
}
|
|
1604
|
+
function createPlaybackReadinessSlice(set) {
|
|
1605
|
+
return {
|
|
1606
|
+
timelineReady: false,
|
|
1607
|
+
setTimelineReady: (ready) => {
|
|
1608
|
+
timelineReadyGeneration++;
|
|
1609
|
+
set({ timelineReady: ready });
|
|
1610
|
+
},
|
|
1611
|
+
requestTimelineReady: (doc) => {
|
|
1612
|
+
const generation = ++timelineReadyGeneration;
|
|
1613
|
+
if (!doc) return set({ timelineReady: true });
|
|
1614
|
+
settleCompositionReadiness(doc, () => {
|
|
1615
|
+
if (generation === timelineReadyGeneration) set({ timelineReady: true });
|
|
1616
|
+
});
|
|
1617
|
+
}
|
|
1618
|
+
};
|
|
1619
|
+
}
|
|
1620
|
+
|
|
1597
1621
|
// src/player/store/liveTime.ts
|
|
1598
1622
|
var timeListeners = /* @__PURE__ */ new Set();
|
|
1599
1623
|
var liveTime = {
|
|
@@ -1623,7 +1647,7 @@ function createTimelineResetState() {
|
|
|
1623
1647
|
isPlaying: false,
|
|
1624
1648
|
currentTime: 0,
|
|
1625
1649
|
duration: 0,
|
|
1626
|
-
|
|
1650
|
+
...resetPlaybackReadinessState(),
|
|
1627
1651
|
beatDragging: false,
|
|
1628
1652
|
elements: [],
|
|
1629
1653
|
selectedElementId: null,
|
|
@@ -1666,7 +1690,6 @@ var usePlayerStore = create2((set, get) => ({
|
|
|
1666
1690
|
isPlaying: false,
|
|
1667
1691
|
currentTime: 0,
|
|
1668
1692
|
duration: 0,
|
|
1669
|
-
timelineReady: false,
|
|
1670
1693
|
timelineSessionEpoch: 0,
|
|
1671
1694
|
timelineProjectId: null,
|
|
1672
1695
|
beatDragging: false,
|
|
@@ -1692,6 +1715,7 @@ var usePlayerStore = create2((set, get) => ({
|
|
|
1692
1715
|
...createThumbnailSlice(set),
|
|
1693
1716
|
...createAutomationSelectionSlice(set),
|
|
1694
1717
|
...createEditingModeSlice(set),
|
|
1718
|
+
...createPlaybackReadinessSlice(set),
|
|
1695
1719
|
activeKeyframePct: null,
|
|
1696
1720
|
setActiveKeyframePct: (pct) => set({ activeKeyframePct: pct }),
|
|
1697
1721
|
selectedElementIds: /* @__PURE__ */ new Set(),
|
|
@@ -1856,7 +1880,6 @@ var usePlayerStore = create2((set, get) => ({
|
|
|
1856
1880
|
bumpZEditVersion: () => set((state) => ({ zEditVersion: state.zEditVersion + 1 })),
|
|
1857
1881
|
setCurrentTime: (time) => set({ currentTime: Number.isFinite(time) ? time : 0 }),
|
|
1858
1882
|
setDuration: (duration) => set({ duration: Number.isFinite(duration) ? duration : 0 }),
|
|
1859
|
-
setTimelineReady: (ready) => set({ timelineReady: ready }),
|
|
1860
1883
|
setBeatDragging: (dragging) => set({ beatDragging: dragging }),
|
|
1861
1884
|
setElements: (elements) => set({ elements }),
|
|
1862
1885
|
// A genuine single selection: always collapse the set to just this element. User
|
|
@@ -25080,7 +25103,7 @@ function useTimelineSyncCallbacks({
|
|
|
25080
25103
|
syncTimelineElements,
|
|
25081
25104
|
setDuration,
|
|
25082
25105
|
setCurrentTime,
|
|
25083
|
-
|
|
25106
|
+
requestTimelineReady,
|
|
25084
25107
|
setIsPlaying,
|
|
25085
25108
|
attachIframeShortcutListeners,
|
|
25086
25109
|
applyPreviewAudioState
|
|
@@ -25152,7 +25175,7 @@ function useTimelineSyncCallbacks({
|
|
|
25152
25175
|
syncAdapterDuration(adapter, setDuration);
|
|
25153
25176
|
setCurrentTime(startTime);
|
|
25154
25177
|
if (!isRefreshingRef.current) {
|
|
25155
|
-
|
|
25178
|
+
requestTimelineReady(safeContentDocument(iframeRef.current));
|
|
25156
25179
|
}
|
|
25157
25180
|
isRefreshingRef.current = false;
|
|
25158
25181
|
setIsPlaying(false);
|
|
@@ -25170,7 +25193,7 @@ function useTimelineSyncCallbacks({
|
|
|
25170
25193
|
getAdapter,
|
|
25171
25194
|
setDuration,
|
|
25172
25195
|
setCurrentTime,
|
|
25173
|
-
|
|
25196
|
+
requestTimelineReady,
|
|
25174
25197
|
setIsPlaying,
|
|
25175
25198
|
processTimelineMessage,
|
|
25176
25199
|
enrichMissingCompositions,
|
|
@@ -25641,7 +25664,7 @@ function useTimelinePlayer() {
|
|
|
25641
25664
|
const lastTimelineMessageRef = useRef44(0);
|
|
25642
25665
|
const staticSeekAdapterRef = useRef44(null);
|
|
25643
25666
|
const staticSeekWarnedRef = useRef44(false);
|
|
25644
|
-
const { setIsPlaying, setCurrentTime, setDuration,
|
|
25667
|
+
const { setIsPlaying, setCurrentTime, setDuration, requestTimelineReady, setElements } = usePlayerStore.getState();
|
|
25645
25668
|
const syncTimelineElements = useCallback36(
|
|
25646
25669
|
// The lease guard adds one deliberate branch at the shared synchronization boundary.
|
|
25647
25670
|
// fallow-ignore-next-line complexity
|
|
@@ -25667,7 +25690,7 @@ function useTimelinePlayer() {
|
|
|
25667
25690
|
setDuration(nextDuration ?? 0);
|
|
25668
25691
|
}
|
|
25669
25692
|
if (!state.timelineReady) {
|
|
25670
|
-
|
|
25693
|
+
requestTimelineReady(safeContentDocument(iframeRef.current));
|
|
25671
25694
|
}
|
|
25672
25695
|
void probeMissingSourceDurations(
|
|
25673
25696
|
mergedElements,
|
|
@@ -25683,7 +25706,7 @@ function useTimelinePlayer() {
|
|
|
25683
25706
|
}
|
|
25684
25707
|
);
|
|
25685
25708
|
},
|
|
25686
|
-
[setElements,
|
|
25709
|
+
[setElements, requestTimelineReady, setDuration]
|
|
25687
25710
|
);
|
|
25688
25711
|
const getAdapter = useCallback36(() => {
|
|
25689
25712
|
try {
|
|
@@ -25953,7 +25976,7 @@ function useTimelinePlayer() {
|
|
|
25953
25976
|
syncTimelineElements,
|
|
25954
25977
|
setDuration,
|
|
25955
25978
|
setCurrentTime,
|
|
25956
|
-
|
|
25979
|
+
requestTimelineReady,
|
|
25957
25980
|
setIsPlaying,
|
|
25958
25981
|
attachIframeShortcutListeners,
|
|
25959
25982
|
applyPreviewAudioState
|
|
@@ -38233,7 +38256,7 @@ var STUDIO_SDK_OPERATION_FAMILIES = [
|
|
|
38233
38256
|
function resolveEnabledSdkFamilies(env2, masterEnabled) {
|
|
38234
38257
|
if (!masterEnabled) return /* @__PURE__ */ new Set();
|
|
38235
38258
|
const raw = env2["VITE_STUDIO_SDK_CUTOVER_FAMILIES"];
|
|
38236
|
-
if (typeof raw !== "string" || raw.trim() === "") return
|
|
38259
|
+
if (typeof raw !== "string" || raw.trim() === "") return new Set(STUDIO_SDK_OPERATION_FAMILIES);
|
|
38237
38260
|
const requested = raw.split(",").map((family) => family.trim()).filter(Boolean);
|
|
38238
38261
|
const known = new Set(STUDIO_SDK_OPERATION_FAMILIES);
|
|
38239
38262
|
const unknown = requested.filter((family) => !known.has(family));
|
|
@@ -38266,7 +38289,7 @@ var env = { ...import.meta.env ?? {}, ...runtimeEnv };
|
|
|
38266
38289
|
var STUDIO_SDK_CUTOVER_ENABLED = resolveStudioBooleanEnvFlag(
|
|
38267
38290
|
env,
|
|
38268
38291
|
["VITE_STUDIO_SDK_CUTOVER_ENABLED"],
|
|
38269
|
-
|
|
38292
|
+
true
|
|
38270
38293
|
);
|
|
38271
38294
|
var STUDIO_SDK_CUTOVER_FAMILIES = resolveEnabledSdkFamilies(
|
|
38272
38295
|
env,
|
|
@@ -38759,6 +38782,13 @@ function shouldDeclineTextCutoverForTarget(target, ops) {
|
|
|
38759
38782
|
const tag = elementTag(children[0]);
|
|
38760
38783
|
return tag !== null && NON_HTML_CHILD_TAGS.has(tag);
|
|
38761
38784
|
}
|
|
38785
|
+
function isResolverDisagreement(session, hfId) {
|
|
38786
|
+
try {
|
|
38787
|
+
return resolveSnapshot(session, hfId) !== null;
|
|
38788
|
+
} catch {
|
|
38789
|
+
return false;
|
|
38790
|
+
}
|
|
38791
|
+
}
|
|
38762
38792
|
function shouldUseSdkCutover(flagEnabled, hasSession, hfId, ops) {
|
|
38763
38793
|
return flagEnabled && hasSession && !!hfId && ops.length > 0 && ops.every((o) => CUTOVER_OP_TYPES.has(o.type)) && // SDK edit ops target only the element hfId; child-scoped patch ops need the server path.
|
|
38764
38794
|
!hasChildScopedOp(ops) && !ops.some(mapsToReservedAttr) && !hasUnsafeHtmlAttributeOp(ops);
|
|
@@ -38800,7 +38830,12 @@ function isSelfWriteEcho(path, content, now2 = Date.now()) {
|
|
|
38800
38830
|
}
|
|
38801
38831
|
|
|
38802
38832
|
// src/utils/sdkEditTransaction.ts
|
|
38803
|
-
function declinedCutover(reason) {
|
|
38833
|
+
function declinedCutover(reason, family, resolverDisagreement) {
|
|
38834
|
+
trackStudioEvent("sdk_cutover_declined", {
|
|
38835
|
+
reason,
|
|
38836
|
+
family: family ?? null,
|
|
38837
|
+
...resolverDisagreement ? { resolverDisagreement: true } : {}
|
|
38838
|
+
});
|
|
38804
38839
|
return { status: "declined", reason };
|
|
38805
38840
|
}
|
|
38806
38841
|
function asCutoverError(error) {
|
|
@@ -38967,15 +39002,17 @@ function gsapReadSource(deps, targetPath) {
|
|
|
38967
39002
|
}
|
|
38968
39003
|
async function sdkCutoverPersist(selection, ops, originalContent, targetPath, sdkSession, deps, options) {
|
|
38969
39004
|
if (!shouldUseSdkCutover(sdkFamilyEnabled("dom"), !!sdkSession, selection.hfId, ops))
|
|
38970
|
-
return declinedCutover("ineligible_operation");
|
|
38971
|
-
if (!sdkSession) return declinedCutover("session_unavailable");
|
|
39005
|
+
return declinedCutover("ineligible_operation", "dom");
|
|
39006
|
+
if (!sdkSession) return declinedCutover("session_unavailable", "dom");
|
|
38972
39007
|
const hfId = selection.hfId;
|
|
38973
|
-
if (!hfId) return declinedCutover("target_unaddressable");
|
|
39008
|
+
if (!hfId) return declinedCutover("target_unaddressable", "dom");
|
|
38974
39009
|
const target = sdkSession.getElement(hfId);
|
|
38975
|
-
if (!target)
|
|
39010
|
+
if (!target)
|
|
39011
|
+
return declinedCutover("target_not_found", "dom", isResolverDisagreement(sdkSession, hfId));
|
|
38976
39012
|
if (shouldDeclineTextCutoverForTarget(target, ops))
|
|
38977
|
-
return declinedCutover("unsupported_text_target");
|
|
38978
|
-
if (wrongCompositionFile(deps, targetPath))
|
|
39013
|
+
return declinedCutover("unsupported_text_target", "dom");
|
|
39014
|
+
if (wrongCompositionFile(deps, targetPath))
|
|
39015
|
+
return declinedCutover("wrong_composition_file", "dom");
|
|
38979
39016
|
const result = await persistSdkCandidateMutation(
|
|
38980
39017
|
sdkSession,
|
|
38981
39018
|
targetPath,
|
|
@@ -38998,10 +39035,12 @@ async function sdkTimingPersist(hfId, targetPath, timingUpdate, sdkSession, deps
|
|
|
38998
39035
|
timingSrc ? () => timingSrc(targetPath) : void 0,
|
|
38999
39036
|
{ targetPath, compositionPath: deps.compositionPath }
|
|
39000
39037
|
);
|
|
39001
|
-
if (!sdkFamilyEnabled("timing")) return declinedCutover("feature_disabled");
|
|
39002
|
-
if (!sdkSession) return declinedCutover("session_unavailable");
|
|
39003
|
-
if (!sdkSession.getElement(hfId))
|
|
39004
|
-
|
|
39038
|
+
if (!sdkFamilyEnabled("timing")) return declinedCutover("feature_disabled", "timing");
|
|
39039
|
+
if (!sdkSession) return declinedCutover("session_unavailable", "timing");
|
|
39040
|
+
if (!sdkSession.getElement(hfId))
|
|
39041
|
+
return declinedCutover("target_not_found", "timing", isResolverDisagreement(sdkSession, hfId));
|
|
39042
|
+
if (wrongCompositionFile(deps, targetPath))
|
|
39043
|
+
return declinedCutover("wrong_composition_file", "timing");
|
|
39005
39044
|
try {
|
|
39006
39045
|
const serializedBefore = sdkSession.serialize();
|
|
39007
39046
|
const result = await persistSdkCandidateMutation(
|
|
@@ -39032,11 +39071,17 @@ async function sdkTimingBatchPersist(changes, targetPath, sdkSession, deps, opti
|
|
|
39032
39071
|
{ targetPath, compositionPath: deps.compositionPath }
|
|
39033
39072
|
);
|
|
39034
39073
|
}
|
|
39035
|
-
if (!sdkFamilyEnabled("timing")) return declinedCutover("feature_disabled");
|
|
39036
|
-
if (!sdkSession) return declinedCutover("session_unavailable");
|
|
39037
|
-
if (wrongCompositionFile(deps, targetPath))
|
|
39038
|
-
|
|
39039
|
-
|
|
39074
|
+
if (!sdkFamilyEnabled("timing")) return declinedCutover("feature_disabled", "timing");
|
|
39075
|
+
if (!sdkSession) return declinedCutover("session_unavailable", "timing");
|
|
39076
|
+
if (wrongCompositionFile(deps, targetPath))
|
|
39077
|
+
return declinedCutover("wrong_composition_file", "timing");
|
|
39078
|
+
const unresolved = changes.find((change) => !sdkSession.getElement(change.hfId));
|
|
39079
|
+
if (unresolved)
|
|
39080
|
+
return declinedCutover(
|
|
39081
|
+
"target_not_found",
|
|
39082
|
+
"timing",
|
|
39083
|
+
isResolverDisagreement(sdkSession, unresolved.hfId)
|
|
39084
|
+
);
|
|
39040
39085
|
try {
|
|
39041
39086
|
const serializedBefore = sdkSession.serialize();
|
|
39042
39087
|
const result = await persistSdkCandidateMutation(
|
|
@@ -39091,9 +39136,9 @@ function sdkGsapTweenPersist(targetPath, op, sdkSession, deps, options) {
|
|
|
39091
39136
|
);
|
|
39092
39137
|
}
|
|
39093
39138
|
if (!sdkFamilyEnabled("gsap-animation"))
|
|
39094
|
-
return Promise.resolve(declinedCutover("feature_disabled"));
|
|
39139
|
+
return Promise.resolve(declinedCutover("feature_disabled", "gsap-animation"));
|
|
39095
39140
|
if (op.kind === "add" && sdkSession && !sdkSession.getElement(op.target))
|
|
39096
|
-
return Promise.resolve(declinedCutover("target_not_found"));
|
|
39141
|
+
return Promise.resolve(declinedCutover("target_not_found", "gsap-animation"));
|
|
39097
39142
|
return dispatchGsapOpAndPersist("gsap-animation", targetPath, sdkSession, deps, options, (s) => {
|
|
39098
39143
|
s.batch(() => {
|
|
39099
39144
|
if (op.kind === "add") {
|
|
@@ -39116,9 +39161,10 @@ async function dispatchGsapOpAndPersist(family, targetPath, sdkSession, deps, op
|
|
|
39116
39161
|
{ targetPath, compositionPath: deps.compositionPath }
|
|
39117
39162
|
);
|
|
39118
39163
|
}
|
|
39119
|
-
if (!sdkFamilyEnabled(family)) return declinedCutover("feature_disabled");
|
|
39120
|
-
if (!sdkSession) return declinedCutover("session_unavailable");
|
|
39121
|
-
if (wrongCompositionFile(deps, targetPath))
|
|
39164
|
+
if (!sdkFamilyEnabled(family)) return declinedCutover("feature_disabled", family);
|
|
39165
|
+
if (!sdkSession) return declinedCutover("session_unavailable", family);
|
|
39166
|
+
if (wrongCompositionFile(deps, targetPath))
|
|
39167
|
+
return declinedCutover("wrong_composition_file", family);
|
|
39122
39168
|
const session = sdkSession;
|
|
39123
39169
|
try {
|
|
39124
39170
|
const serializedBefore = session.serialize();
|
|
@@ -39271,10 +39317,16 @@ async function sdkDeletePersist(hfId, originalContent, targetPath, sdkSession, d
|
|
|
39271
39317
|
() => Promise.resolve(originalContent),
|
|
39272
39318
|
{ targetPath, compositionPath: deps.compositionPath }
|
|
39273
39319
|
);
|
|
39274
|
-
if (!sdkFamilyEnabled("lifecycle")) return declinedCutover("feature_disabled");
|
|
39275
|
-
if (!sdkSession) return declinedCutover("session_unavailable");
|
|
39276
|
-
if (!sdkSession.getElement(hfId))
|
|
39277
|
-
|
|
39320
|
+
if (!sdkFamilyEnabled("lifecycle")) return declinedCutover("feature_disabled", "lifecycle");
|
|
39321
|
+
if (!sdkSession) return declinedCutover("session_unavailable", "lifecycle");
|
|
39322
|
+
if (!sdkSession.getElement(hfId))
|
|
39323
|
+
return declinedCutover(
|
|
39324
|
+
"target_not_found",
|
|
39325
|
+
"lifecycle",
|
|
39326
|
+
isResolverDisagreement(sdkSession, hfId)
|
|
39327
|
+
);
|
|
39328
|
+
if (wrongCompositionFile(deps, targetPath))
|
|
39329
|
+
return declinedCutover("wrong_composition_file", "lifecycle");
|
|
39278
39330
|
const result = await persistSdkCandidateMutation(
|
|
39279
39331
|
sdkSession,
|
|
39280
39332
|
targetPath,
|
|
@@ -74679,7 +74731,11 @@ function useSdkSession(projectId, activeCompPath) {
|
|
|
74679
74731
|
generation
|
|
74680
74732
|
};
|
|
74681
74733
|
readProjectFileOptional(projectId, activeCompPath).then(async (content) => {
|
|
74682
|
-
if (cancelled
|
|
74734
|
+
if (cancelled) return;
|
|
74735
|
+
if (typeof content !== "string") {
|
|
74736
|
+
trackStudioEvent("sdk_session_unavailable", { stage: "read" });
|
|
74737
|
+
return;
|
|
74738
|
+
}
|
|
74683
74739
|
const comp = await openComposition3(content, { history: false });
|
|
74684
74740
|
if (cancelled) {
|
|
74685
74741
|
disposeSdkSession(comp);
|
|
@@ -74693,6 +74749,7 @@ function useSdkSession(projectId, activeCompPath) {
|
|
|
74693
74749
|
reloadTokenRef.current
|
|
74694
74750
|
)) {
|
|
74695
74751
|
disposeSdkSession(comp);
|
|
74752
|
+
trackStudioEvent("sdk_session_unavailable", { stage: "ownership" });
|
|
74696
74753
|
return;
|
|
74697
74754
|
}
|
|
74698
74755
|
const displaced = ownedSessionRef.current;
|
|
@@ -74701,8 +74758,14 @@ function useSdkSession(projectId, activeCompPath) {
|
|
|
74701
74758
|
ownedSessionRef.current = installed;
|
|
74702
74759
|
setOwnedSession(installed);
|
|
74703
74760
|
if (displaced && displaced.session !== comp) disposeSdkSession(displaced.session);
|
|
74704
|
-
}).catch(() => {
|
|
74705
|
-
if (!cancelled && generationRef.current === generation)
|
|
74761
|
+
}).catch((error) => {
|
|
74762
|
+
if (!cancelled && generationRef.current === generation) {
|
|
74763
|
+
setOwnedSession(null);
|
|
74764
|
+
trackStudioEvent("sdk_session_unavailable", {
|
|
74765
|
+
stage: "open",
|
|
74766
|
+
error: error instanceof Error ? error.message : String(error)
|
|
74767
|
+
});
|
|
74768
|
+
}
|
|
74706
74769
|
});
|
|
74707
74770
|
return () => {
|
|
74708
74771
|
cancelled = true;
|