@flowsterix/react 0.3.1 → 0.4.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,7 +1,7 @@
1
1
  import {
2
2
  createPathString,
3
3
  notifyRouteChange
4
- } from "./chunk-WTGQUDPT.mjs";
4
+ } from "./chunk-B44EX7YT.mjs";
5
5
 
6
6
  // src/router/tanstackRouterAdapter.tsx
7
7
  import { useRouterState } from "@tanstack/react-router";
@@ -222,6 +222,12 @@ var notifyRouteChange = (path) => {
222
222
  var subscribeToRouteChanges = (listener) => {
223
223
  return routeGatingChannel.subscribe(listener);
224
224
  };
225
+ var matchRoute = (params) => {
226
+ const { pattern, path } = params;
227
+ if (!pattern) return true;
228
+ if (typeof pattern === "string") return path === pattern;
229
+ return pattern.test(path);
230
+ };
225
231
 
226
232
  // src/router/utils.ts
227
233
  var ensurePrefix = (value, prefix) => value.startsWith(prefix) ? value : `${prefix}${value}`;
@@ -278,5 +284,6 @@ export {
278
284
  getCurrentRoutePath,
279
285
  notifyRouteChange,
280
286
  subscribeToRouteChanges,
287
+ matchRoute,
281
288
  createPathString
282
289
  };
@@ -1 +1 @@
1
- {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../src/context.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../src/context.tsx"],"names":[],"mappings":"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"}
package/dist/index.cjs CHANGED
@@ -305,6 +305,127 @@ var supportsMasking = () => {
305
305
  return cachedMaskSupport;
306
306
  };
307
307
 
308
+ // src/router/routeGating.ts
309
+ var DEFAULT_POLL_MS = 150;
310
+ var normalizePathname = (pathname) => {
311
+ if (typeof pathname !== "string" || pathname.length === 0) {
312
+ return "/";
313
+ }
314
+ return pathname.startsWith("/") ? pathname : `/${pathname}`;
315
+ };
316
+ var normalizePrefixedSegment = (value, prefix) => {
317
+ if (typeof value !== "string" || value.length === 0) {
318
+ return "";
319
+ }
320
+ return value.startsWith(prefix) ? value : `${prefix}${value}`;
321
+ };
322
+ var getWindowPath = () => {
323
+ if (!isBrowser) return "/";
324
+ const { pathname, search, hash } = window.location;
325
+ return normalizePathname(pathname) + normalizePrefixedSegment(search, "?") + normalizePrefixedSegment(hash, "#");
326
+ };
327
+ var normalizeExternalPath = (path) => {
328
+ if (path.length === 0) {
329
+ return "/";
330
+ }
331
+ try {
332
+ const parsed = new URL(path, "http://flowsterix.local");
333
+ return normalizePathname(parsed.pathname) + normalizePrefixedSegment(parsed.search, "?") + normalizePrefixedSegment(parsed.hash, "#");
334
+ } catch {
335
+ const [withoutHash, hash = ""] = path.split("#");
336
+ const [base, search = ""] = withoutHash.split("?");
337
+ return normalizePathname(base) + normalizePrefixedSegment(search ? `?${search}` : "", "?") + normalizePrefixedSegment(hash ? `#${hash}` : "", "#");
338
+ }
339
+ };
340
+ var RouteGatingChannel = class {
341
+ #listeners = /* @__PURE__ */ new Set();
342
+ #currentPath = getWindowPath();
343
+ #teardown = null;
344
+ #attachDefaultListeners() {
345
+ if (!isBrowser) return;
346
+ if (this.#teardown) return;
347
+ let lastPath = getWindowPath();
348
+ const emitIfChanged = () => {
349
+ const nextPath = getWindowPath();
350
+ if (nextPath === lastPath) return;
351
+ lastPath = nextPath;
352
+ this.notify(nextPath);
353
+ };
354
+ const handler = () => emitIfChanged();
355
+ window.addEventListener("popstate", handler);
356
+ window.addEventListener("hashchange", handler);
357
+ const pollId = window.setInterval(emitIfChanged, DEFAULT_POLL_MS);
358
+ this.#teardown = () => {
359
+ window.removeEventListener("popstate", handler);
360
+ window.removeEventListener("hashchange", handler);
361
+ window.clearInterval(pollId);
362
+ this.#teardown = null;
363
+ };
364
+ }
365
+ #detachDefaultListeners() {
366
+ if (this.#listeners.size > 0) return;
367
+ this.#teardown?.();
368
+ this.#teardown = null;
369
+ }
370
+ getCurrentPath() {
371
+ if (isBrowser) {
372
+ this.#currentPath = getWindowPath();
373
+ }
374
+ return this.#currentPath;
375
+ }
376
+ notify(path) {
377
+ const resolved = typeof path === "string" && path.length > 0 ? normalizeExternalPath(path) : this.getCurrentPath();
378
+ if (resolved === this.#currentPath) {
379
+ this.#currentPath = resolved;
380
+ return;
381
+ }
382
+ this.#currentPath = resolved;
383
+ for (const listener of Array.from(this.#listeners)) {
384
+ try {
385
+ listener(resolved);
386
+ } catch (error) {
387
+ console.warn("[tour][route-gating] listener error", error);
388
+ }
389
+ }
390
+ }
391
+ subscribe(listener) {
392
+ if (this.#listeners.has(listener)) {
393
+ return () => {
394
+ this.#listeners.delete(listener);
395
+ this.#detachDefaultListeners();
396
+ };
397
+ }
398
+ this.#listeners.add(listener);
399
+ if (this.#listeners.size === 1) {
400
+ this.#attachDefaultListeners();
401
+ }
402
+ const current = this.getCurrentPath();
403
+ try {
404
+ listener(current);
405
+ } catch (error) {
406
+ console.warn("[tour][route-gating] listener error", error);
407
+ }
408
+ return () => {
409
+ this.#listeners.delete(listener);
410
+ this.#detachDefaultListeners();
411
+ };
412
+ }
413
+ };
414
+ var routeGatingChannel = new RouteGatingChannel();
415
+ var getCurrentRoutePath = () => routeGatingChannel.getCurrentPath();
416
+ var notifyRouteChange = (path) => {
417
+ routeGatingChannel.notify(path);
418
+ };
419
+ var subscribeToRouteChanges = (listener) => {
420
+ return routeGatingChannel.subscribe(listener);
421
+ };
422
+ var matchRoute = (params) => {
423
+ const { pattern, path } = params;
424
+ if (!pattern) return true;
425
+ if (typeof pattern === "string") return path === pattern;
426
+ return pattern.test(path);
427
+ };
428
+
308
429
  // src/context.tsx
309
430
  var import_jsx_runtime2 = require("react/jsx-runtime");
310
431
  var TourContext = (0, import_react4.createContext)(void 0);
@@ -352,9 +473,6 @@ var TourProvider = ({
352
473
  );
353
474
  const [debugEnabled, setDebugEnabled] = (0, import_react4.useState)(defaultDebug);
354
475
  const [delayInfo, setDelayInfo] = (0, import_react4.useState)(null);
355
- const autoStartFlow = (0, import_react4.useMemo)(() => {
356
- return flows.find((flow) => flow.autoStart);
357
- }, [flows]);
358
476
  const teardownStore = (0, import_react4.useCallback)(() => {
359
477
  unsubscribeRef.current?.();
360
478
  unsubscribeRef.current = null;
@@ -387,8 +505,7 @@ var TourProvider = ({
387
505
  if (!hook) return;
388
506
  try {
389
507
  const result = hook(context);
390
- if (typeof result === "object" && result !== null && typeof result.then === "function") {
391
- ;
508
+ if (result instanceof Promise) {
392
509
  result.catch((error) => {
393
510
  console.warn(`[tour][step] ${phase} hook rejected`, error);
394
511
  });
@@ -557,49 +674,106 @@ var TourProvider = ({
557
674
  },
558
675
  [ensureStore, resolveResumeStrategy, runResumeHooks]
559
676
  );
677
+ const [eligibleFlows, setEligibleFlows] = (0, import_react4.useState)([]);
560
678
  (0, import_react4.useEffect)(() => {
561
- if (!autoStartFlow) {
562
- autoStartRequestedRef.current = null;
679
+ const autoStartFlows = flows.filter((f) => f.autoStart);
680
+ if (autoStartFlows.length === 0) {
681
+ setEligibleFlows([]);
682
+ return;
683
+ }
684
+ if (!storageAdapter && !fallbackStorageRef.current && isBrowser) {
685
+ fallbackStorageRef.current = (0, import_core.createLocalStorageAdapter)();
686
+ }
687
+ const resolvedStorageAdapter = storageAdapter ?? fallbackStorageRef.current;
688
+ if (!resolvedStorageAdapter) {
689
+ setEligibleFlows(
690
+ autoStartFlows.map((flow) => ({
691
+ flow,
692
+ resolvedState: null,
693
+ stepIndex: 0
694
+ }))
695
+ );
563
696
  return;
564
697
  }
565
- if (activeFlowId) return;
566
- if (autoStartRequestedRef.current === autoStartFlow.id) return;
567
- autoStartRequestedRef.current = autoStartFlow.id;
568
698
  let cancelled = false;
569
- const maybeAutoStart = async () => {
570
- if (!storageAdapter && !fallbackStorageRef.current && isBrowser) {
571
- fallbackStorageRef.current = (0, import_core.createLocalStorageAdapter)();
572
- }
573
- const resolvedStorageAdapter = storageAdapter ? storageAdapter : fallbackStorageRef.current;
574
- if (!resolvedStorageAdapter) {
575
- startFlow(autoStartFlow.id, { resume: true });
576
- return;
577
- }
578
- const storageKey = storageNamespace ? `${storageNamespace}:${autoStartFlow.id}` : `${DEFAULT_STORAGE_PREFIX}:${autoStartFlow.id}`;
579
- const snapshot = await (0, import_core.resolveMaybePromise)(
580
- resolvedStorageAdapter.get(storageKey)
699
+ const findEligible = async () => {
700
+ const storageKeys = autoStartFlows.map(
701
+ (f) => storageNamespace ? `${storageNamespace}:${f.id}` : `${DEFAULT_STORAGE_PREFIX}:${f.id}`
702
+ );
703
+ const snapshots = await Promise.all(
704
+ storageKeys.map(
705
+ (key) => (0, import_core.resolveMaybePromise)(resolvedStorageAdapter.get(key))
706
+ )
581
707
  );
582
708
  if (cancelled) return;
583
- const currentVersionStr = (0, import_core.serializeVersion)(autoStartFlow.version);
584
- const storedVersionStr = typeof snapshot?.version === "number" ? (0, import_core.serializeVersion)({ major: snapshot.version, minor: 0 }) : snapshot?.version;
585
- if (snapshot && storedVersionStr === currentVersionStr) {
586
- const storedState = snapshot.value;
587
- const isFinished = storedState.status === "completed";
588
- const isSkipped = storedState.status === "cancelled" && storedState.cancelReason === "skipped";
589
- if (isFinished || isSkipped) {
590
- return;
709
+ const eligible = [];
710
+ for (let i = 0; i < autoStartFlows.length; i++) {
711
+ const flow = autoStartFlows[i];
712
+ const snapshot = snapshots[i];
713
+ if (!snapshot) {
714
+ eligible.push({ flow, resolvedState: null, stepIndex: 0 });
715
+ continue;
716
+ }
717
+ const storedVersionStr = typeof snapshot.version === "number" ? (0, import_core.serializeVersion)({ major: snapshot.version, minor: 0 }) : snapshot.version;
718
+ const storedVersion = (0, import_core.parseVersion)(storedVersionStr);
719
+ const stepIdMap = (0, import_core.buildStepIdMap)(flow);
720
+ const { state: resolvedState } = (0, import_core.handleVersionMismatch)({
721
+ storedState: snapshot.value,
722
+ storedVersion,
723
+ definition: flow,
724
+ currentVersion: flow.version,
725
+ stepIdMap,
726
+ now: () => Date.now()
727
+ });
728
+ if (resolvedState.status === "completed") continue;
729
+ if (resolvedState.status === "cancelled" && resolvedState.cancelReason === "skipped") {
730
+ continue;
591
731
  }
732
+ const stepIndex = Math.max(0, resolvedState.stepIndex);
733
+ eligible.push({ flow, resolvedState, stepIndex });
592
734
  }
593
- startFlow(autoStartFlow.id, { resume: true });
735
+ setEligibleFlows(eligible);
594
736
  };
595
- void maybeAutoStart();
737
+ void findEligible();
596
738
  return () => {
597
739
  cancelled = true;
740
+ };
741
+ }, [flows, storageAdapter, storageNamespace]);
742
+ (0, import_react4.useEffect)(() => {
743
+ if (eligibleFlows.length === 0) {
744
+ autoStartRequestedRef.current = null;
745
+ return;
746
+ }
747
+ if (activeFlowId) return;
748
+ const findMatchingFlow = (path) => {
749
+ for (const { flow, stepIndex } of eligibleFlows) {
750
+ const step = flow.steps[stepIndex];
751
+ if (!step.route || matchRoute({ pattern: step.route, path })) {
752
+ return flow;
753
+ }
754
+ }
755
+ return null;
756
+ };
757
+ const tryStart = (path) => {
758
+ if (activeFlowId) return;
759
+ const flow = findMatchingFlow(path);
760
+ if (flow && autoStartRequestedRef.current !== flow.id) {
761
+ autoStartRequestedRef.current = flow.id;
762
+ startFlow(flow.id, { resume: true });
763
+ }
764
+ };
765
+ const currentPath = getCurrentRoutePath();
766
+ tryStart(currentPath);
767
+ const unsubscribe = subscribeToRouteChanges((path) => {
768
+ tryStart(path);
769
+ });
770
+ return () => {
771
+ unsubscribe();
598
772
  if (!activeFlowId) {
599
773
  autoStartRequestedRef.current = null;
600
774
  }
601
775
  };
602
- }, [activeFlowId, autoStartFlow, startFlow, storageAdapter, storageNamespace]);
776
+ }, [activeFlowId, eligibleFlows, startFlow]);
603
777
  const next = (0, import_react4.useCallback)(() => getActiveStore().next(), [getActiveStore]);
604
778
  const back = (0, import_react4.useCallback)(() => getActiveStore().back(), [getActiveStore]);
605
779
  const goToStep = (0, import_react4.useCallback)(
@@ -1493,123 +1667,6 @@ var import_react9 = require("react");
1493
1667
 
1494
1668
  // src/hooks/useAdvanceRules.ts
1495
1669
  var import_react6 = require("react");
1496
-
1497
- // src/router/routeGating.ts
1498
- var DEFAULT_POLL_MS = 150;
1499
- var normalizePathname = (pathname) => {
1500
- if (typeof pathname !== "string" || pathname.length === 0) {
1501
- return "/";
1502
- }
1503
- return pathname.startsWith("/") ? pathname : `/${pathname}`;
1504
- };
1505
- var normalizePrefixedSegment = (value, prefix) => {
1506
- if (typeof value !== "string" || value.length === 0) {
1507
- return "";
1508
- }
1509
- return value.startsWith(prefix) ? value : `${prefix}${value}`;
1510
- };
1511
- var getWindowPath = () => {
1512
- if (!isBrowser) return "/";
1513
- const { pathname, search, hash } = window.location;
1514
- return normalizePathname(pathname) + normalizePrefixedSegment(search, "?") + normalizePrefixedSegment(hash, "#");
1515
- };
1516
- var normalizeExternalPath = (path) => {
1517
- if (path.length === 0) {
1518
- return "/";
1519
- }
1520
- try {
1521
- const parsed = new URL(path, "http://flowsterix.local");
1522
- return normalizePathname(parsed.pathname) + normalizePrefixedSegment(parsed.search, "?") + normalizePrefixedSegment(parsed.hash, "#");
1523
- } catch {
1524
- const [withoutHash, hash = ""] = path.split("#");
1525
- const [base, search = ""] = withoutHash.split("?");
1526
- return normalizePathname(base) + normalizePrefixedSegment(search ? `?${search}` : "", "?") + normalizePrefixedSegment(hash ? `#${hash}` : "", "#");
1527
- }
1528
- };
1529
- var RouteGatingChannel = class {
1530
- #listeners = /* @__PURE__ */ new Set();
1531
- #currentPath = getWindowPath();
1532
- #teardown = null;
1533
- #attachDefaultListeners() {
1534
- if (!isBrowser) return;
1535
- if (this.#teardown) return;
1536
- let lastPath = getWindowPath();
1537
- const emitIfChanged = () => {
1538
- const nextPath = getWindowPath();
1539
- if (nextPath === lastPath) return;
1540
- lastPath = nextPath;
1541
- this.notify(nextPath);
1542
- };
1543
- const handler = () => emitIfChanged();
1544
- window.addEventListener("popstate", handler);
1545
- window.addEventListener("hashchange", handler);
1546
- const pollId = window.setInterval(emitIfChanged, DEFAULT_POLL_MS);
1547
- this.#teardown = () => {
1548
- window.removeEventListener("popstate", handler);
1549
- window.removeEventListener("hashchange", handler);
1550
- window.clearInterval(pollId);
1551
- this.#teardown = null;
1552
- };
1553
- }
1554
- #detachDefaultListeners() {
1555
- if (this.#listeners.size > 0) return;
1556
- this.#teardown?.();
1557
- this.#teardown = null;
1558
- }
1559
- getCurrentPath() {
1560
- if (isBrowser) {
1561
- this.#currentPath = getWindowPath();
1562
- }
1563
- return this.#currentPath;
1564
- }
1565
- notify(path) {
1566
- const resolved = typeof path === "string" && path.length > 0 ? normalizeExternalPath(path) : this.getCurrentPath();
1567
- if (resolved === this.#currentPath) {
1568
- this.#currentPath = resolved;
1569
- return;
1570
- }
1571
- this.#currentPath = resolved;
1572
- for (const listener of Array.from(this.#listeners)) {
1573
- try {
1574
- listener(resolved);
1575
- } catch (error) {
1576
- console.warn("[tour][route-gating] listener error", error);
1577
- }
1578
- }
1579
- }
1580
- subscribe(listener) {
1581
- if (this.#listeners.has(listener)) {
1582
- return () => {
1583
- this.#listeners.delete(listener);
1584
- this.#detachDefaultListeners();
1585
- };
1586
- }
1587
- this.#listeners.add(listener);
1588
- if (this.#listeners.size === 1) {
1589
- this.#attachDefaultListeners();
1590
- }
1591
- const current = this.getCurrentPath();
1592
- try {
1593
- listener(current);
1594
- } catch (error) {
1595
- console.warn("[tour][route-gating] listener error", error);
1596
- }
1597
- return () => {
1598
- this.#listeners.delete(listener);
1599
- this.#detachDefaultListeners();
1600
- };
1601
- }
1602
- };
1603
- var routeGatingChannel = new RouteGatingChannel();
1604
- var getCurrentRoutePath = () => routeGatingChannel.getCurrentPath();
1605
- var notifyRouteChange = (path) => {
1606
- routeGatingChannel.notify(path);
1607
- };
1608
- var subscribeToRouteChanges = (listener) => {
1609
- return routeGatingChannel.subscribe(listener);
1610
- };
1611
-
1612
- // src/hooks/useAdvanceRules.ts
1613
1670
  var DEFAULT_POLL_MS2 = 250;
1614
1671
  var isListenerTarget = (value) => {
1615
1672
  return !!value && typeof value.addEventListener === "function" && typeof value.removeEventListener === "function";
@@ -3390,7 +3447,7 @@ var TourPopoverPortal = ({
3390
3447
  padding: FLOATING_OFFSET,
3391
3448
  alignment: autoAlignment
3392
3449
  })
3393
- ] : [(0, import_dom13.flip)({ padding: FLOATING_OFFSET })],
3450
+ ] : [(0, import_dom13.flip)({ padding: FLOATING_OFFSET, fallbackStrategy: "bestFit" })],
3394
3451
  (0, import_dom13.shift)({ padding: FLOATING_OFFSET })
3395
3452
  ];
3396
3453
  const updatePosition = async () => {
package/dist/index.mjs CHANGED
@@ -6,16 +6,20 @@ import {
6
6
  getScrollParents,
7
7
  getViewportRect,
8
8
  isBrowser,
9
+ matchRoute,
9
10
  notifyRouteChange,
10
11
  portalHost,
11
12
  subscribeToRouteChanges,
12
13
  supportsMasking
13
- } from "./chunk-WTGQUDPT.mjs";
14
+ } from "./chunk-B44EX7YT.mjs";
14
15
 
15
16
  // src/context.tsx
16
17
  import {
18
+ buildStepIdMap,
17
19
  createFlowStore,
18
20
  createLocalStorageAdapter,
21
+ handleVersionMismatch,
22
+ parseVersion,
19
23
  resolveMaybePromise,
20
24
  serializeVersion
21
25
  } from "@flowsterix/core";
@@ -210,9 +214,6 @@ var TourProvider = ({
210
214
  );
211
215
  const [debugEnabled, setDebugEnabled] = useState2(defaultDebug);
212
216
  const [delayInfo, setDelayInfo] = useState2(null);
213
- const autoStartFlow = useMemo2(() => {
214
- return flows.find((flow) => flow.autoStart);
215
- }, [flows]);
216
217
  const teardownStore = useCallback(() => {
217
218
  unsubscribeRef.current?.();
218
219
  unsubscribeRef.current = null;
@@ -245,8 +246,7 @@ var TourProvider = ({
245
246
  if (!hook) return;
246
247
  try {
247
248
  const result = hook(context);
248
- if (typeof result === "object" && result !== null && typeof result.then === "function") {
249
- ;
249
+ if (result instanceof Promise) {
250
250
  result.catch((error) => {
251
251
  console.warn(`[tour][step] ${phase} hook rejected`, error);
252
252
  });
@@ -415,49 +415,106 @@ var TourProvider = ({
415
415
  },
416
416
  [ensureStore, resolveResumeStrategy, runResumeHooks]
417
417
  );
418
+ const [eligibleFlows, setEligibleFlows] = useState2([]);
418
419
  useEffect2(() => {
419
- if (!autoStartFlow) {
420
- autoStartRequestedRef.current = null;
420
+ const autoStartFlows = flows.filter((f) => f.autoStart);
421
+ if (autoStartFlows.length === 0) {
422
+ setEligibleFlows([]);
423
+ return;
424
+ }
425
+ if (!storageAdapter && !fallbackStorageRef.current && isBrowser) {
426
+ fallbackStorageRef.current = createLocalStorageAdapter();
427
+ }
428
+ const resolvedStorageAdapter = storageAdapter ?? fallbackStorageRef.current;
429
+ if (!resolvedStorageAdapter) {
430
+ setEligibleFlows(
431
+ autoStartFlows.map((flow) => ({
432
+ flow,
433
+ resolvedState: null,
434
+ stepIndex: 0
435
+ }))
436
+ );
421
437
  return;
422
438
  }
423
- if (activeFlowId) return;
424
- if (autoStartRequestedRef.current === autoStartFlow.id) return;
425
- autoStartRequestedRef.current = autoStartFlow.id;
426
439
  let cancelled = false;
427
- const maybeAutoStart = async () => {
428
- if (!storageAdapter && !fallbackStorageRef.current && isBrowser) {
429
- fallbackStorageRef.current = createLocalStorageAdapter();
430
- }
431
- const resolvedStorageAdapter = storageAdapter ? storageAdapter : fallbackStorageRef.current;
432
- if (!resolvedStorageAdapter) {
433
- startFlow(autoStartFlow.id, { resume: true });
434
- return;
435
- }
436
- const storageKey = storageNamespace ? `${storageNamespace}:${autoStartFlow.id}` : `${DEFAULT_STORAGE_PREFIX}:${autoStartFlow.id}`;
437
- const snapshot = await resolveMaybePromise(
438
- resolvedStorageAdapter.get(storageKey)
440
+ const findEligible = async () => {
441
+ const storageKeys = autoStartFlows.map(
442
+ (f) => storageNamespace ? `${storageNamespace}:${f.id}` : `${DEFAULT_STORAGE_PREFIX}:${f.id}`
443
+ );
444
+ const snapshots = await Promise.all(
445
+ storageKeys.map(
446
+ (key) => resolveMaybePromise(resolvedStorageAdapter.get(key))
447
+ )
439
448
  );
440
449
  if (cancelled) return;
441
- const currentVersionStr = serializeVersion(autoStartFlow.version);
442
- const storedVersionStr = typeof snapshot?.version === "number" ? serializeVersion({ major: snapshot.version, minor: 0 }) : snapshot?.version;
443
- if (snapshot && storedVersionStr === currentVersionStr) {
444
- const storedState = snapshot.value;
445
- const isFinished = storedState.status === "completed";
446
- const isSkipped = storedState.status === "cancelled" && storedState.cancelReason === "skipped";
447
- if (isFinished || isSkipped) {
448
- return;
450
+ const eligible = [];
451
+ for (let i = 0; i < autoStartFlows.length; i++) {
452
+ const flow = autoStartFlows[i];
453
+ const snapshot = snapshots[i];
454
+ if (!snapshot) {
455
+ eligible.push({ flow, resolvedState: null, stepIndex: 0 });
456
+ continue;
457
+ }
458
+ const storedVersionStr = typeof snapshot.version === "number" ? serializeVersion({ major: snapshot.version, minor: 0 }) : snapshot.version;
459
+ const storedVersion = parseVersion(storedVersionStr);
460
+ const stepIdMap = buildStepIdMap(flow);
461
+ const { state: resolvedState } = handleVersionMismatch({
462
+ storedState: snapshot.value,
463
+ storedVersion,
464
+ definition: flow,
465
+ currentVersion: flow.version,
466
+ stepIdMap,
467
+ now: () => Date.now()
468
+ });
469
+ if (resolvedState.status === "completed") continue;
470
+ if (resolvedState.status === "cancelled" && resolvedState.cancelReason === "skipped") {
471
+ continue;
449
472
  }
473
+ const stepIndex = Math.max(0, resolvedState.stepIndex);
474
+ eligible.push({ flow, resolvedState, stepIndex });
450
475
  }
451
- startFlow(autoStartFlow.id, { resume: true });
476
+ setEligibleFlows(eligible);
452
477
  };
453
- void maybeAutoStart();
478
+ void findEligible();
454
479
  return () => {
455
480
  cancelled = true;
481
+ };
482
+ }, [flows, storageAdapter, storageNamespace]);
483
+ useEffect2(() => {
484
+ if (eligibleFlows.length === 0) {
485
+ autoStartRequestedRef.current = null;
486
+ return;
487
+ }
488
+ if (activeFlowId) return;
489
+ const findMatchingFlow = (path) => {
490
+ for (const { flow, stepIndex } of eligibleFlows) {
491
+ const step = flow.steps[stepIndex];
492
+ if (!step.route || matchRoute({ pattern: step.route, path })) {
493
+ return flow;
494
+ }
495
+ }
496
+ return null;
497
+ };
498
+ const tryStart = (path) => {
499
+ if (activeFlowId) return;
500
+ const flow = findMatchingFlow(path);
501
+ if (flow && autoStartRequestedRef.current !== flow.id) {
502
+ autoStartRequestedRef.current = flow.id;
503
+ startFlow(flow.id, { resume: true });
504
+ }
505
+ };
506
+ const currentPath = getCurrentRoutePath();
507
+ tryStart(currentPath);
508
+ const unsubscribe = subscribeToRouteChanges((path) => {
509
+ tryStart(path);
510
+ });
511
+ return () => {
512
+ unsubscribe();
456
513
  if (!activeFlowId) {
457
514
  autoStartRequestedRef.current = null;
458
515
  }
459
516
  };
460
- }, [activeFlowId, autoStartFlow, startFlow, storageAdapter, storageNamespace]);
517
+ }, [activeFlowId, eligibleFlows, startFlow]);
461
518
  const next = useCallback(() => getActiveStore().next(), [getActiveStore]);
462
519
  const back = useCallback(() => getActiveStore().back(), [getActiveStore]);
463
520
  const goToStep = useCallback(
@@ -3137,7 +3194,7 @@ var TourPopoverPortal = ({
3137
3194
  padding: FLOATING_OFFSET,
3138
3195
  alignment: autoAlignment
3139
3196
  })
3140
- ] : [flip({ padding: FLOATING_OFFSET })],
3197
+ ] : [flip({ padding: FLOATING_OFFSET, fallbackStrategy: "bestFit" })],
3141
3198
  shift({ padding: FLOATING_OFFSET })
3142
3199
  ];
3143
3200
  const updatePosition = async () => {
@@ -35,6 +35,7 @@ __export(router_exports, {
35
35
  getCurrentRoutePath: () => getCurrentRoutePath,
36
36
  getTanStackRouter: () => getTanStackRouter,
37
37
  getTourRouter: () => getTourRouter,
38
+ matchRoute: () => matchRoute,
38
39
  notifyRouteChange: () => notifyRouteChange,
39
40
  routeGatingChannel: () => routeGatingChannel,
40
41
  setTanStackRouter: () => setTanStackRouter,
@@ -161,6 +162,12 @@ var notifyRouteChange = (path) => {
161
162
  var subscribeToRouteChanges = (listener) => {
162
163
  return routeGatingChannel.subscribe(listener);
163
164
  };
165
+ var matchRoute = (params) => {
166
+ const { pattern, path } = params;
167
+ if (!pattern) return true;
168
+ if (typeof pattern === "string") return path === pattern;
169
+ return pattern.test(path);
170
+ };
164
171
 
165
172
  // src/router/utils.ts
166
173
  var ensurePrefix = (value, prefix) => value.startsWith(prefix) ? value : `${prefix}${value}`;
@@ -268,6 +275,7 @@ var useTanStackRouterTourAdapter = () => {
268
275
  getCurrentRoutePath,
269
276
  getTanStackRouter,
270
277
  getTourRouter,
278
+ matchRoute,
271
279
  notifyRouteChange,
272
280
  routeGatingChannel,
273
281
  setTanStackRouter,
@@ -1,4 +1,4 @@
1
- export { getCurrentRoutePath, notifyRouteChange, routeGatingChannel, subscribeToRouteChanges, } from './routeGating';
1
+ export { getCurrentRoutePath, matchRoute, notifyRouteChange, routeGatingChannel, subscribeToRouteChanges, } from './routeGating';
2
2
  export { createPathString } from './utils';
3
3
  export { useTanStackRouterTourAdapter } from './tanstackRouterAdapter';
4
4
  export { getTanStackRouter, getTourRouter, setTanStackRouter, setTourRouter, TanStackRouterSync, } from './tanstackRouterSync';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/router/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,mBAAmB,EACnB,iBAAiB,EACjB,kBAAkB,EAClB,uBAAuB,GACxB,MAAM,eAAe,CAAA;AAEtB,OAAO,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAA;AAE1C,OAAO,EAAE,4BAA4B,EAAE,MAAM,yBAAyB,CAAA;AAEtE,OAAO,EACL,iBAAiB,EACjB,aAAa,EACb,iBAAiB,EACjB,aAAa,EACb,kBAAkB,GACnB,MAAM,sBAAsB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/router/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,mBAAmB,EACnB,UAAU,EACV,iBAAiB,EACjB,kBAAkB,EAClB,uBAAuB,GACxB,MAAM,eAAe,CAAA;AAEtB,OAAO,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAA;AAE1C,OAAO,EAAE,4BAA4B,EAAE,MAAM,yBAAyB,CAAA;AAEtE,OAAO,EACL,iBAAiB,EACjB,aAAa,EACb,iBAAiB,EACjB,aAAa,EACb,kBAAkB,GACnB,MAAM,sBAAsB,CAAA"}
@@ -5,20 +5,22 @@ import {
5
5
  setTanStackRouter,
6
6
  setTourRouter,
7
7
  useTanStackRouterTourAdapter
8
- } from "../chunk-L6HQUDEA.mjs";
8
+ } from "../chunk-2ZX2Y3JL.mjs";
9
9
  import {
10
10
  createPathString,
11
11
  getCurrentRoutePath,
12
+ matchRoute,
12
13
  notifyRouteChange,
13
14
  routeGatingChannel,
14
15
  subscribeToRouteChanges
15
- } from "../chunk-WTGQUDPT.mjs";
16
+ } from "../chunk-B44EX7YT.mjs";
16
17
  export {
17
18
  TanStackRouterSync,
18
19
  createPathString,
19
20
  getCurrentRoutePath,
20
21
  getTanStackRouter,
21
22
  getTourRouter,
23
+ matchRoute,
22
24
  notifyRouteChange,
23
25
  routeGatingChannel,
24
26
  setTanStackRouter,
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  createPathString,
3
3
  notifyRouteChange
4
- } from "../chunk-WTGQUDPT.mjs";
4
+ } from "../chunk-B44EX7YT.mjs";
5
5
 
6
6
  // src/router/nextAppRouterAdapter.tsx
7
7
  import * as NextNavigation from "next/navigation";
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  createPathString,
3
3
  notifyRouteChange
4
- } from "../chunk-WTGQUDPT.mjs";
4
+ } from "../chunk-B44EX7YT.mjs";
5
5
 
6
6
  // src/router/nextPagesRouterAdapter.tsx
7
7
  import * as NextRouter from "next/router";
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  createPathString,
3
3
  notifyRouteChange
4
- } from "../chunk-WTGQUDPT.mjs";
4
+ } from "../chunk-B44EX7YT.mjs";
5
5
 
6
6
  // src/router/reactRouterAdapter.tsx
7
7
  import { useEffect } from "react";
@@ -9,5 +9,9 @@ export declare const routeGatingChannel: RouteGatingChannel;
9
9
  export declare const getCurrentRoutePath: () => string;
10
10
  export declare const notifyRouteChange: (path?: string) => void;
11
11
  export declare const subscribeToRouteChanges: (listener: RouteChangeListener) => () => void;
12
+ export declare const matchRoute: (params: {
13
+ pattern: string | RegExp | undefined;
14
+ path: string;
15
+ }) => boolean;
12
16
  export {};
13
17
  //# sourceMappingURL=routeGating.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"routeGating.d.ts","sourceRoot":"","sources":["../../src/router/routeGating.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,mBAAmB,GAAG,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;AAsDxD,cAAM,kBAAkB;;IAsCtB,cAAc,IAAI,MAAM;IAOxB,MAAM,CAAC,IAAI,CAAC,EAAE,MAAM;IAmBpB,SAAS,CAAC,QAAQ,EAAE,mBAAmB;CAyBxC;AAED,eAAO,MAAM,kBAAkB,oBAA2B,CAAA;AAE1D,eAAO,MAAM,mBAAmB,cAA4C,CAAA;AAE5E,eAAO,MAAM,iBAAiB,GAAI,OAAO,MAAM,SAE9C,CAAA;AAED,eAAO,MAAM,uBAAuB,GAAI,UAAU,mBAAmB,eAEpE,CAAA"}
1
+ {"version":3,"file":"routeGating.d.ts","sourceRoot":"","sources":["../../src/router/routeGating.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,mBAAmB,GAAG,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;AAsDxD,cAAM,kBAAkB;;IAsCtB,cAAc,IAAI,MAAM;IAOxB,MAAM,CAAC,IAAI,CAAC,EAAE,MAAM;IAmBpB,SAAS,CAAC,QAAQ,EAAE,mBAAmB;CAyBxC;AAED,eAAO,MAAM,kBAAkB,oBAA2B,CAAA;AAE1D,eAAO,MAAM,mBAAmB,cAA4C,CAAA;AAE5E,eAAO,MAAM,iBAAiB,GAAI,OAAO,MAAM,SAE9C,CAAA;AAED,eAAO,MAAM,uBAAuB,GAAI,UAAU,mBAAmB,eAEpE,CAAA;AAED,eAAO,MAAM,UAAU,GAAI,QAAQ;IACjC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAA;IACpC,IAAI,EAAE,MAAM,CAAA;CACb,KAAG,OAKH,CAAA"}
@@ -5,8 +5,8 @@ import {
5
5
  setTanStackRouter,
6
6
  setTourRouter,
7
7
  useTanStackRouterTourAdapter
8
- } from "../chunk-L6HQUDEA.mjs";
9
- import "../chunk-WTGQUDPT.mjs";
8
+ } from "../chunk-2ZX2Y3JL.mjs";
9
+ import "../chunk-B44EX7YT.mjs";
10
10
  export {
11
11
  TanStackRouterSync,
12
12
  getTanStackRouter,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flowsterix/react",
3
- "version": "0.3.1",
3
+ "version": "0.4.1",
4
4
  "description": "React bindings for Flowsterix - guided tours and onboarding flows",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -64,7 +64,7 @@
64
64
  ],
65
65
  "dependencies": {
66
66
  "@floating-ui/dom": "^1.7.4",
67
- "@flowsterix/core": "0.3.0"
67
+ "@flowsterix/core": "0.4.0"
68
68
  },
69
69
  "peerDependencies": {
70
70
  "react": ">=18",