@modelnex/sdk 0.5.17 → 0.5.18

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 CHANGED
@@ -2718,6 +2718,31 @@ function createTourSocketPool({
2718
2718
  }
2719
2719
  var tourSocketPool = createTourSocketPool();
2720
2720
 
2721
+ // src/utils/tour-playback-guards.ts
2722
+ function shouldExecuteTourCommandBatch(isPlaybackActive) {
2723
+ return isPlaybackActive;
2724
+ }
2725
+ function shouldAcceptTourStart(state) {
2726
+ return !state.isPlaybackActive && !state.startRequested;
2727
+ }
2728
+ function shouldRunTourAutoDiscovery(state) {
2729
+ return state.enableAutoDiscovery && !state.disabled && !state.isPlaybackActive && !state.startRequested && !state.hasPendingTour;
2730
+ }
2731
+ function shouldLogTourDebugEntry(state) {
2732
+ if (!state.isPlaybackActive) {
2733
+ if (state.entryType !== "tour_start") {
2734
+ return false;
2735
+ }
2736
+ if (state.entryTourType && state.entryTourType !== state.experienceType) {
2737
+ return false;
2738
+ }
2739
+ }
2740
+ if (state.entryTourType && state.entryTourType !== state.experienceType) {
2741
+ return false;
2742
+ }
2743
+ return true;
2744
+ }
2745
+
2721
2746
  // src/hooks/useTourPlayback.ts
2722
2747
  function resolveElement(step) {
2723
2748
  const el = step.element;
@@ -3251,6 +3276,7 @@ function useTourPlayback({
3251
3276
  const showCaptionsRef = (0, import_react12.useRef)(showCaptions);
3252
3277
  const runIdRef = (0, import_react12.useRef)(null);
3253
3278
  const turnIdRef = (0, import_react12.useRef)(null);
3279
+ const startRequestedRef = (0, import_react12.useRef)(false);
3254
3280
  const socketRef = (0, import_react12.useRef)(null);
3255
3281
  const socketIdRef = (0, import_react12.useRef)(socketId);
3256
3282
  const commandUrlRef = (0, import_react12.useRef)(commandUrl);
@@ -3303,6 +3329,11 @@ function useTourPlayback({
3303
3329
  }
3304
3330
  };
3305
3331
  const handleCommand = async (payload) => {
3332
+ if (!shouldExecuteTourCommandBatch(isActiveRef.current)) {
3333
+ const activeType = experienceTypeRef.current;
3334
+ console.log("[TourClient] Ignoring command batch for inactive playback hook:", activeType, payload.stepIndex);
3335
+ return;
3336
+ }
3306
3337
  const emitIfOpen = (ev, data) => {
3307
3338
  if (socketRef.current !== socket) return;
3308
3339
  emitSocketEvent(socket, ev, data);
@@ -3809,6 +3840,7 @@ function useTourPlayback({
3809
3840
  return;
3810
3841
  }
3811
3842
  skipRequestedRef.current = false;
3843
+ startRequestedRef.current = false;
3812
3844
  const total = tourData.totalSteps ?? tour?.steps?.length ?? 0;
3813
3845
  isActiveRef.current = true;
3814
3846
  setIsActive(true);
@@ -3867,6 +3899,15 @@ function useTourPlayback({
3867
3899
  };
3868
3900
  const handleDebugLog = (entry) => {
3869
3901
  const isDev = devModeRef.current || process.env.NODE_ENV === "development" || typeof window !== "undefined" && window.MODELNEX_DEBUG;
3902
+ const entryTourType = entry?.data?.tourContext?.type ?? tourRef.current?.type ?? null;
3903
+ if (!shouldLogTourDebugEntry({
3904
+ isPlaybackActive: isActiveRef.current,
3905
+ entryType: entry?.type,
3906
+ entryTourType,
3907
+ experienceType: experienceTypeRef.current
3908
+ })) {
3909
+ return;
3910
+ }
3870
3911
  if (isDev) {
3871
3912
  console.log(`%c[ModelNex Tour] ${entry.type}`, "color: #3b82f6; font-weight: bold", entry);
3872
3913
  if (typeof window !== "undefined") {
@@ -3972,6 +4013,7 @@ function useTourPlayback({
3972
4013
  const stopTour = (0, import_react12.useCallback)(() => {
3973
4014
  skipRequestedRef.current = true;
3974
4015
  isActiveRef.current = false;
4016
+ startRequestedRef.current = false;
3975
4017
  activeExecutionTokenRef.current += 1;
3976
4018
  commandInFlightRef.current = false;
3977
4019
  activeCommandBatchIdRef.current = null;
@@ -4013,6 +4055,7 @@ function useTourPlayback({
4013
4055
  const endingPreviewRunId = previewRunIdRef.current;
4014
4056
  const endingStepOrder = stepIndexRef.current;
4015
4057
  isActiveRef.current = false;
4058
+ startRequestedRef.current = false;
4016
4059
  setPlaybackState("complete");
4017
4060
  removeHighlight();
4018
4061
  removeCaption();
@@ -4046,6 +4089,13 @@ function useTourPlayback({
4046
4089
  onTourEnd?.();
4047
4090
  }, [experienceType, userProfile, serverUrl, voice, onTourEnd, websiteId]);
4048
4091
  const runTour = (0, import_react12.useCallback)(async (tour, options) => {
4092
+ if (!shouldAcceptTourStart({
4093
+ isPlaybackActive: isActiveRef.current,
4094
+ startRequested: startRequestedRef.current
4095
+ })) {
4096
+ console.log("[TourClient] Ignoring duplicate start request while playback is already active or starting:", tour.id);
4097
+ return;
4098
+ }
4049
4099
  setPendingTour(null);
4050
4100
  pendingTourRef.current = null;
4051
4101
  let retries = 0;
@@ -4057,6 +4107,7 @@ function useTourPlayback({
4057
4107
  console.warn("[TourClient] Cannot run tour, socket not connected.");
4058
4108
  return;
4059
4109
  }
4110
+ startRequestedRef.current = true;
4060
4111
  const shouldReview = Boolean(options?.reviewMode);
4061
4112
  resetCaptionSuppression();
4062
4113
  setReviewStatusMessage(null);
@@ -4088,8 +4139,13 @@ function useTourPlayback({
4088
4139
  });
4089
4140
  }, [serverUrl, websiteId]);
4090
4141
  (0, import_react12.useEffect)(() => {
4091
- if (!enableAutoDiscovery) return;
4092
- if (disabled) return;
4142
+ if (!shouldRunTourAutoDiscovery({
4143
+ enableAutoDiscovery,
4144
+ disabled,
4145
+ isPlaybackActive: isActiveRef.current,
4146
+ startRequested: startRequestedRef.current,
4147
+ hasPendingTour: Boolean(pendingTourRef.current)
4148
+ })) return;
4093
4149
  if (typeof window === "undefined") return;
4094
4150
  const params = new URLSearchParams(window.location.search);
4095
4151
  const queryParam = experienceType === "onboarding" ? "modelnex_test_workflow" : "modelnex_test_tour";
@@ -4137,8 +4193,13 @@ function useTourPlayback({
4137
4193
  };
4138
4194
  }, [serverUrl, toursApiBase, disabled, websiteId, experienceType, enableAutoDiscovery]);
4139
4195
  (0, import_react12.useEffect)(() => {
4140
- if (!enableAutoDiscovery) return;
4141
- if (disabled) return;
4196
+ if (!shouldRunTourAutoDiscovery({
4197
+ enableAutoDiscovery,
4198
+ disabled,
4199
+ isPlaybackActive: isActiveRef.current,
4200
+ startRequested: startRequestedRef.current,
4201
+ hasPendingTour: Boolean(pendingTourRef.current)
4202
+ })) return;
4142
4203
  if (!websiteId || !userProfile) return;
4143
4204
  if (typeof window !== "undefined") {
4144
4205
  const params = new URLSearchParams(window.location.search);
@@ -4177,7 +4238,7 @@ function useTourPlayback({
4177
4238
  cancelled = true;
4178
4239
  clearTimeout(timer);
4179
4240
  };
4180
- }, [websiteId, serverUrl, toursApiBase, disabled, experienceType, userProfile, enableAutoDiscovery]);
4241
+ }, [websiteId, serverUrl, toursApiBase, disabled, experienceType, userProfile?.userId, userProfile?.type, userProfile?.isNewUser, enableAutoDiscovery]);
4181
4242
  (0, import_react12.useEffect)(() => {
4182
4243
  if (!disabled || !isActiveRef.current) return;
4183
4244
  stopTour();
package/dist/index.mjs CHANGED
@@ -2508,6 +2508,31 @@ function createTourSocketPool({
2508
2508
  }
2509
2509
  var tourSocketPool = createTourSocketPool();
2510
2510
 
2511
+ // src/utils/tour-playback-guards.ts
2512
+ function shouldExecuteTourCommandBatch(isPlaybackActive) {
2513
+ return isPlaybackActive;
2514
+ }
2515
+ function shouldAcceptTourStart(state) {
2516
+ return !state.isPlaybackActive && !state.startRequested;
2517
+ }
2518
+ function shouldRunTourAutoDiscovery(state) {
2519
+ return state.enableAutoDiscovery && !state.disabled && !state.isPlaybackActive && !state.startRequested && !state.hasPendingTour;
2520
+ }
2521
+ function shouldLogTourDebugEntry(state) {
2522
+ if (!state.isPlaybackActive) {
2523
+ if (state.entryType !== "tour_start") {
2524
+ return false;
2525
+ }
2526
+ if (state.entryTourType && state.entryTourType !== state.experienceType) {
2527
+ return false;
2528
+ }
2529
+ }
2530
+ if (state.entryTourType && state.entryTourType !== state.experienceType) {
2531
+ return false;
2532
+ }
2533
+ return true;
2534
+ }
2535
+
2511
2536
  // src/hooks/useTourPlayback.ts
2512
2537
  function resolveElement(step) {
2513
2538
  const el = step.element;
@@ -3041,6 +3066,7 @@ function useTourPlayback({
3041
3066
  const showCaptionsRef = useRef8(showCaptions);
3042
3067
  const runIdRef = useRef8(null);
3043
3068
  const turnIdRef = useRef8(null);
3069
+ const startRequestedRef = useRef8(false);
3044
3070
  const socketRef = useRef8(null);
3045
3071
  const socketIdRef = useRef8(socketId);
3046
3072
  const commandUrlRef = useRef8(commandUrl);
@@ -3093,6 +3119,11 @@ function useTourPlayback({
3093
3119
  }
3094
3120
  };
3095
3121
  const handleCommand = async (payload) => {
3122
+ if (!shouldExecuteTourCommandBatch(isActiveRef.current)) {
3123
+ const activeType = experienceTypeRef.current;
3124
+ console.log("[TourClient] Ignoring command batch for inactive playback hook:", activeType, payload.stepIndex);
3125
+ return;
3126
+ }
3096
3127
  const emitIfOpen = (ev, data) => {
3097
3128
  if (socketRef.current !== socket) return;
3098
3129
  emitSocketEvent(socket, ev, data);
@@ -3599,6 +3630,7 @@ function useTourPlayback({
3599
3630
  return;
3600
3631
  }
3601
3632
  skipRequestedRef.current = false;
3633
+ startRequestedRef.current = false;
3602
3634
  const total = tourData.totalSteps ?? tour?.steps?.length ?? 0;
3603
3635
  isActiveRef.current = true;
3604
3636
  setIsActive(true);
@@ -3657,6 +3689,15 @@ function useTourPlayback({
3657
3689
  };
3658
3690
  const handleDebugLog = (entry) => {
3659
3691
  const isDev = devModeRef.current || process.env.NODE_ENV === "development" || typeof window !== "undefined" && window.MODELNEX_DEBUG;
3692
+ const entryTourType = entry?.data?.tourContext?.type ?? tourRef.current?.type ?? null;
3693
+ if (!shouldLogTourDebugEntry({
3694
+ isPlaybackActive: isActiveRef.current,
3695
+ entryType: entry?.type,
3696
+ entryTourType,
3697
+ experienceType: experienceTypeRef.current
3698
+ })) {
3699
+ return;
3700
+ }
3660
3701
  if (isDev) {
3661
3702
  console.log(`%c[ModelNex Tour] ${entry.type}`, "color: #3b82f6; font-weight: bold", entry);
3662
3703
  if (typeof window !== "undefined") {
@@ -3762,6 +3803,7 @@ function useTourPlayback({
3762
3803
  const stopTour = useCallback7(() => {
3763
3804
  skipRequestedRef.current = true;
3764
3805
  isActiveRef.current = false;
3806
+ startRequestedRef.current = false;
3765
3807
  activeExecutionTokenRef.current += 1;
3766
3808
  commandInFlightRef.current = false;
3767
3809
  activeCommandBatchIdRef.current = null;
@@ -3803,6 +3845,7 @@ function useTourPlayback({
3803
3845
  const endingPreviewRunId = previewRunIdRef.current;
3804
3846
  const endingStepOrder = stepIndexRef.current;
3805
3847
  isActiveRef.current = false;
3848
+ startRequestedRef.current = false;
3806
3849
  setPlaybackState("complete");
3807
3850
  removeHighlight();
3808
3851
  removeCaption();
@@ -3836,6 +3879,13 @@ function useTourPlayback({
3836
3879
  onTourEnd?.();
3837
3880
  }, [experienceType, userProfile, serverUrl, voice, onTourEnd, websiteId]);
3838
3881
  const runTour = useCallback7(async (tour, options) => {
3882
+ if (!shouldAcceptTourStart({
3883
+ isPlaybackActive: isActiveRef.current,
3884
+ startRequested: startRequestedRef.current
3885
+ })) {
3886
+ console.log("[TourClient] Ignoring duplicate start request while playback is already active or starting:", tour.id);
3887
+ return;
3888
+ }
3839
3889
  setPendingTour(null);
3840
3890
  pendingTourRef.current = null;
3841
3891
  let retries = 0;
@@ -3847,6 +3897,7 @@ function useTourPlayback({
3847
3897
  console.warn("[TourClient] Cannot run tour, socket not connected.");
3848
3898
  return;
3849
3899
  }
3900
+ startRequestedRef.current = true;
3850
3901
  const shouldReview = Boolean(options?.reviewMode);
3851
3902
  resetCaptionSuppression();
3852
3903
  setReviewStatusMessage(null);
@@ -3878,8 +3929,13 @@ function useTourPlayback({
3878
3929
  });
3879
3930
  }, [serverUrl, websiteId]);
3880
3931
  useEffect11(() => {
3881
- if (!enableAutoDiscovery) return;
3882
- if (disabled) return;
3932
+ if (!shouldRunTourAutoDiscovery({
3933
+ enableAutoDiscovery,
3934
+ disabled,
3935
+ isPlaybackActive: isActiveRef.current,
3936
+ startRequested: startRequestedRef.current,
3937
+ hasPendingTour: Boolean(pendingTourRef.current)
3938
+ })) return;
3883
3939
  if (typeof window === "undefined") return;
3884
3940
  const params = new URLSearchParams(window.location.search);
3885
3941
  const queryParam = experienceType === "onboarding" ? "modelnex_test_workflow" : "modelnex_test_tour";
@@ -3927,8 +3983,13 @@ function useTourPlayback({
3927
3983
  };
3928
3984
  }, [serverUrl, toursApiBase, disabled, websiteId, experienceType, enableAutoDiscovery]);
3929
3985
  useEffect11(() => {
3930
- if (!enableAutoDiscovery) return;
3931
- if (disabled) return;
3986
+ if (!shouldRunTourAutoDiscovery({
3987
+ enableAutoDiscovery,
3988
+ disabled,
3989
+ isPlaybackActive: isActiveRef.current,
3990
+ startRequested: startRequestedRef.current,
3991
+ hasPendingTour: Boolean(pendingTourRef.current)
3992
+ })) return;
3932
3993
  if (!websiteId || !userProfile) return;
3933
3994
  if (typeof window !== "undefined") {
3934
3995
  const params = new URLSearchParams(window.location.search);
@@ -3967,7 +4028,7 @@ function useTourPlayback({
3967
4028
  cancelled = true;
3968
4029
  clearTimeout(timer);
3969
4030
  };
3970
- }, [websiteId, serverUrl, toursApiBase, disabled, experienceType, userProfile, enableAutoDiscovery]);
4031
+ }, [websiteId, serverUrl, toursApiBase, disabled, experienceType, userProfile?.userId, userProfile?.type, userProfile?.isNewUser, enableAutoDiscovery]);
3971
4032
  useEffect11(() => {
3972
4033
  if (!disabled || !isActiveRef.current) return;
3973
4034
  stopTour();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@modelnex/sdk",
3
- "version": "0.5.17",
3
+ "version": "0.5.18",
4
4
  "description": "React SDK for natural language control of web apps via AI agents",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",