@inertiajs/core 2.2.19 → 2.2.20

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.esm.js CHANGED
@@ -38,7 +38,8 @@ var config = new Config({
38
38
  future: {
39
39
  preserveEqualProps: false,
40
40
  useDataInertiaHeadAttribute: false,
41
- useDialogForErrorModal: false
41
+ useDialogForErrorModal: false,
42
+ useScriptElementForInitialPage: false
42
43
  },
43
44
  prefetch: {
44
45
  cacheFor: 3e4,
@@ -97,7 +98,7 @@ var firePrefetchingEvent = (visit) => {
97
98
  };
98
99
 
99
100
  // src/history.ts
100
- import { cloneDeep, isEqual } from "lodash-es";
101
+ import { cloneDeep as cloneDeep2, isEqual } from "lodash-es";
101
102
 
102
103
  // src/sessionStorage.ts
103
104
  var SessionStorage = class {
@@ -268,6 +269,300 @@ var getKeyFromSessionStorage = async () => {
268
269
  return key;
269
270
  };
270
271
 
272
+ // src/prefetched.ts
273
+ import { cloneDeep } from "lodash-es";
274
+
275
+ // src/objectUtils.ts
276
+ var objectsAreEqual = (obj1, obj2, excludeKeys) => {
277
+ if (obj1 === obj2) {
278
+ return true;
279
+ }
280
+ for (const key in obj1) {
281
+ if (excludeKeys.includes(key)) {
282
+ continue;
283
+ }
284
+ if (obj1[key] === obj2[key]) {
285
+ continue;
286
+ }
287
+ if (!compareValues(obj1[key], obj2[key])) {
288
+ return false;
289
+ }
290
+ }
291
+ for (const key in obj2) {
292
+ if (excludeKeys.includes(key)) {
293
+ continue;
294
+ }
295
+ if (!(key in obj1)) {
296
+ return false;
297
+ }
298
+ }
299
+ return true;
300
+ };
301
+ var compareValues = (value1, value2) => {
302
+ switch (typeof value1) {
303
+ case "object":
304
+ return objectsAreEqual(value1, value2, []);
305
+ case "function":
306
+ return value1.toString() === value2.toString();
307
+ default:
308
+ return value1 === value2;
309
+ }
310
+ };
311
+
312
+ // src/time.ts
313
+ var conversionMap = {
314
+ ms: 1,
315
+ s: 1e3,
316
+ m: 1e3 * 60,
317
+ h: 1e3 * 60 * 60,
318
+ d: 1e3 * 60 * 60 * 24
319
+ };
320
+ var timeToMs = (time) => {
321
+ if (typeof time === "number") {
322
+ return time;
323
+ }
324
+ for (const [unit, conversion] of Object.entries(conversionMap)) {
325
+ if (time.endsWith(unit)) {
326
+ return parseFloat(time) * conversion;
327
+ }
328
+ }
329
+ return parseInt(time);
330
+ };
331
+
332
+ // src/prefetched.ts
333
+ var PrefetchedRequests = class {
334
+ constructor() {
335
+ this.cached = [];
336
+ this.inFlightRequests = [];
337
+ this.removalTimers = [];
338
+ this.currentUseId = null;
339
+ }
340
+ add(params, sendFunc, { cacheFor, cacheTags }) {
341
+ const inFlight = this.findInFlight(params);
342
+ if (inFlight) {
343
+ return Promise.resolve();
344
+ }
345
+ const existing = this.findCached(params);
346
+ if (!params.fresh && existing && existing.staleTimestamp > Date.now()) {
347
+ return Promise.resolve();
348
+ }
349
+ const [stale, prefetchExpiresIn] = this.extractStaleValues(cacheFor);
350
+ const promise = new Promise((resolve, reject) => {
351
+ sendFunc({
352
+ ...params,
353
+ onCancel: () => {
354
+ this.remove(params);
355
+ params.onCancel();
356
+ reject();
357
+ },
358
+ onError: (error) => {
359
+ this.remove(params);
360
+ params.onError(error);
361
+ reject();
362
+ },
363
+ onPrefetching(visitParams) {
364
+ params.onPrefetching(visitParams);
365
+ },
366
+ onPrefetched(response, visit) {
367
+ params.onPrefetched(response, visit);
368
+ },
369
+ onPrefetchResponse(response) {
370
+ resolve(response);
371
+ },
372
+ onPrefetchError(error) {
373
+ prefetchedRequests.removeFromInFlight(params);
374
+ reject(error);
375
+ }
376
+ });
377
+ }).then((response) => {
378
+ this.remove(params);
379
+ const pageResponse = response.getPageResponse();
380
+ page.mergeOncePropsIntoResponse(pageResponse);
381
+ this.cached.push({
382
+ params: { ...params },
383
+ staleTimestamp: Date.now() + stale,
384
+ expiresAt: Date.now() + prefetchExpiresIn,
385
+ response: promise,
386
+ singleUse: prefetchExpiresIn === 0,
387
+ timestamp: Date.now(),
388
+ inFlight: false,
389
+ tags: Array.isArray(cacheTags) ? cacheTags : [cacheTags]
390
+ });
391
+ const oncePropExpiresIn = this.getShortestOncePropTtl(pageResponse);
392
+ this.scheduleForRemoval(
393
+ params,
394
+ oncePropExpiresIn ? Math.min(prefetchExpiresIn, oncePropExpiresIn) : prefetchExpiresIn
395
+ );
396
+ this.removeFromInFlight(params);
397
+ response.handlePrefetch();
398
+ return response;
399
+ });
400
+ this.inFlightRequests.push({
401
+ params: { ...params },
402
+ response: promise,
403
+ staleTimestamp: null,
404
+ inFlight: true
405
+ });
406
+ return promise;
407
+ }
408
+ removeAll() {
409
+ this.cached = [];
410
+ this.removalTimers.forEach((removalTimer) => {
411
+ clearTimeout(removalTimer.timer);
412
+ });
413
+ this.removalTimers = [];
414
+ }
415
+ removeByTags(tags) {
416
+ this.cached = this.cached.filter((prefetched) => {
417
+ return !prefetched.tags.some((tag) => tags.includes(tag));
418
+ });
419
+ }
420
+ remove(params) {
421
+ this.cached = this.cached.filter((prefetched) => {
422
+ return !this.paramsAreEqual(prefetched.params, params);
423
+ });
424
+ this.clearTimer(params);
425
+ }
426
+ removeFromInFlight(params) {
427
+ this.inFlightRequests = this.inFlightRequests.filter((prefetching) => {
428
+ return !this.paramsAreEqual(prefetching.params, params);
429
+ });
430
+ }
431
+ extractStaleValues(cacheFor) {
432
+ const [stale, expires] = this.cacheForToStaleAndExpires(cacheFor);
433
+ return [timeToMs(stale), timeToMs(expires)];
434
+ }
435
+ cacheForToStaleAndExpires(cacheFor) {
436
+ if (!Array.isArray(cacheFor)) {
437
+ return [cacheFor, cacheFor];
438
+ }
439
+ switch (cacheFor.length) {
440
+ case 0:
441
+ return [0, 0];
442
+ case 1:
443
+ return [cacheFor[0], cacheFor[0]];
444
+ default:
445
+ return [cacheFor[0], cacheFor[1]];
446
+ }
447
+ }
448
+ clearTimer(params) {
449
+ const timer = this.removalTimers.find((removalTimer) => {
450
+ return this.paramsAreEqual(removalTimer.params, params);
451
+ });
452
+ if (timer) {
453
+ clearTimeout(timer.timer);
454
+ this.removalTimers = this.removalTimers.filter((removalTimer) => removalTimer !== timer);
455
+ }
456
+ }
457
+ scheduleForRemoval(params, expiresIn) {
458
+ if (typeof window === "undefined") {
459
+ return;
460
+ }
461
+ this.clearTimer(params);
462
+ if (expiresIn > 0) {
463
+ const timer = window.setTimeout(() => this.remove(params), expiresIn);
464
+ this.removalTimers.push({
465
+ params,
466
+ timer
467
+ });
468
+ }
469
+ }
470
+ get(params) {
471
+ return this.findCached(params) || this.findInFlight(params);
472
+ }
473
+ use(prefetched, params) {
474
+ const id = `${params.url.pathname}-${Date.now()}-${Math.random().toString(36).substring(7)}`;
475
+ this.currentUseId = id;
476
+ return prefetched.response.then((response) => {
477
+ if (this.currentUseId !== id) {
478
+ return;
479
+ }
480
+ response.mergeParams({ ...params, onPrefetched: () => {
481
+ } });
482
+ this.removeSingleUseItems(params);
483
+ return response.handle();
484
+ });
485
+ }
486
+ removeSingleUseItems(params) {
487
+ this.cached = this.cached.filter((prefetched) => {
488
+ if (!this.paramsAreEqual(prefetched.params, params)) {
489
+ return true;
490
+ }
491
+ return !prefetched.singleUse;
492
+ });
493
+ }
494
+ findCached(params) {
495
+ return this.cached.find((prefetched) => {
496
+ return this.paramsAreEqual(prefetched.params, params);
497
+ }) || null;
498
+ }
499
+ findInFlight(params) {
500
+ return this.inFlightRequests.find((prefetched) => {
501
+ return this.paramsAreEqual(prefetched.params, params);
502
+ }) || null;
503
+ }
504
+ withoutPurposePrefetchHeader(params) {
505
+ const newParams = cloneDeep(params);
506
+ if (newParams.headers["Purpose"] === "prefetch") {
507
+ delete newParams.headers["Purpose"];
508
+ }
509
+ return newParams;
510
+ }
511
+ paramsAreEqual(params1, params2) {
512
+ return objectsAreEqual(
513
+ this.withoutPurposePrefetchHeader(params1),
514
+ this.withoutPurposePrefetchHeader(params2),
515
+ [
516
+ "showProgress",
517
+ "replace",
518
+ "prefetch",
519
+ "preserveScroll",
520
+ "preserveState",
521
+ "onBefore",
522
+ "onBeforeUpdate",
523
+ "onStart",
524
+ "onProgress",
525
+ "onFinish",
526
+ "onCancel",
527
+ "onSuccess",
528
+ "onError",
529
+ "onPrefetched",
530
+ "onCancelToken",
531
+ "onPrefetching",
532
+ "async",
533
+ "viewTransition"
534
+ ]
535
+ );
536
+ }
537
+ updateCachedOncePropsFromCurrentPage() {
538
+ this.cached.forEach((prefetched) => {
539
+ prefetched.response.then((response) => {
540
+ const pageResponse = response.getPageResponse();
541
+ page.mergeOncePropsIntoResponse(pageResponse, { force: true });
542
+ const oncePropExpiresIn = this.getShortestOncePropTtl(pageResponse);
543
+ if (oncePropExpiresIn === null) {
544
+ return;
545
+ }
546
+ const prefetchExpiresIn = prefetched.expiresAt - Date.now();
547
+ const expiresIn = Math.min(prefetchExpiresIn, oncePropExpiresIn);
548
+ if (expiresIn > 0) {
549
+ this.scheduleForRemoval(prefetched.params, expiresIn);
550
+ } else {
551
+ this.remove(prefetched.params);
552
+ }
553
+ });
554
+ });
555
+ }
556
+ getShortestOncePropTtl(page2) {
557
+ const expiryTimestamps = Object.values(page2.onceProps ?? {}).map((onceProp) => onceProp.expiresAt).filter((expiresAt) => !!expiresAt);
558
+ if (expiryTimestamps.length === 0) {
559
+ return null;
560
+ }
561
+ return Math.min(...expiryTimestamps) - Date.now();
562
+ }
563
+ };
564
+ var prefetchedRequests = new PrefetchedRequests();
565
+
271
566
  // src/scroll.ts
272
567
  var Scroll = class {
273
568
  static save() {
@@ -526,6 +821,9 @@ var CurrentPage = class {
526
821
  }
527
822
  this.page = page2;
528
823
  this.cleared = false;
824
+ if (this.hasOnceProps()) {
825
+ prefetchedRequests.updateCachedOncePropsFromCurrentPage();
826
+ }
529
827
  if (isNewComponent) {
530
828
  this.fireEventsFor("newComponent");
531
829
  }
@@ -574,6 +872,9 @@ var CurrentPage = class {
574
872
  get() {
575
873
  return this.page;
576
874
  }
875
+ hasOnceProps() {
876
+ return Object.keys(this.page.onceProps ?? {}).length > 0;
877
+ }
577
878
  merge(data) {
578
879
  this.page = { ...this.page, ...data };
579
880
  }
@@ -616,6 +917,18 @@ var CurrentPage = class {
616
917
  fireEventsFor(event) {
617
918
  this.listeners.filter((listener) => listener.event === event).forEach((listener) => listener.callback());
618
919
  }
920
+ mergeOncePropsIntoResponse(response, { force = false } = {}) {
921
+ Object.entries(response.onceProps ?? {}).forEach(([key, onceProp]) => {
922
+ const existingOnceProp = this.page.onceProps?.[key];
923
+ if (existingOnceProp === void 0) {
924
+ return;
925
+ }
926
+ if (force || response.props[onceProp.prop] === void 0) {
927
+ response.props[onceProp.prop] = this.page.props[existingOnceProp.prop];
928
+ response.onceProps[key].expiresAt = existingOnceProp.expiresAt;
929
+ }
930
+ });
931
+ }
619
932
  };
620
933
  var page = new CurrentPage();
621
934
 
@@ -699,7 +1012,7 @@ var History = class {
699
1012
  } catch {
700
1013
  return {
701
1014
  ...page2,
702
- props: cloneDeep(page2.props)
1015
+ props: cloneDeep2(page2.props)
703
1016
  };
704
1017
  }
705
1018
  }
@@ -803,549 +1116,288 @@ var History = class {
803
1116
  "",
804
1117
  url
805
1118
  )
806
- );
807
- }
808
- doPushState(data, url) {
809
- return Promise.resolve().then(() => window.history.pushState(data, "", url));
810
- }
811
- getState(key, defaultValue) {
812
- return this.current?.[key] ?? defaultValue;
813
- }
814
- deleteState(key) {
815
- if (this.current[key] !== void 0) {
816
- delete this.current[key];
817
- this.replaceState(this.current);
818
- }
819
- }
820
- clearInitialState(key) {
821
- if (this.initialState && this.initialState[key] !== void 0) {
822
- delete this.initialState[key];
823
- }
824
- }
825
- hasAnyState() {
826
- return !!this.getAllState();
827
- }
828
- clear() {
829
- SessionStorage.remove(historySessionStorageKeys.key);
830
- SessionStorage.remove(historySessionStorageKeys.iv);
831
- }
832
- setCurrent(page2) {
833
- this.current = page2;
834
- }
835
- isValidState(state) {
836
- return !!state.page;
837
- }
838
- getAllState() {
839
- return this.current;
840
- }
841
- };
842
- if (typeof window !== "undefined" && window.history.scrollRestoration) {
843
- window.history.scrollRestoration = "manual";
844
- }
845
- var history = new History();
846
-
847
- // src/eventHandler.ts
848
- var EventHandler = class {
849
- constructor() {
850
- this.internalListeners = [];
851
- }
852
- init() {
853
- if (typeof window !== "undefined") {
854
- window.addEventListener("popstate", this.handlePopstateEvent.bind(this));
855
- window.addEventListener("scroll", debounce(Scroll.onWindowScroll.bind(Scroll), 100), true);
856
- }
857
- if (typeof document !== "undefined") {
858
- document.addEventListener("scroll", debounce(Scroll.onScroll.bind(Scroll), 100), true);
859
- }
860
- }
861
- onGlobalEvent(type, callback) {
862
- const listener = ((event) => {
863
- const response = callback(event);
864
- if (event.cancelable && !event.defaultPrevented && response === false) {
865
- event.preventDefault();
866
- }
867
- });
868
- return this.registerListener(`inertia:${type}`, listener);
869
- }
870
- on(event, callback) {
871
- this.internalListeners.push({ event, listener: callback });
872
- return () => {
873
- this.internalListeners = this.internalListeners.filter((listener) => listener.listener !== callback);
874
- };
875
- }
876
- onMissingHistoryItem() {
877
- page.clear();
878
- this.fireInternalEvent("missingHistoryItem");
879
- }
880
- fireInternalEvent(event, ...args) {
881
- this.internalListeners.filter((listener) => listener.event === event).forEach((listener) => listener.listener(...args));
882
- }
883
- registerListener(type, listener) {
884
- document.addEventListener(type, listener);
885
- return () => document.removeEventListener(type, listener);
886
- }
887
- handlePopstateEvent(event) {
888
- const state = event.state || null;
889
- if (state === null) {
890
- const url = hrefToUrl(page.get().url);
891
- url.hash = window.location.hash;
892
- history.replaceState({ ...page.get(), url: url.href });
893
- Scroll.reset();
894
- return;
895
- }
896
- if (!history.isValidState(state)) {
897
- return this.onMissingHistoryItem();
898
- }
899
- history.decrypt(state.page).then((data) => {
900
- if (page.get().version !== data.version) {
901
- this.onMissingHistoryItem();
902
- return;
903
- }
904
- router.cancelAll();
905
- page.setQuietly(data, { preserveState: false }).then(() => {
906
- Scroll.restore(history.getScrollRegions());
907
- fireNavigateEvent(page.get());
908
- });
909
- }).catch(() => {
910
- this.onMissingHistoryItem();
911
- });
912
- }
913
- };
914
- var eventHandler = new EventHandler();
915
-
916
- // src/navigationType.ts
917
- var NavigationType = class {
918
- constructor() {
919
- this.type = this.resolveType();
920
- }
921
- resolveType() {
922
- if (typeof window === "undefined") {
923
- return "navigate";
924
- }
925
- if (window.performance && window.performance.getEntriesByType && window.performance.getEntriesByType("navigation").length > 0) {
926
- return window.performance.getEntriesByType("navigation")[0].type;
927
- }
928
- return "navigate";
929
- }
930
- get() {
931
- return this.type;
932
- }
933
- isBackForward() {
934
- return this.type === "back_forward";
935
- }
936
- isReload() {
937
- return this.type === "reload";
938
- }
939
- };
940
- var navigationType = new NavigationType();
941
-
942
- // src/initialVisit.ts
943
- var InitialVisit = class {
944
- static handle() {
945
- this.clearRememberedStateOnReload();
946
- const scenarios = [this.handleBackForward, this.handleLocation, this.handleDefault];
947
- scenarios.find((handler) => handler.bind(this)());
948
- }
949
- static clearRememberedStateOnReload() {
950
- if (navigationType.isReload()) {
951
- history.deleteState(history.rememberedState);
952
- history.clearInitialState(history.rememberedState);
953
- }
954
- }
955
- static handleBackForward() {
956
- if (!navigationType.isBackForward() || !history.hasAnyState()) {
957
- return false;
958
- }
959
- const scrollRegions = history.getScrollRegions();
960
- history.decrypt().then((data) => {
961
- page.set(data, { preserveScroll: true, preserveState: true }).then(() => {
962
- Scroll.restore(scrollRegions);
963
- fireNavigateEvent(page.get());
964
- });
965
- }).catch(() => {
966
- eventHandler.onMissingHistoryItem();
967
- });
968
- return true;
969
- }
970
- /**
971
- * @link https://inertiajs.com/redirects#external-redirects
972
- */
973
- static handleLocation() {
974
- if (!SessionStorage.exists(SessionStorage.locationVisitKey)) {
975
- return false;
976
- }
977
- const locationVisit = SessionStorage.get(SessionStorage.locationVisitKey) || {};
978
- SessionStorage.remove(SessionStorage.locationVisitKey);
979
- if (typeof window !== "undefined") {
980
- page.setUrlHash(window.location.hash);
981
- }
982
- history.decrypt(page.get()).then(() => {
983
- const rememberedState = history.getState(history.rememberedState, {});
984
- const scrollRegions = history.getScrollRegions();
985
- page.remember(rememberedState);
986
- page.set(page.get(), {
987
- preserveScroll: locationVisit.preserveScroll,
988
- preserveState: true
989
- }).then(() => {
990
- if (locationVisit.preserveScroll) {
991
- Scroll.restore(scrollRegions);
992
- }
993
- fireNavigateEvent(page.get());
994
- });
995
- }).catch(() => {
996
- eventHandler.onMissingHistoryItem();
997
- });
998
- return true;
999
- }
1000
- static handleDefault() {
1001
- if (typeof window !== "undefined") {
1002
- page.setUrlHash(window.location.hash);
1003
- }
1004
- page.set(page.get(), { preserveScroll: true, preserveState: true }).then(() => {
1005
- if (navigationType.isReload()) {
1006
- Scroll.restore(history.getScrollRegions());
1007
- } else {
1008
- Scroll.scrollToAnchor();
1009
- }
1010
- fireNavigateEvent(page.get());
1011
- });
1012
- }
1013
- };
1014
-
1015
- // src/poll.ts
1016
- var Poll = class {
1017
- constructor(interval, cb, options) {
1018
- this.id = null;
1019
- this.throttle = false;
1020
- this.keepAlive = false;
1021
- this.cbCount = 0;
1022
- this.keepAlive = options.keepAlive ?? false;
1023
- this.cb = cb;
1024
- this.interval = interval;
1025
- if (options.autoStart ?? true) {
1026
- this.start();
1027
- }
1119
+ );
1028
1120
  }
1029
- stop() {
1030
- if (this.id) {
1031
- clearInterval(this.id);
1032
- }
1121
+ doPushState(data, url) {
1122
+ return Promise.resolve().then(() => window.history.pushState(data, "", url));
1033
1123
  }
1034
- start() {
1035
- if (typeof window === "undefined") {
1036
- return;
1037
- }
1038
- this.stop();
1039
- this.id = window.setInterval(() => {
1040
- if (!this.throttle || this.cbCount % 10 === 0) {
1041
- this.cb();
1042
- }
1043
- if (this.throttle) {
1044
- this.cbCount++;
1045
- }
1046
- }, this.interval);
1124
+ getState(key, defaultValue) {
1125
+ return this.current?.[key] ?? defaultValue;
1047
1126
  }
1048
- isInBackground(hidden) {
1049
- this.throttle = this.keepAlive ? false : hidden;
1050
- if (this.throttle) {
1051
- this.cbCount = 0;
1127
+ deleteState(key) {
1128
+ if (this.current[key] !== void 0) {
1129
+ delete this.current[key];
1130
+ this.replaceState(this.current);
1052
1131
  }
1053
1132
  }
1054
- };
1055
-
1056
- // src/polls.ts
1057
- var Polls = class {
1058
- constructor() {
1059
- this.polls = [];
1060
- this.setupVisibilityListener();
1133
+ clearInitialState(key) {
1134
+ if (this.initialState && this.initialState[key] !== void 0) {
1135
+ delete this.initialState[key];
1136
+ }
1061
1137
  }
1062
- add(interval, cb, options) {
1063
- const poll = new Poll(interval, cb, options);
1064
- this.polls.push(poll);
1065
- return {
1066
- stop: () => poll.stop(),
1067
- start: () => poll.start()
1068
- };
1138
+ hasAnyState() {
1139
+ return !!this.getAllState();
1069
1140
  }
1070
1141
  clear() {
1071
- this.polls.forEach((poll) => poll.stop());
1072
- this.polls = [];
1142
+ SessionStorage.remove(historySessionStorageKeys.key);
1143
+ SessionStorage.remove(historySessionStorageKeys.iv);
1073
1144
  }
1074
- setupVisibilityListener() {
1075
- if (typeof document === "undefined") {
1076
- return;
1077
- }
1078
- document.addEventListener(
1079
- "visibilitychange",
1080
- () => {
1081
- this.polls.forEach((poll) => poll.isInBackground(document.hidden));
1082
- },
1083
- false
1084
- );
1145
+ setCurrent(page2) {
1146
+ this.current = page2;
1147
+ }
1148
+ isValidState(state) {
1149
+ return !!state.page;
1150
+ }
1151
+ getAllState() {
1152
+ return this.current;
1085
1153
  }
1086
1154
  };
1087
- var polls = new Polls();
1088
-
1089
- // src/prefetched.ts
1090
- import { cloneDeep as cloneDeep2 } from "lodash-es";
1155
+ if (typeof window !== "undefined" && window.history.scrollRestoration) {
1156
+ window.history.scrollRestoration = "manual";
1157
+ }
1158
+ var history = new History();
1091
1159
 
1092
- // src/objectUtils.ts
1093
- var objectsAreEqual = (obj1, obj2, excludeKeys) => {
1094
- if (obj1 === obj2) {
1095
- return true;
1160
+ // src/eventHandler.ts
1161
+ var EventHandler = class {
1162
+ constructor() {
1163
+ this.internalListeners = [];
1096
1164
  }
1097
- for (const key in obj1) {
1098
- if (excludeKeys.includes(key)) {
1099
- continue;
1100
- }
1101
- if (obj1[key] === obj2[key]) {
1102
- continue;
1165
+ init() {
1166
+ if (typeof window !== "undefined") {
1167
+ window.addEventListener("popstate", this.handlePopstateEvent.bind(this));
1168
+ window.addEventListener("scroll", debounce(Scroll.onWindowScroll.bind(Scroll), 100), true);
1103
1169
  }
1104
- if (!compareValues(obj1[key], obj2[key])) {
1105
- return false;
1170
+ if (typeof document !== "undefined") {
1171
+ document.addEventListener("scroll", debounce(Scroll.onScroll.bind(Scroll), 100), true);
1106
1172
  }
1107
1173
  }
1108
- for (const key in obj2) {
1109
- if (excludeKeys.includes(key)) {
1110
- continue;
1111
- }
1112
- if (!(key in obj1)) {
1113
- return false;
1114
- }
1174
+ onGlobalEvent(type, callback) {
1175
+ const listener = ((event) => {
1176
+ const response = callback(event);
1177
+ if (event.cancelable && !event.defaultPrevented && response === false) {
1178
+ event.preventDefault();
1179
+ }
1180
+ });
1181
+ return this.registerListener(`inertia:${type}`, listener);
1115
1182
  }
1116
- return true;
1117
- };
1118
- var compareValues = (value1, value2) => {
1119
- switch (typeof value1) {
1120
- case "object":
1121
- return objectsAreEqual(value1, value2, []);
1122
- case "function":
1123
- return value1.toString() === value2.toString();
1124
- default:
1125
- return value1 === value2;
1183
+ on(event, callback) {
1184
+ this.internalListeners.push({ event, listener: callback });
1185
+ return () => {
1186
+ this.internalListeners = this.internalListeners.filter((listener) => listener.listener !== callback);
1187
+ };
1126
1188
  }
1127
- };
1128
-
1129
- // src/time.ts
1130
- var conversionMap = {
1131
- ms: 1,
1132
- s: 1e3,
1133
- m: 1e3 * 60,
1134
- h: 1e3 * 60 * 60,
1135
- d: 1e3 * 60 * 60 * 24
1136
- };
1137
- var timeToMs = (time) => {
1138
- if (typeof time === "number") {
1139
- return time;
1189
+ onMissingHistoryItem() {
1190
+ page.clear();
1191
+ this.fireInternalEvent("missingHistoryItem");
1140
1192
  }
1141
- for (const [unit, conversion] of Object.entries(conversionMap)) {
1142
- if (time.endsWith(unit)) {
1143
- return parseFloat(time) * conversion;
1193
+ fireInternalEvent(event, ...args) {
1194
+ this.internalListeners.filter((listener) => listener.event === event).forEach((listener) => listener.listener(...args));
1195
+ }
1196
+ registerListener(type, listener) {
1197
+ document.addEventListener(type, listener);
1198
+ return () => document.removeEventListener(type, listener);
1199
+ }
1200
+ handlePopstateEvent(event) {
1201
+ const state = event.state || null;
1202
+ if (state === null) {
1203
+ const url = hrefToUrl(page.get().url);
1204
+ url.hash = window.location.hash;
1205
+ history.replaceState({ ...page.get(), url: url.href });
1206
+ Scroll.reset();
1207
+ return;
1208
+ }
1209
+ if (!history.isValidState(state)) {
1210
+ return this.onMissingHistoryItem();
1144
1211
  }
1212
+ history.decrypt(state.page).then((data) => {
1213
+ if (page.get().version !== data.version) {
1214
+ this.onMissingHistoryItem();
1215
+ return;
1216
+ }
1217
+ router.cancelAll();
1218
+ page.setQuietly(data, { preserveState: false }).then(() => {
1219
+ Scroll.restore(history.getScrollRegions());
1220
+ fireNavigateEvent(page.get());
1221
+ });
1222
+ }).catch(() => {
1223
+ this.onMissingHistoryItem();
1224
+ });
1145
1225
  }
1146
- return parseInt(time);
1147
1226
  };
1227
+ var eventHandler = new EventHandler();
1148
1228
 
1149
- // src/prefetched.ts
1150
- var PrefetchedRequests = class {
1229
+ // src/navigationType.ts
1230
+ var NavigationType = class {
1151
1231
  constructor() {
1152
- this.cached = [];
1153
- this.inFlightRequests = [];
1154
- this.removalTimers = [];
1155
- this.currentUseId = null;
1232
+ this.type = this.resolveType();
1156
1233
  }
1157
- add(params, sendFunc, { cacheFor, cacheTags }) {
1158
- const inFlight = this.findInFlight(params);
1159
- if (inFlight) {
1160
- return Promise.resolve();
1234
+ resolveType() {
1235
+ if (typeof window === "undefined") {
1236
+ return "navigate";
1161
1237
  }
1162
- const existing = this.findCached(params);
1163
- if (!params.fresh && existing && existing.staleTimestamp > Date.now()) {
1164
- return Promise.resolve();
1238
+ if (window.performance && window.performance.getEntriesByType && window.performance.getEntriesByType("navigation").length > 0) {
1239
+ return window.performance.getEntriesByType("navigation")[0].type;
1165
1240
  }
1166
- const [stale, expires] = this.extractStaleValues(cacheFor);
1167
- const promise = new Promise((resolve, reject) => {
1168
- sendFunc({
1169
- ...params,
1170
- onCancel: () => {
1171
- this.remove(params);
1172
- params.onCancel();
1173
- reject();
1174
- },
1175
- onError: (error) => {
1176
- this.remove(params);
1177
- params.onError(error);
1178
- reject();
1179
- },
1180
- onPrefetching(visitParams) {
1181
- params.onPrefetching(visitParams);
1182
- },
1183
- onPrefetched(response, visit) {
1184
- params.onPrefetched(response, visit);
1185
- },
1186
- onPrefetchResponse(response) {
1187
- resolve(response);
1188
- },
1189
- onPrefetchError(error) {
1190
- prefetchedRequests.removeFromInFlight(params);
1191
- reject(error);
1192
- }
1193
- });
1194
- }).then((response) => {
1195
- this.remove(params);
1196
- this.cached.push({
1197
- params: { ...params },
1198
- staleTimestamp: Date.now() + stale,
1199
- response: promise,
1200
- singleUse: expires === 0,
1201
- timestamp: Date.now(),
1202
- inFlight: false,
1203
- tags: Array.isArray(cacheTags) ? cacheTags : [cacheTags]
1204
- });
1205
- this.scheduleForRemoval(params, expires);
1206
- this.removeFromInFlight(params);
1207
- response.handlePrefetch();
1208
- return response;
1209
- });
1210
- this.inFlightRequests.push({
1211
- params: { ...params },
1212
- response: promise,
1213
- staleTimestamp: null,
1214
- inFlight: true
1215
- });
1216
- return promise;
1241
+ return "navigate";
1242
+ }
1243
+ get() {
1244
+ return this.type;
1217
1245
  }
1218
- removeAll() {
1219
- this.cached = [];
1220
- this.removalTimers.forEach((removalTimer) => {
1221
- clearTimeout(removalTimer.timer);
1222
- });
1223
- this.removalTimers = [];
1246
+ isBackForward() {
1247
+ return this.type === "back_forward";
1224
1248
  }
1225
- removeByTags(tags) {
1226
- this.cached = this.cached.filter((prefetched) => {
1227
- return !prefetched.tags.some((tag) => tags.includes(tag));
1228
- });
1249
+ isReload() {
1250
+ return this.type === "reload";
1229
1251
  }
1230
- remove(params) {
1231
- this.cached = this.cached.filter((prefetched) => {
1232
- return !this.paramsAreEqual(prefetched.params, params);
1233
- });
1234
- this.clearTimer(params);
1252
+ };
1253
+ var navigationType = new NavigationType();
1254
+
1255
+ // src/initialVisit.ts
1256
+ var InitialVisit = class {
1257
+ static handle() {
1258
+ this.clearRememberedStateOnReload();
1259
+ const scenarios = [this.handleBackForward, this.handleLocation, this.handleDefault];
1260
+ scenarios.find((handler) => handler.bind(this)());
1235
1261
  }
1236
- removeFromInFlight(params) {
1237
- this.inFlightRequests = this.inFlightRequests.filter((prefetching) => {
1238
- return !this.paramsAreEqual(prefetching.params, params);
1239
- });
1262
+ static clearRememberedStateOnReload() {
1263
+ if (navigationType.isReload()) {
1264
+ history.deleteState(history.rememberedState);
1265
+ history.clearInitialState(history.rememberedState);
1266
+ }
1240
1267
  }
1241
- extractStaleValues(cacheFor) {
1242
- const [stale, expires] = this.cacheForToStaleAndExpires(cacheFor);
1243
- return [timeToMs(stale), timeToMs(expires)];
1268
+ static handleBackForward() {
1269
+ if (!navigationType.isBackForward() || !history.hasAnyState()) {
1270
+ return false;
1271
+ }
1272
+ const scrollRegions = history.getScrollRegions();
1273
+ history.decrypt().then((data) => {
1274
+ page.set(data, { preserveScroll: true, preserveState: true }).then(() => {
1275
+ Scroll.restore(scrollRegions);
1276
+ fireNavigateEvent(page.get());
1277
+ });
1278
+ }).catch(() => {
1279
+ eventHandler.onMissingHistoryItem();
1280
+ });
1281
+ return true;
1244
1282
  }
1245
- cacheForToStaleAndExpires(cacheFor) {
1246
- if (!Array.isArray(cacheFor)) {
1247
- return [cacheFor, cacheFor];
1283
+ /**
1284
+ * @link https://inertiajs.com/redirects#external-redirects
1285
+ */
1286
+ static handleLocation() {
1287
+ if (!SessionStorage.exists(SessionStorage.locationVisitKey)) {
1288
+ return false;
1248
1289
  }
1249
- switch (cacheFor.length) {
1250
- case 0:
1251
- return [0, 0];
1252
- case 1:
1253
- return [cacheFor[0], cacheFor[0]];
1254
- default:
1255
- return [cacheFor[0], cacheFor[1]];
1290
+ const locationVisit = SessionStorage.get(SessionStorage.locationVisitKey) || {};
1291
+ SessionStorage.remove(SessionStorage.locationVisitKey);
1292
+ if (typeof window !== "undefined") {
1293
+ page.setUrlHash(window.location.hash);
1256
1294
  }
1295
+ history.decrypt(page.get()).then(() => {
1296
+ const rememberedState = history.getState(history.rememberedState, {});
1297
+ const scrollRegions = history.getScrollRegions();
1298
+ page.remember(rememberedState);
1299
+ page.set(page.get(), {
1300
+ preserveScroll: locationVisit.preserveScroll,
1301
+ preserveState: true
1302
+ }).then(() => {
1303
+ if (locationVisit.preserveScroll) {
1304
+ Scroll.restore(scrollRegions);
1305
+ }
1306
+ fireNavigateEvent(page.get());
1307
+ });
1308
+ }).catch(() => {
1309
+ eventHandler.onMissingHistoryItem();
1310
+ });
1311
+ return true;
1257
1312
  }
1258
- clearTimer(params) {
1259
- const timer = this.removalTimers.find((removalTimer) => {
1260
- return this.paramsAreEqual(removalTimer.params, params);
1313
+ static handleDefault() {
1314
+ if (typeof window !== "undefined") {
1315
+ page.setUrlHash(window.location.hash);
1316
+ }
1317
+ page.set(page.get(), { preserveScroll: true, preserveState: true }).then(() => {
1318
+ if (navigationType.isReload()) {
1319
+ Scroll.restore(history.getScrollRegions());
1320
+ } else {
1321
+ Scroll.scrollToAnchor();
1322
+ }
1323
+ fireNavigateEvent(page.get());
1261
1324
  });
1262
- if (timer) {
1263
- clearTimeout(timer.timer);
1264
- this.removalTimers = this.removalTimers.filter((removalTimer) => removalTimer !== timer);
1325
+ }
1326
+ };
1327
+
1328
+ // src/poll.ts
1329
+ var Poll = class {
1330
+ constructor(interval, cb, options) {
1331
+ this.id = null;
1332
+ this.throttle = false;
1333
+ this.keepAlive = false;
1334
+ this.cbCount = 0;
1335
+ this.keepAlive = options.keepAlive ?? false;
1336
+ this.cb = cb;
1337
+ this.interval = interval;
1338
+ if (options.autoStart ?? true) {
1339
+ this.start();
1265
1340
  }
1266
1341
  }
1267
- scheduleForRemoval(params, expiresIn) {
1342
+ stop() {
1343
+ if (this.id) {
1344
+ clearInterval(this.id);
1345
+ }
1346
+ }
1347
+ start() {
1268
1348
  if (typeof window === "undefined") {
1269
1349
  return;
1270
1350
  }
1271
- this.clearTimer(params);
1272
- if (expiresIn > 0) {
1273
- const timer = window.setTimeout(() => this.remove(params), expiresIn);
1274
- this.removalTimers.push({
1275
- params,
1276
- timer
1277
- });
1278
- }
1279
- }
1280
- get(params) {
1281
- return this.findCached(params) || this.findInFlight(params);
1282
- }
1283
- use(prefetched, params) {
1284
- const id = `${params.url.pathname}-${Date.now()}-${Math.random().toString(36).substring(7)}`;
1285
- this.currentUseId = id;
1286
- return prefetched.response.then((response) => {
1287
- if (this.currentUseId !== id) {
1288
- return;
1351
+ this.stop();
1352
+ this.id = window.setInterval(() => {
1353
+ if (!this.throttle || this.cbCount % 10 === 0) {
1354
+ this.cb();
1289
1355
  }
1290
- response.mergeParams({ ...params, onPrefetched: () => {
1291
- } });
1292
- this.removeSingleUseItems(params);
1293
- return response.handle();
1294
- });
1295
- }
1296
- removeSingleUseItems(params) {
1297
- this.cached = this.cached.filter((prefetched) => {
1298
- if (!this.paramsAreEqual(prefetched.params, params)) {
1299
- return true;
1356
+ if (this.throttle) {
1357
+ this.cbCount++;
1300
1358
  }
1301
- return !prefetched.singleUse;
1302
- });
1359
+ }, this.interval);
1303
1360
  }
1304
- findCached(params) {
1305
- return this.cached.find((prefetched) => {
1306
- return this.paramsAreEqual(prefetched.params, params);
1307
- }) || null;
1361
+ isInBackground(hidden) {
1362
+ this.throttle = this.keepAlive ? false : hidden;
1363
+ if (this.throttle) {
1364
+ this.cbCount = 0;
1365
+ }
1308
1366
  }
1309
- findInFlight(params) {
1310
- return this.inFlightRequests.find((prefetched) => {
1311
- return this.paramsAreEqual(prefetched.params, params);
1312
- }) || null;
1367
+ };
1368
+
1369
+ // src/polls.ts
1370
+ var Polls = class {
1371
+ constructor() {
1372
+ this.polls = [];
1373
+ this.setupVisibilityListener();
1313
1374
  }
1314
- withoutPurposePrefetchHeader(params) {
1315
- const newParams = cloneDeep2(params);
1316
- if (newParams.headers["Purpose"] === "prefetch") {
1317
- delete newParams.headers["Purpose"];
1318
- }
1319
- return newParams;
1375
+ add(interval, cb, options) {
1376
+ const poll = new Poll(interval, cb, options);
1377
+ this.polls.push(poll);
1378
+ return {
1379
+ stop: () => poll.stop(),
1380
+ start: () => poll.start()
1381
+ };
1320
1382
  }
1321
- paramsAreEqual(params1, params2) {
1322
- return objectsAreEqual(
1323
- this.withoutPurposePrefetchHeader(params1),
1324
- this.withoutPurposePrefetchHeader(params2),
1325
- [
1326
- "showProgress",
1327
- "replace",
1328
- "prefetch",
1329
- "preserveScroll",
1330
- "preserveState",
1331
- "onBefore",
1332
- "onBeforeUpdate",
1333
- "onStart",
1334
- "onProgress",
1335
- "onFinish",
1336
- "onCancel",
1337
- "onSuccess",
1338
- "onError",
1339
- "onPrefetched",
1340
- "onCancelToken",
1341
- "onPrefetching",
1342
- "async",
1343
- "viewTransition"
1344
- ]
1383
+ clear() {
1384
+ this.polls.forEach((poll) => poll.stop());
1385
+ this.polls = [];
1386
+ }
1387
+ setupVisibilityListener() {
1388
+ if (typeof document === "undefined") {
1389
+ return;
1390
+ }
1391
+ document.addEventListener(
1392
+ "visibilitychange",
1393
+ () => {
1394
+ this.polls.forEach((poll) => poll.isInBackground(document.hidden));
1395
+ },
1396
+ false
1345
1397
  );
1346
1398
  }
1347
1399
  };
1348
- var prefetchedRequests = new PrefetchedRequests();
1400
+ var polls = new Polls();
1349
1401
 
1350
1402
  // src/request.ts
1351
1403
  import { default as axios } from "axios";
@@ -1392,6 +1444,9 @@ var RequestParams = class _RequestParams {
1392
1444
  isPartial() {
1393
1445
  return this.params.only.length > 0 || this.params.except.length > 0 || this.params.reset.length > 0;
1394
1446
  }
1447
+ isDeferredPropsRequest() {
1448
+ return this.params.deferredProps === true;
1449
+ }
1395
1450
  onCancelToken(cb) {
1396
1451
  this.params.onCancelToken({
1397
1452
  cancel: cb
@@ -1650,6 +1705,9 @@ var Response = class _Response {
1650
1705
  mergeParams(params) {
1651
1706
  this.requestParams.merge(params);
1652
1707
  }
1708
+ getPageResponse() {
1709
+ return this.response.data = this.getDataFromResponse(this.response.data);
1710
+ }
1653
1711
  async handleNonInertiaResponse() {
1654
1712
  if (this.isLocationVisit()) {
1655
1713
  const locationUrl = hrefToUrl(this.getHeader("x-inertia-location"));
@@ -1700,11 +1758,12 @@ var Response = class _Response {
1700
1758
  }
1701
1759
  }
1702
1760
  async setPage() {
1703
- const pageResponse = this.getDataFromResponse(this.response.data);
1761
+ const pageResponse = this.getPageResponse();
1704
1762
  if (!this.shouldSetPage(pageResponse)) {
1705
1763
  return Promise.resolve();
1706
1764
  }
1707
1765
  this.mergeProps(pageResponse);
1766
+ page.mergeOncePropsIntoResponse(pageResponse);
1708
1767
  this.preserveEqualProps(pageResponse);
1709
1768
  await this.setRememberedState(pageResponse);
1710
1769
  this.requestParams.setPreserveOptions(pageResponse);
@@ -1809,6 +1868,12 @@ var Response = class _Response {
1809
1868
  pageResponse.props[prop] = deepMerge(currentProp, incomingProp, prop);
1810
1869
  });
1811
1870
  pageResponse.props = { ...page.get().props, ...pageResponse.props };
1871
+ if (this.requestParams.isDeferredPropsRequest()) {
1872
+ const currentErrors = page.get().props.errors;
1873
+ if (currentErrors && Object.keys(currentErrors).length > 0) {
1874
+ pageResponse.props.errors = currentErrors;
1875
+ }
1876
+ }
1812
1877
  if (page.get().scrollProps) {
1813
1878
  pageResponse.scrollProps = {
1814
1879
  ...page.get().scrollProps || {},
@@ -1971,8 +2036,13 @@ var Request = class _Request {
1971
2036
  "X-Requested-With": "XMLHttpRequest",
1972
2037
  "X-Inertia": true
1973
2038
  };
1974
- if (page.get().version) {
1975
- headers["X-Inertia-Version"] = page.get().version;
2039
+ const page2 = page.get();
2040
+ if (page2.version) {
2041
+ headers["X-Inertia-Version"] = page2.version;
2042
+ }
2043
+ const onceProps = Object.entries(page2.onceProps || {}).filter(([, onceProp]) => !onceProp.expiresAt || onceProp.expiresAt > Date.now()).map(([key]) => key);
2044
+ if (onceProps.length > 0) {
2045
+ headers["X-Inertia-Except-Once-Props"] = onceProps.join(",");
1976
2046
  }
1977
2047
  return headers;
1978
2048
  }
@@ -2023,6 +2093,7 @@ var Router = class {
2023
2093
  maxConcurrent: Infinity,
2024
2094
  interruptible: false
2025
2095
  });
2096
+ this.clientVisitQueue = new Queue();
2026
2097
  }
2027
2098
  init({
2028
2099
  initialPage,
@@ -2061,6 +2132,9 @@ var Router = class {
2061
2132
  return this.visit(url, { preserveState: true, ...options, method: "delete" });
2062
2133
  }
2063
2134
  reload(options = {}) {
2135
+ return this.doReload(options);
2136
+ }
2137
+ doReload(options = {}) {
2064
2138
  if (typeof window === "undefined") {
2065
2139
  return;
2066
2140
  }
@@ -2249,6 +2323,9 @@ var Router = class {
2249
2323
  this.clientVisit(params);
2250
2324
  }
2251
2325
  clientVisit(params, { replace = false } = {}) {
2326
+ this.clientVisitQueue.add(() => this.performClientVisit(params, { replace }));
2327
+ }
2328
+ performClientVisit(params, { replace = false } = {}) {
2252
2329
  const current = page.get();
2253
2330
  const props = typeof params.props === "function" ? params.props(current.props) : params.props ?? current.props;
2254
2331
  const { viewTransition, onError, onFinish, onSuccess, ...pageParams } = params;
@@ -2259,7 +2336,7 @@ var Router = class {
2259
2336
  };
2260
2337
  const preserveScroll = RequestParams.resolvePreserveOption(params.preserveScroll ?? false, page2);
2261
2338
  const preserveState = RequestParams.resolvePreserveOption(params.preserveState ?? false, page2);
2262
- page.set(page2, {
2339
+ return page.set(page2, {
2263
2340
  replace,
2264
2341
  preserveScroll,
2265
2342
  preserveState,
@@ -2267,10 +2344,11 @@ var Router = class {
2267
2344
  }).then(() => {
2268
2345
  const errors = page.get().props.errors || {};
2269
2346
  if (Object.keys(errors).length === 0) {
2270
- return onSuccess?.(page.get());
2347
+ onSuccess?.(page.get());
2348
+ return;
2271
2349
  }
2272
2350
  const scopedErrors = params.errorBag ? errors[params.errorBag || ""] || {} : errors;
2273
- return onError?.(scopedErrors);
2351
+ onError?.(scopedErrors);
2274
2352
  }).finally(() => onFinish?.(params));
2275
2353
  }
2276
2354
  getPrefetchParams(href, options) {
@@ -2366,7 +2444,7 @@ var Router = class {
2366
2444
  loadDeferredProps(deferred) {
2367
2445
  if (deferred) {
2368
2446
  Object.entries(deferred).forEach(([_, group]) => {
2369
- this.reload({ only: group });
2447
+ this.doReload({ only: group, deferredProps: true });
2370
2448
  });
2371
2449
  }
2372
2450
  }
@@ -2462,6 +2540,22 @@ var requestAnimationFrame = (cb, times = 1) => {
2462
2540
  }
2463
2541
  });
2464
2542
  };
2543
+ var getInitialPageFromDOM = (id, useScriptElement = false) => {
2544
+ if (typeof window === "undefined") {
2545
+ return null;
2546
+ }
2547
+ if (!useScriptElement) {
2548
+ const el = document.getElementById(id);
2549
+ if (el?.dataset.page) {
2550
+ return JSON.parse(el.dataset.page);
2551
+ }
2552
+ }
2553
+ const scriptEl = document.querySelector(`script[data-page="${id}"][type="application/json"]`);
2554
+ if (scriptEl?.textContent) {
2555
+ return JSON.parse(scriptEl.textContent);
2556
+ }
2557
+ return null;
2558
+ };
2465
2559
 
2466
2560
  // src/formObject.ts
2467
2561
  import { get as get4, set as set4 } from "lodash-es";
@@ -3689,6 +3783,7 @@ export {
3689
3783
  config,
3690
3784
  createHeadManager,
3691
3785
  formDataToObject,
3786
+ getInitialPageFromDOM,
3692
3787
  getScrollableParent,
3693
3788
  hide2 as hideProgress,
3694
3789
  hrefToUrl,