@modelnex/sdk 0.5.18 → 0.5.19
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.js +50 -5
- package/dist/index.mjs +50 -5
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -2719,6 +2719,7 @@ function createTourSocketPool({
|
|
|
2719
2719
|
var tourSocketPool = createTourSocketPool();
|
|
2720
2720
|
|
|
2721
2721
|
// src/utils/tour-playback-guards.ts
|
|
2722
|
+
var playbackOwners = /* @__PURE__ */ new Map();
|
|
2722
2723
|
function shouldExecuteTourCommandBatch(isPlaybackActive) {
|
|
2723
2724
|
return isPlaybackActive;
|
|
2724
2725
|
}
|
|
@@ -2742,6 +2743,25 @@ function shouldLogTourDebugEntry(state) {
|
|
|
2742
2743
|
}
|
|
2743
2744
|
return true;
|
|
2744
2745
|
}
|
|
2746
|
+
function createTourPlaybackOwnerKey(serverUrl, websiteId, experienceType) {
|
|
2747
|
+
return `${serverUrl}::${websiteId || "__default__"}::${experienceType}`;
|
|
2748
|
+
}
|
|
2749
|
+
function claimTourPlaybackOwnership(key, ownerId) {
|
|
2750
|
+
const currentOwner = playbackOwners.get(key);
|
|
2751
|
+
if (currentOwner && currentOwner !== ownerId) {
|
|
2752
|
+
return false;
|
|
2753
|
+
}
|
|
2754
|
+
playbackOwners.set(key, ownerId);
|
|
2755
|
+
return true;
|
|
2756
|
+
}
|
|
2757
|
+
function hasTourPlaybackOwnership(key, ownerId) {
|
|
2758
|
+
return playbackOwners.get(key) === ownerId;
|
|
2759
|
+
}
|
|
2760
|
+
function releaseTourPlaybackOwnership(key, ownerId) {
|
|
2761
|
+
if (playbackOwners.get(key) === ownerId) {
|
|
2762
|
+
playbackOwners.delete(key);
|
|
2763
|
+
}
|
|
2764
|
+
}
|
|
2745
2765
|
|
|
2746
2766
|
// src/hooks/useTourPlayback.ts
|
|
2747
2767
|
function resolveElement(step) {
|
|
@@ -3277,6 +3297,8 @@ function useTourPlayback({
|
|
|
3277
3297
|
const runIdRef = (0, import_react12.useRef)(null);
|
|
3278
3298
|
const turnIdRef = (0, import_react12.useRef)(null);
|
|
3279
3299
|
const startRequestedRef = (0, import_react12.useRef)(false);
|
|
3300
|
+
const playbackOwnerIdRef = (0, import_react12.useRef)(`playback-owner-${Math.random().toString(36).slice(2, 10)}`);
|
|
3301
|
+
const claimedPlaybackOwnerKeyRef = (0, import_react12.useRef)(null);
|
|
3280
3302
|
const socketRef = (0, import_react12.useRef)(null);
|
|
3281
3303
|
const socketIdRef = (0, import_react12.useRef)(socketId);
|
|
3282
3304
|
const commandUrlRef = (0, import_react12.useRef)(commandUrl);
|
|
@@ -3294,6 +3316,12 @@ function useTourPlayback({
|
|
|
3294
3316
|
toursApiBaseRef.current = toursApiBase;
|
|
3295
3317
|
pendingTourRef.current = pendingTour;
|
|
3296
3318
|
showCaptionsRef.current = showCaptions;
|
|
3319
|
+
const releasePlaybackOwnership = (0, import_react12.useCallback)(() => {
|
|
3320
|
+
const claimedKey = claimedPlaybackOwnerKeyRef.current;
|
|
3321
|
+
if (!claimedKey) return;
|
|
3322
|
+
releaseTourPlaybackOwnership(claimedKey, playbackOwnerIdRef.current);
|
|
3323
|
+
claimedPlaybackOwnerKeyRef.current = null;
|
|
3324
|
+
}, []);
|
|
3297
3325
|
(0, import_react12.useEffect)(() => {
|
|
3298
3326
|
if (disabled) return;
|
|
3299
3327
|
if (typeof window === "undefined") return;
|
|
@@ -3329,7 +3357,8 @@ function useTourPlayback({
|
|
|
3329
3357
|
}
|
|
3330
3358
|
};
|
|
3331
3359
|
const handleCommand = async (payload) => {
|
|
3332
|
-
|
|
3360
|
+
const ownerKey = createTourPlaybackOwnerKey(serverUrl, websiteIdRef.current, experienceTypeRef.current);
|
|
3361
|
+
if (!shouldExecuteTourCommandBatch(isActiveRef.current) || !hasTourPlaybackOwnership(ownerKey, playbackOwnerIdRef.current)) {
|
|
3333
3362
|
const activeType = experienceTypeRef.current;
|
|
3334
3363
|
console.log("[TourClient] Ignoring command batch for inactive playback hook:", activeType, payload.stepIndex);
|
|
3335
3364
|
return;
|
|
@@ -3839,6 +3868,12 @@ function useTourPlayback({
|
|
|
3839
3868
|
console.log(`[TourClient] Ignoring ${tour.type} start (this hook is for ${expType})`);
|
|
3840
3869
|
return;
|
|
3841
3870
|
}
|
|
3871
|
+
const ownerKey = createTourPlaybackOwnerKey(serverUrl, websiteIdRef.current, expType);
|
|
3872
|
+
if (!claimTourPlaybackOwnership(ownerKey, playbackOwnerIdRef.current)) {
|
|
3873
|
+
console.log(`[TourClient] Ignoring ${expType} start because another hook already owns playback`);
|
|
3874
|
+
return;
|
|
3875
|
+
}
|
|
3876
|
+
claimedPlaybackOwnerKeyRef.current = ownerKey;
|
|
3842
3877
|
skipRequestedRef.current = false;
|
|
3843
3878
|
startRequestedRef.current = false;
|
|
3844
3879
|
const total = tourData.totalSteps ?? tour?.steps?.length ?? 0;
|
|
@@ -3900,12 +3935,13 @@ function useTourPlayback({
|
|
|
3900
3935
|
const handleDebugLog = (entry) => {
|
|
3901
3936
|
const isDev = devModeRef.current || process.env.NODE_ENV === "development" || typeof window !== "undefined" && window.MODELNEX_DEBUG;
|
|
3902
3937
|
const entryTourType = entry?.data?.tourContext?.type ?? tourRef.current?.type ?? null;
|
|
3938
|
+
const ownerKey = createTourPlaybackOwnerKey(serverUrl, websiteIdRef.current, experienceTypeRef.current);
|
|
3903
3939
|
if (!shouldLogTourDebugEntry({
|
|
3904
3940
|
isPlaybackActive: isActiveRef.current,
|
|
3905
3941
|
entryType: entry?.type,
|
|
3906
3942
|
entryTourType,
|
|
3907
3943
|
experienceType: experienceTypeRef.current
|
|
3908
|
-
})) {
|
|
3944
|
+
}) || isActiveRef.current && !hasTourPlaybackOwnership(ownerKey, playbackOwnerIdRef.current)) {
|
|
3909
3945
|
return;
|
|
3910
3946
|
}
|
|
3911
3947
|
if (isDev) {
|
|
@@ -3940,9 +3976,10 @@ function useTourPlayback({
|
|
|
3940
3976
|
setServerState(null);
|
|
3941
3977
|
runIdRef.current = null;
|
|
3942
3978
|
turnIdRef.current = null;
|
|
3979
|
+
releasePlaybackOwnership();
|
|
3943
3980
|
tourSocketPool.release(serverUrl, toClose);
|
|
3944
3981
|
};
|
|
3945
|
-
}, [serverUrl, disabled]);
|
|
3982
|
+
}, [serverUrl, disabled, releasePlaybackOwnership]);
|
|
3946
3983
|
(0, import_react12.useEffect)(() => {
|
|
3947
3984
|
if (disabled) return;
|
|
3948
3985
|
const s = socketRef.current;
|
|
@@ -4014,6 +4051,7 @@ function useTourPlayback({
|
|
|
4014
4051
|
skipRequestedRef.current = true;
|
|
4015
4052
|
isActiveRef.current = false;
|
|
4016
4053
|
startRequestedRef.current = false;
|
|
4054
|
+
releasePlaybackOwnership();
|
|
4017
4055
|
activeExecutionTokenRef.current += 1;
|
|
4018
4056
|
commandInFlightRef.current = false;
|
|
4019
4057
|
activeCommandBatchIdRef.current = null;
|
|
@@ -4049,13 +4087,14 @@ function useTourPlayback({
|
|
|
4049
4087
|
setIsReviewMode(false);
|
|
4050
4088
|
pendingInputBufRef.current = null;
|
|
4051
4089
|
onTourEnd?.();
|
|
4052
|
-
}, [voice, onTourEnd, serverUrl, websiteId]);
|
|
4090
|
+
}, [voice, onTourEnd, serverUrl, websiteId, releasePlaybackOwnership]);
|
|
4053
4091
|
const handleTourEnd = (0, import_react12.useCallback)(() => {
|
|
4054
4092
|
const endingTourId = tourRef.current?.id;
|
|
4055
4093
|
const endingPreviewRunId = previewRunIdRef.current;
|
|
4056
4094
|
const endingStepOrder = stepIndexRef.current;
|
|
4057
4095
|
isActiveRef.current = false;
|
|
4058
4096
|
startRequestedRef.current = false;
|
|
4097
|
+
releasePlaybackOwnership();
|
|
4059
4098
|
setPlaybackState("complete");
|
|
4060
4099
|
removeHighlight();
|
|
4061
4100
|
removeCaption();
|
|
@@ -4087,7 +4126,7 @@ function useTourPlayback({
|
|
|
4087
4126
|
clearActiveDraftPreview(experienceType);
|
|
4088
4127
|
}
|
|
4089
4128
|
onTourEnd?.();
|
|
4090
|
-
}, [experienceType, userProfile, serverUrl, voice, onTourEnd, websiteId]);
|
|
4129
|
+
}, [experienceType, userProfile, serverUrl, voice, onTourEnd, websiteId, releasePlaybackOwnership]);
|
|
4091
4130
|
const runTour = (0, import_react12.useCallback)(async (tour, options) => {
|
|
4092
4131
|
if (!shouldAcceptTourStart({
|
|
4093
4132
|
isPlaybackActive: isActiveRef.current,
|
|
@@ -4107,6 +4146,12 @@ function useTourPlayback({
|
|
|
4107
4146
|
console.warn("[TourClient] Cannot run tour, socket not connected.");
|
|
4108
4147
|
return;
|
|
4109
4148
|
}
|
|
4149
|
+
const ownerKey = createTourPlaybackOwnerKey(serverUrl, websiteIdRef.current, tour.type ?? experienceTypeRef.current);
|
|
4150
|
+
if (!claimTourPlaybackOwnership(ownerKey, playbackOwnerIdRef.current)) {
|
|
4151
|
+
console.log("[TourClient] Ignoring duplicate start request because another hook already owns this experience:", tour.id);
|
|
4152
|
+
return;
|
|
4153
|
+
}
|
|
4154
|
+
claimedPlaybackOwnerKeyRef.current = ownerKey;
|
|
4110
4155
|
startRequestedRef.current = true;
|
|
4111
4156
|
const shouldReview = Boolean(options?.reviewMode);
|
|
4112
4157
|
resetCaptionSuppression();
|
package/dist/index.mjs
CHANGED
|
@@ -2509,6 +2509,7 @@ function createTourSocketPool({
|
|
|
2509
2509
|
var tourSocketPool = createTourSocketPool();
|
|
2510
2510
|
|
|
2511
2511
|
// src/utils/tour-playback-guards.ts
|
|
2512
|
+
var playbackOwners = /* @__PURE__ */ new Map();
|
|
2512
2513
|
function shouldExecuteTourCommandBatch(isPlaybackActive) {
|
|
2513
2514
|
return isPlaybackActive;
|
|
2514
2515
|
}
|
|
@@ -2532,6 +2533,25 @@ function shouldLogTourDebugEntry(state) {
|
|
|
2532
2533
|
}
|
|
2533
2534
|
return true;
|
|
2534
2535
|
}
|
|
2536
|
+
function createTourPlaybackOwnerKey(serverUrl, websiteId, experienceType) {
|
|
2537
|
+
return `${serverUrl}::${websiteId || "__default__"}::${experienceType}`;
|
|
2538
|
+
}
|
|
2539
|
+
function claimTourPlaybackOwnership(key, ownerId) {
|
|
2540
|
+
const currentOwner = playbackOwners.get(key);
|
|
2541
|
+
if (currentOwner && currentOwner !== ownerId) {
|
|
2542
|
+
return false;
|
|
2543
|
+
}
|
|
2544
|
+
playbackOwners.set(key, ownerId);
|
|
2545
|
+
return true;
|
|
2546
|
+
}
|
|
2547
|
+
function hasTourPlaybackOwnership(key, ownerId) {
|
|
2548
|
+
return playbackOwners.get(key) === ownerId;
|
|
2549
|
+
}
|
|
2550
|
+
function releaseTourPlaybackOwnership(key, ownerId) {
|
|
2551
|
+
if (playbackOwners.get(key) === ownerId) {
|
|
2552
|
+
playbackOwners.delete(key);
|
|
2553
|
+
}
|
|
2554
|
+
}
|
|
2535
2555
|
|
|
2536
2556
|
// src/hooks/useTourPlayback.ts
|
|
2537
2557
|
function resolveElement(step) {
|
|
@@ -3067,6 +3087,8 @@ function useTourPlayback({
|
|
|
3067
3087
|
const runIdRef = useRef8(null);
|
|
3068
3088
|
const turnIdRef = useRef8(null);
|
|
3069
3089
|
const startRequestedRef = useRef8(false);
|
|
3090
|
+
const playbackOwnerIdRef = useRef8(`playback-owner-${Math.random().toString(36).slice(2, 10)}`);
|
|
3091
|
+
const claimedPlaybackOwnerKeyRef = useRef8(null);
|
|
3070
3092
|
const socketRef = useRef8(null);
|
|
3071
3093
|
const socketIdRef = useRef8(socketId);
|
|
3072
3094
|
const commandUrlRef = useRef8(commandUrl);
|
|
@@ -3084,6 +3106,12 @@ function useTourPlayback({
|
|
|
3084
3106
|
toursApiBaseRef.current = toursApiBase;
|
|
3085
3107
|
pendingTourRef.current = pendingTour;
|
|
3086
3108
|
showCaptionsRef.current = showCaptions;
|
|
3109
|
+
const releasePlaybackOwnership = useCallback7(() => {
|
|
3110
|
+
const claimedKey = claimedPlaybackOwnerKeyRef.current;
|
|
3111
|
+
if (!claimedKey) return;
|
|
3112
|
+
releaseTourPlaybackOwnership(claimedKey, playbackOwnerIdRef.current);
|
|
3113
|
+
claimedPlaybackOwnerKeyRef.current = null;
|
|
3114
|
+
}, []);
|
|
3087
3115
|
useEffect11(() => {
|
|
3088
3116
|
if (disabled) return;
|
|
3089
3117
|
if (typeof window === "undefined") return;
|
|
@@ -3119,7 +3147,8 @@ function useTourPlayback({
|
|
|
3119
3147
|
}
|
|
3120
3148
|
};
|
|
3121
3149
|
const handleCommand = async (payload) => {
|
|
3122
|
-
|
|
3150
|
+
const ownerKey = createTourPlaybackOwnerKey(serverUrl, websiteIdRef.current, experienceTypeRef.current);
|
|
3151
|
+
if (!shouldExecuteTourCommandBatch(isActiveRef.current) || !hasTourPlaybackOwnership(ownerKey, playbackOwnerIdRef.current)) {
|
|
3123
3152
|
const activeType = experienceTypeRef.current;
|
|
3124
3153
|
console.log("[TourClient] Ignoring command batch for inactive playback hook:", activeType, payload.stepIndex);
|
|
3125
3154
|
return;
|
|
@@ -3629,6 +3658,12 @@ function useTourPlayback({
|
|
|
3629
3658
|
console.log(`[TourClient] Ignoring ${tour.type} start (this hook is for ${expType})`);
|
|
3630
3659
|
return;
|
|
3631
3660
|
}
|
|
3661
|
+
const ownerKey = createTourPlaybackOwnerKey(serverUrl, websiteIdRef.current, expType);
|
|
3662
|
+
if (!claimTourPlaybackOwnership(ownerKey, playbackOwnerIdRef.current)) {
|
|
3663
|
+
console.log(`[TourClient] Ignoring ${expType} start because another hook already owns playback`);
|
|
3664
|
+
return;
|
|
3665
|
+
}
|
|
3666
|
+
claimedPlaybackOwnerKeyRef.current = ownerKey;
|
|
3632
3667
|
skipRequestedRef.current = false;
|
|
3633
3668
|
startRequestedRef.current = false;
|
|
3634
3669
|
const total = tourData.totalSteps ?? tour?.steps?.length ?? 0;
|
|
@@ -3690,12 +3725,13 @@ function useTourPlayback({
|
|
|
3690
3725
|
const handleDebugLog = (entry) => {
|
|
3691
3726
|
const isDev = devModeRef.current || process.env.NODE_ENV === "development" || typeof window !== "undefined" && window.MODELNEX_DEBUG;
|
|
3692
3727
|
const entryTourType = entry?.data?.tourContext?.type ?? tourRef.current?.type ?? null;
|
|
3728
|
+
const ownerKey = createTourPlaybackOwnerKey(serverUrl, websiteIdRef.current, experienceTypeRef.current);
|
|
3693
3729
|
if (!shouldLogTourDebugEntry({
|
|
3694
3730
|
isPlaybackActive: isActiveRef.current,
|
|
3695
3731
|
entryType: entry?.type,
|
|
3696
3732
|
entryTourType,
|
|
3697
3733
|
experienceType: experienceTypeRef.current
|
|
3698
|
-
})) {
|
|
3734
|
+
}) || isActiveRef.current && !hasTourPlaybackOwnership(ownerKey, playbackOwnerIdRef.current)) {
|
|
3699
3735
|
return;
|
|
3700
3736
|
}
|
|
3701
3737
|
if (isDev) {
|
|
@@ -3730,9 +3766,10 @@ function useTourPlayback({
|
|
|
3730
3766
|
setServerState(null);
|
|
3731
3767
|
runIdRef.current = null;
|
|
3732
3768
|
turnIdRef.current = null;
|
|
3769
|
+
releasePlaybackOwnership();
|
|
3733
3770
|
tourSocketPool.release(serverUrl, toClose);
|
|
3734
3771
|
};
|
|
3735
|
-
}, [serverUrl, disabled]);
|
|
3772
|
+
}, [serverUrl, disabled, releasePlaybackOwnership]);
|
|
3736
3773
|
useEffect11(() => {
|
|
3737
3774
|
if (disabled) return;
|
|
3738
3775
|
const s = socketRef.current;
|
|
@@ -3804,6 +3841,7 @@ function useTourPlayback({
|
|
|
3804
3841
|
skipRequestedRef.current = true;
|
|
3805
3842
|
isActiveRef.current = false;
|
|
3806
3843
|
startRequestedRef.current = false;
|
|
3844
|
+
releasePlaybackOwnership();
|
|
3807
3845
|
activeExecutionTokenRef.current += 1;
|
|
3808
3846
|
commandInFlightRef.current = false;
|
|
3809
3847
|
activeCommandBatchIdRef.current = null;
|
|
@@ -3839,13 +3877,14 @@ function useTourPlayback({
|
|
|
3839
3877
|
setIsReviewMode(false);
|
|
3840
3878
|
pendingInputBufRef.current = null;
|
|
3841
3879
|
onTourEnd?.();
|
|
3842
|
-
}, [voice, onTourEnd, serverUrl, websiteId]);
|
|
3880
|
+
}, [voice, onTourEnd, serverUrl, websiteId, releasePlaybackOwnership]);
|
|
3843
3881
|
const handleTourEnd = useCallback7(() => {
|
|
3844
3882
|
const endingTourId = tourRef.current?.id;
|
|
3845
3883
|
const endingPreviewRunId = previewRunIdRef.current;
|
|
3846
3884
|
const endingStepOrder = stepIndexRef.current;
|
|
3847
3885
|
isActiveRef.current = false;
|
|
3848
3886
|
startRequestedRef.current = false;
|
|
3887
|
+
releasePlaybackOwnership();
|
|
3849
3888
|
setPlaybackState("complete");
|
|
3850
3889
|
removeHighlight();
|
|
3851
3890
|
removeCaption();
|
|
@@ -3877,7 +3916,7 @@ function useTourPlayback({
|
|
|
3877
3916
|
clearActiveDraftPreview(experienceType);
|
|
3878
3917
|
}
|
|
3879
3918
|
onTourEnd?.();
|
|
3880
|
-
}, [experienceType, userProfile, serverUrl, voice, onTourEnd, websiteId]);
|
|
3919
|
+
}, [experienceType, userProfile, serverUrl, voice, onTourEnd, websiteId, releasePlaybackOwnership]);
|
|
3881
3920
|
const runTour = useCallback7(async (tour, options) => {
|
|
3882
3921
|
if (!shouldAcceptTourStart({
|
|
3883
3922
|
isPlaybackActive: isActiveRef.current,
|
|
@@ -3897,6 +3936,12 @@ function useTourPlayback({
|
|
|
3897
3936
|
console.warn("[TourClient] Cannot run tour, socket not connected.");
|
|
3898
3937
|
return;
|
|
3899
3938
|
}
|
|
3939
|
+
const ownerKey = createTourPlaybackOwnerKey(serverUrl, websiteIdRef.current, tour.type ?? experienceTypeRef.current);
|
|
3940
|
+
if (!claimTourPlaybackOwnership(ownerKey, playbackOwnerIdRef.current)) {
|
|
3941
|
+
console.log("[TourClient] Ignoring duplicate start request because another hook already owns this experience:", tour.id);
|
|
3942
|
+
return;
|
|
3943
|
+
}
|
|
3944
|
+
claimedPlaybackOwnerKeyRef.current = ownerKey;
|
|
3900
3945
|
startRequestedRef.current = true;
|
|
3901
3946
|
const shouldReview = Boolean(options?.reviewMode);
|
|
3902
3947
|
resetCaptionSuppression();
|