@thoughtbot/superglue 1.0.2 → 2.0.0-alpha.2

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.
@@ -100,6 +100,9 @@ function getIn(node, path) {
100
100
  return void 0;
101
101
  }
102
102
  }
103
+ function clone(node) {
104
+ return Array.isArray(node) ? [].slice.call(node) : { ...node };
105
+ }
103
106
  function getKey(node, key) {
104
107
  if (Array.isArray(node) && Number.isNaN(Number(key))) {
105
108
  const key_parts = Array.from(key.split("="));
@@ -148,6 +151,34 @@ function normalizeKeyPath(path) {
148
151
  return path;
149
152
  }
150
153
  }
154
+ function setIn(object, path, value) {
155
+ const keypath = normalizeKeyPath(path);
156
+ const results = { 0: object };
157
+ const parents = { 0: object };
158
+ let i;
159
+ for (i = 0; i < keypath.length; i++) {
160
+ const parent = parents[i];
161
+ if (!(typeof parent === "object" && parent !== null)) {
162
+ throw new KeyPathError(
163
+ `Expected to traverse an Array or Obj, got ${JSON.stringify(parent)}`
164
+ );
165
+ }
166
+ const child = atKey(parent, keypath[i]);
167
+ parents[i + 1] = child;
168
+ }
169
+ results[keypath.length] = value;
170
+ for (i = keypath.length - 1; i >= 0; i--) {
171
+ const target = clone(parents[i]);
172
+ results[i] = target;
173
+ const key = getKey(results[i], keypath[i]);
174
+ if (Array.isArray(target)) {
175
+ target[key] = results[i + 1];
176
+ } else {
177
+ target[key] = results[i + 1];
178
+ }
179
+ }
180
+ return results[0];
181
+ }
151
182
 
152
183
  // lib/config.ts
153
184
  var config = {
@@ -155,7 +186,26 @@ var config = {
155
186
  maxPages: 20
156
187
  };
157
188
 
189
+ // lib/utils/limited_set.ts
190
+ var LimitedSet = class extends Set {
191
+ constructor(maxSize) {
192
+ super();
193
+ this.maxSize = maxSize;
194
+ }
195
+ add(value) {
196
+ if (this.size >= this.maxSize) {
197
+ const iterator = this.values();
198
+ const oldestValue = iterator.next().value;
199
+ this.delete(oldestValue);
200
+ }
201
+ super.add(value);
202
+ return this;
203
+ }
204
+ };
205
+
158
206
  // lib/utils/request.ts
207
+ var import_uuid = require("uuid");
208
+ var lastRequestIds = new LimitedSet(20);
159
209
  function isValidResponse(xhr) {
160
210
  return isValidContent(xhr) && !downloadingFile(xhr);
161
211
  }
@@ -211,6 +261,9 @@ function argsForFetch(getState, pathQuery2, {
211
261
  nextHeaders["x-requested-with"] = "XMLHttpRequest";
212
262
  nextHeaders["accept"] = "application/json";
213
263
  nextHeaders["x-superglue-request"] = "true";
264
+ const requestId = (0, import_uuid.v4)();
265
+ lastRequestIds.add(requestId);
266
+ nextHeaders["X-Superglue-Request-Id"] = requestId;
214
267
  if (method != "GET" && method != "HEAD") {
215
268
  nextHeaders["content-type"] = "application/json";
216
269
  }
@@ -302,7 +355,6 @@ var handleGraft = (0, import_toolkit.createAction)(
302
355
  var superglueError = (0, import_toolkit.createAction)(
303
356
  "@@superglue/ERROR"
304
357
  );
305
- var updateFragments = (0, import_toolkit.createAction)("@@superglue/UPDATE_FRAGMENTS");
306
358
  var copyPage = (0, import_toolkit.createAction)(
307
359
  "@@superglue/COPY_PAGE"
308
360
  );
@@ -317,6 +369,65 @@ var beforeRemote = (0, import_toolkit.createAction)("@@superglue/BEFORE_REMOTE")
317
369
  var setCSRFToken = (0, import_toolkit.createAction)("@@superglue/SET_CSRF_TOKEN");
318
370
  var historyChange = (0, import_toolkit.createAction)("@@superglue/HISTORY_CHANGE");
319
371
  var setActivePage = (0, import_toolkit.createAction)("@@superglue/SET_ACTIVE_PAGE");
372
+ var handleFragmentGraft = (0, import_toolkit.createAction)(
373
+ "@@superglue/HANDLE_FRAGMENT_GRAFT",
374
+ ({
375
+ fragmentId,
376
+ response
377
+ }) => {
378
+ return {
379
+ payload: {
380
+ response,
381
+ fragmentId
382
+ }
383
+ };
384
+ }
385
+ );
386
+ var saveFragment = (0, import_toolkit.createAction)(
387
+ "@@superglue/SAVE_FRAGMENT",
388
+ ({ fragmentId, data }) => {
389
+ return {
390
+ payload: {
391
+ fragmentId,
392
+ data
393
+ }
394
+ };
395
+ }
396
+ );
397
+ var receiveResponse = (0, import_toolkit.createAction)(
398
+ "@@superglue/RECEIVE_RESPONSE",
399
+ ({ pageKey, response }) => {
400
+ pageKey = urlToPageKey(pageKey);
401
+ return {
402
+ payload: {
403
+ pageKey,
404
+ response
405
+ }
406
+ };
407
+ }
408
+ );
409
+ var appendToFragment = (0, import_toolkit.createAction)(
410
+ "@@superglue/APPEND_TO_FRAGMENT",
411
+ ({ data, fragmentId }) => {
412
+ return {
413
+ payload: {
414
+ data,
415
+ fragmentId
416
+ }
417
+ };
418
+ }
419
+ );
420
+ var prependToFragment = (0, import_toolkit.createAction)(
421
+ "@@superglue/PREPEND_TO_FRAGMENT",
422
+ ({ data, fragmentId }) => {
423
+ return {
424
+ payload: {
425
+ data,
426
+ fragmentId
427
+ }
428
+ };
429
+ }
430
+ );
320
431
 
321
432
  // lib/action_creators/requests.ts
322
433
  function handleFetchErr(err, fetchArgs, dispatch) {
@@ -326,15 +437,18 @@ function handleFetchErr(err, fetchArgs, dispatch) {
326
437
  function buildMeta(pageKey, page, state, rsp, fetchArgs) {
327
438
  const { assets: prevAssets } = state;
328
439
  const { assets: nextAssets } = page;
329
- return {
440
+ const meta = {
330
441
  pageKey,
331
442
  page,
332
443
  redirected: rsp.redirected,
333
444
  rsp,
334
445
  fetchArgs,
335
- componentIdentifier: page.componentIdentifier,
336
446
  needsRefresh: needsRefresh(prevAssets, nextAssets)
337
447
  };
448
+ if (page.action !== "handleStreamResponse") {
449
+ meta.componentIdentifier = page.componentIdentifier;
450
+ }
451
+ return meta;
338
452
  }
339
453
  var MismatchedComponentError = class extends Error {
340
454
  constructor(message) {
@@ -342,10 +456,11 @@ var MismatchedComponentError = class extends Error {
342
456
  this.name = "MismatchedComponentError";
343
457
  }
344
458
  };
459
+ var defaultBeforeSave = (prevPage, receivedPage) => receivedPage;
345
460
  var remote = (path, {
346
461
  pageKey: targetPageKey,
347
462
  force = false,
348
- beforeSave = (prevPage, receivedPage) => receivedPage,
463
+ beforeSave = defaultBeforeSave,
349
464
  ...rest
350
465
  } = {}) => {
351
466
  targetPageKey = targetPageKey && urlToPageKey(targetPageKey);
@@ -364,10 +479,11 @@ var remote = (path, {
364
479
  pageKey = targetPageKey;
365
480
  }
366
481
  const meta = buildMeta(pageKey, json, superglue, rsp, fetchArgs);
367
- const existingId = pages[pageKey]?.componentIdentifier;
368
- const receivedId = json.componentIdentifier;
369
- if (!!existingId && existingId != receivedId && !force) {
370
- const message = `You cannot replace or update an existing page
482
+ if (json.action !== "handleStreamResponse") {
483
+ const existingId = pages[pageKey]?.componentIdentifier;
484
+ const receivedId = json.componentIdentifier;
485
+ if (!!existingId && existingId != receivedId && !force) {
486
+ const message = `You cannot replace or update an existing page
371
487
  located at pages["${currentPageKey}"] that has a componentIdentifier
372
488
  of "${existingId}" with the contents of a page response that has a
373
489
  componentIdentifier of "${receivedId}".
@@ -383,8 +499,10 @@ compatible with the page component associated with "${existingId}".
383
499
  Consider using data-sg-visit, the visit function, or redirect_back to
384
500
  the same page. Or if you're sure you want to proceed, use force: true.
385
501
  `;
386
- throw new MismatchedComponentError(message);
502
+ throw new MismatchedComponentError(message);
503
+ }
387
504
  }
505
+ dispatch(receiveResponse({ pageKey, response: json }));
388
506
  const page = beforeSave(pages[pageKey], json);
389
507
  return dispatch(saveAndProcessPage(pageKey, page)).then(() => meta);
390
508
  }).catch((e) => handleFetchErr(e, fetchArgs, dispatch));
@@ -427,11 +545,13 @@ var visit = (path, {
427
545
  const { superglue, pages = {} } = getState();
428
546
  const isGet = fetchArgs[1].method === "GET";
429
547
  const pageKey = calculatePageKey(rsp, isGet, currentPageKey);
430
- if (placeholderKey && hasPropsAt(path) && hasPlaceholder) {
431
- const existingId = pages[placeholderKey]?.componentIdentifier;
432
- const receivedId = json.componentIdentifier;
433
- if (!!existingId && existingId != receivedId) {
434
- const message = `You received a page response with a
548
+ const meta = buildMeta(pageKey, json, superglue, rsp, fetchArgs);
549
+ if (json.action !== "handleStreamResponse") {
550
+ if (placeholderKey && hasPropsAt(path) && hasPlaceholder) {
551
+ const existingId = pages[placeholderKey]?.componentIdentifier;
552
+ const receivedId = json.componentIdentifier;
553
+ if (!!existingId && existingId != receivedId) {
554
+ const message = `You received a page response with a
435
555
  componentIdentifier "${receivedId}" that is different than the
436
556
  componentIdentifier "${existingId}" located at ${placeholderKey}.
437
557
 
@@ -447,29 +567,34 @@ Check that you're rendering a page with a matching
447
567
  componentIdentifier, or consider using redirect_back_with_props_at
448
568
  to the same page.
449
569
  `;
450
- throw new MismatchedComponentError(message);
570
+ throw new MismatchedComponentError(message);
571
+ }
572
+ dispatch(copyPage({ from: placeholderKey, to: pageKey }));
451
573
  }
452
- dispatch(copyPage({ from: placeholderKey, to: pageKey }));
453
574
  }
454
- const meta = buildMeta(pageKey, json, superglue, rsp, fetchArgs);
455
575
  const visitMeta = {
456
576
  ...meta,
457
577
  navigationAction: calculateNavAction(
458
578
  meta,
459
579
  rsp,
580
+ json,
460
581
  isGet,
461
582
  pageKey,
462
583
  currentPageKey,
463
584
  revisit
464
585
  )
465
586
  };
587
+ dispatch(receiveResponse({ pageKey, response: json }));
466
588
  const page = beforeSave(pages[pageKey], json);
467
589
  return dispatch(saveAndProcessPage(pageKey, page)).then(() => visitMeta);
468
590
  }).catch((e) => handleFetchErr(e, fetchArgs, dispatch));
469
591
  };
470
592
  };
471
- function calculateNavAction(meta, rsp, isGet, pageKey, currentPageKey, revisit) {
593
+ function calculateNavAction(meta, rsp, json, isGet, pageKey, currentPageKey, revisit) {
472
594
  let navigationAction = "push";
595
+ if (json.action === "handleStreamResponse") {
596
+ return "none";
597
+ }
473
598
  if (!rsp.redirected && !isGet) {
474
599
  navigationAction = "replace";
475
600
  }
@@ -498,6 +623,311 @@ function calculatePageKey(rsp, isGet, currentPageKey) {
498
623
  return pageKey;
499
624
  }
500
625
 
626
+ // lib/action_creators/stream.ts
627
+ var streamPrepend = (fragments, data, options = {}) => {
628
+ return (dispatch) => {
629
+ if (options.saveAs) {
630
+ const { saveAs } = options;
631
+ dispatch(
632
+ saveFragment({
633
+ fragmentId: saveAs,
634
+ data
635
+ })
636
+ );
637
+ fragments.forEach((fragmentId) => {
638
+ dispatch(
639
+ prependToFragment({
640
+ fragmentId,
641
+ data: {
642
+ __id: saveAs
643
+ }
644
+ })
645
+ );
646
+ });
647
+ } else {
648
+ fragments.forEach((fragmentId) => {
649
+ dispatch(
650
+ prependToFragment({
651
+ fragmentId,
652
+ data
653
+ })
654
+ );
655
+ });
656
+ }
657
+ };
658
+ };
659
+ var streamAppend = (fragments, data, options = {}) => {
660
+ return (dispatch) => {
661
+ if (options.saveAs) {
662
+ const { saveAs } = options;
663
+ dispatch(
664
+ saveFragment({
665
+ fragmentId: saveAs,
666
+ data
667
+ })
668
+ );
669
+ fragments.forEach((fragmentId) => {
670
+ dispatch(
671
+ appendToFragment({
672
+ fragmentId,
673
+ data: {
674
+ __id: saveAs
675
+ }
676
+ })
677
+ );
678
+ });
679
+ } else {
680
+ fragments.forEach((fragmentId) => {
681
+ dispatch(
682
+ appendToFragment({
683
+ fragmentId,
684
+ data
685
+ })
686
+ );
687
+ });
688
+ }
689
+ };
690
+ };
691
+ var streamSave = (fragment, data) => {
692
+ return (dispatch) => {
693
+ dispatch(
694
+ saveFragment({
695
+ fragmentId: fragment,
696
+ data
697
+ })
698
+ );
699
+ };
700
+ };
701
+ var handleStreamResponse = (response) => {
702
+ return (dispatch) => {
703
+ let nextResponse = response;
704
+ nextResponse.fragments.reverse().forEach((fragment) => {
705
+ const { id, path } = fragment;
706
+ const node = getIn(nextResponse, path);
707
+ nextResponse = setIn(nextResponse, path, { __id: id });
708
+ dispatch(
709
+ saveFragment({
710
+ fragmentId: id,
711
+ data: node
712
+ })
713
+ );
714
+ });
715
+ nextResponse.data.forEach((message) => {
716
+ if (message.handler === "append") {
717
+ dispatch(
718
+ streamAppend(message.fragmentIds, message.data, message.options)
719
+ );
720
+ }
721
+ if (message.handler === "prepend") {
722
+ dispatch(
723
+ streamPrepend(message.fragmentIds, message.data, message.options)
724
+ );
725
+ }
726
+ if (message.handler === "save") {
727
+ dispatch(streamSave(message.fragmentIds[0], message.data));
728
+ }
729
+ });
730
+ };
731
+ };
732
+
733
+ // lib/utils/proxy.ts
734
+ var ORIGINAL_TARGET = Symbol("@@originalTarget");
735
+ var ARRAY_GETTER_METHODS = /* @__PURE__ */ new Set([
736
+ Symbol.iterator,
737
+ "at",
738
+ "concat",
739
+ "entries",
740
+ "every",
741
+ "filter",
742
+ "find",
743
+ "findIndex",
744
+ "flat",
745
+ "flatMap",
746
+ "forEach",
747
+ "includes",
748
+ "indexOf",
749
+ "join",
750
+ "keys",
751
+ "lastIndexOf",
752
+ "map",
753
+ "reduce",
754
+ "reduceRight",
755
+ "slice",
756
+ "some",
757
+ "toString",
758
+ "values"
759
+ ]);
760
+ var ARRAY_SETTER_METHODS = /* @__PURE__ */ new Set([
761
+ "copyWithin",
762
+ "fill",
763
+ "pop",
764
+ "push",
765
+ "reverse",
766
+ "shift",
767
+ "sort",
768
+ "splice",
769
+ "unshift"
770
+ ]);
771
+ function isArraySetter(prop) {
772
+ return ARRAY_SETTER_METHODS.has(prop);
773
+ }
774
+ function isArrayGetter(prop) {
775
+ return ARRAY_GETTER_METHODS.has(prop);
776
+ }
777
+ function convertToInt(prop) {
778
+ if (typeof prop === "symbol") return null;
779
+ const num = Number(prop);
780
+ return Number.isInteger(num) ? num : null;
781
+ }
782
+ function isFragmentReference(value) {
783
+ return !!value && typeof value === "object" && "__id" in value && typeof value.__id === "string";
784
+ }
785
+ function createArrayProxy(arrayData, fragments, dependencies, proxyCache) {
786
+ if (proxyCache && proxyCache.has(arrayData)) {
787
+ return proxyCache.get(arrayData);
788
+ }
789
+ const proxy = new Proxy(arrayData, {
790
+ get(target, prop) {
791
+ if (prop === ORIGINAL_TARGET) {
792
+ return target;
793
+ }
794
+ if (isArrayGetter(prop)) {
795
+ const method = target[prop];
796
+ if (typeof method === "function") {
797
+ return function(...args) {
798
+ return Reflect.apply(method, proxy, args);
799
+ };
800
+ }
801
+ return method;
802
+ }
803
+ if (isArraySetter(prop)) {
804
+ throw new Error(
805
+ `Cannot mutate proxy array. Use Redux actions to update state.`
806
+ );
807
+ }
808
+ const index = convertToInt(prop);
809
+ if (index !== null && index >= 0 && index < target.length) {
810
+ const item = target[index];
811
+ if (isFragmentReference(item)) {
812
+ dependencies.add(item.__id);
813
+ const fragmentData = fragments.current[item.__id];
814
+ if (!fragmentData) {
815
+ throw new Error(`Fragment with id "${item.__id}" not found`);
816
+ }
817
+ return createProxy(fragmentData, fragments, dependencies, proxyCache);
818
+ }
819
+ if (typeof item === "object" && item !== null) {
820
+ if ("$$typeof" in item) {
821
+ return item;
822
+ } else {
823
+ return createProxy(
824
+ item,
825
+ fragments,
826
+ dependencies,
827
+ proxyCache
828
+ );
829
+ }
830
+ }
831
+ return item;
832
+ }
833
+ return Reflect.get(target, prop);
834
+ },
835
+ has(target, prop) {
836
+ if (prop === ORIGINAL_TARGET) {
837
+ return true;
838
+ }
839
+ return Reflect.has(target, prop);
840
+ },
841
+ set() {
842
+ throw new Error(
843
+ "Cannot mutate proxy array. Use Redux actions to update state."
844
+ );
845
+ },
846
+ deleteProperty() {
847
+ throw new Error(
848
+ "Cannot delete properties on proxy array. Use Redux actions to update state."
849
+ );
850
+ },
851
+ defineProperty() {
852
+ throw new Error(
853
+ "Cannot define properties on proxy array. Use Redux actions to update state."
854
+ );
855
+ }
856
+ });
857
+ if (proxyCache) {
858
+ proxyCache.set(arrayData, proxy);
859
+ }
860
+ return proxy;
861
+ }
862
+ function createObjectProxy(objectData, fragments, dependencies, proxyCache) {
863
+ if (proxyCache && proxyCache.has(objectData)) {
864
+ return proxyCache.get(objectData);
865
+ }
866
+ const proxy = new Proxy(objectData, {
867
+ get(target, prop) {
868
+ if (prop === ORIGINAL_TARGET) {
869
+ return target;
870
+ }
871
+ const value = target[prop];
872
+ if (isFragmentReference(value)) {
873
+ dependencies.add(value.__id);
874
+ const fragmentData = fragments.current[value.__id];
875
+ if (!fragmentData) {
876
+ throw new Error(`Fragment with id "${value.__id}" not found`);
877
+ }
878
+ return createProxy(fragmentData, fragments, dependencies, proxyCache);
879
+ }
880
+ if (typeof value === "object" && value !== null) {
881
+ if ("$$typeof" in value) {
882
+ return value;
883
+ } else if (Array.isArray(value)) {
884
+ return createArrayProxy(value, fragments, dependencies, proxyCache);
885
+ } else {
886
+ return createObjectProxy(value, fragments, dependencies, proxyCache);
887
+ }
888
+ }
889
+ return value;
890
+ },
891
+ has(target, prop) {
892
+ if (prop === ORIGINAL_TARGET) {
893
+ return true;
894
+ }
895
+ return Reflect.has(target, prop);
896
+ },
897
+ set() {
898
+ throw new Error(
899
+ "Cannot mutate proxy object. Use Redux actions to update state."
900
+ );
901
+ },
902
+ deleteProperty() {
903
+ throw new Error(
904
+ "Cannot delete properties on proxy object. Use Redux actions to update state."
905
+ );
906
+ },
907
+ defineProperty() {
908
+ throw new Error(
909
+ "Cannot define properties on proxy object. Use Redux actions to update state."
910
+ );
911
+ }
912
+ });
913
+ if (proxyCache) {
914
+ proxyCache.set(objectData, proxy);
915
+ }
916
+ return proxy;
917
+ }
918
+ function createProxy(content, fragments, dependencies, proxyCache) {
919
+ if (!content || typeof content !== "object") {
920
+ return content;
921
+ }
922
+ if ("$$typeof" in content) {
923
+ return content;
924
+ }
925
+ if (Array.isArray(content)) {
926
+ return createArrayProxy(content, fragments, dependencies, proxyCache);
927
+ }
928
+ return createObjectProxy(content, fragments, dependencies, proxyCache);
929
+ }
930
+
501
931
  // lib/action_creators/index.ts
502
932
  function fetchDeferments(pageKey, defers = []) {
503
933
  pageKey = urlToPageKey(pageKey);
@@ -531,58 +961,64 @@ function fetchDeferments(pageKey, defers = []) {
531
961
  return Promise.all(fetches);
532
962
  };
533
963
  }
964
+ function addPlaceholdersToDeferredNodes(existingPage, page) {
965
+ const { defers = [] } = existingPage;
966
+ const prevDefers = defers.map(({ path }) => {
967
+ const node = getIn(existingPage, path);
968
+ const copy = JSON.stringify(node);
969
+ return [path, JSON.parse(copy)];
970
+ });
971
+ return prevDefers.reduce((memo, [path, node]) => {
972
+ return setIn(page, path, node);
973
+ }, page);
974
+ }
534
975
  function saveAndProcessPage(pageKey, page) {
535
976
  return (dispatch, getState) => {
536
977
  pageKey = urlToPageKey(pageKey);
537
- const { defers = [] } = page;
538
- if ("action" in page) {
539
- const prevPage = getState().pages[pageKey];
540
- dispatch(handleGraft({ pageKey, page }));
541
- const currentPage = getState().pages[pageKey];
542
- currentPage.fragments.forEach((fragment) => {
543
- const { type, path } = fragment;
544
- const currentFragment = getIn(currentPage, path);
545
- const prevFragment = getIn(prevPage, path);
546
- if (!prevFragment) {
547
- dispatch(
548
- updateFragments({
549
- name: type,
550
- pageKey,
551
- value: currentFragment,
552
- path
553
- })
554
- );
555
- } else if (currentFragment !== prevFragment) {
556
- dispatch(
557
- updateFragments({
558
- name: type,
559
- pageKey,
560
- value: currentFragment,
561
- previousValue: prevFragment,
562
- path
563
- })
564
- );
565
- }
566
- });
567
- } else {
568
- dispatch(saveResponse({ pageKey, page }));
569
- const currentPage = getState().pages[pageKey];
570
- currentPage.fragments.forEach((fragment) => {
571
- const { type, path } = fragment;
572
- const currentFragment = getIn(currentPage, path);
978
+ let nextPage = page;
979
+ const state = getState();
980
+ if (page.action === "savePage" && state.pages[pageKey]) {
981
+ const existingPage = createProxy(
982
+ state.pages[pageKey],
983
+ { current: state.fragments },
984
+ /* @__PURE__ */ new Set(),
985
+ /* @__PURE__ */ new WeakMap()
986
+ );
987
+ nextPage = JSON.parse(
988
+ JSON.stringify(addPlaceholdersToDeferredNodes(existingPage, nextPage))
989
+ );
990
+ }
991
+ page.fragments.reverse().forEach((fragment) => {
992
+ const { id, path } = fragment;
993
+ const node = getIn(nextPage, path);
994
+ nextPage = setIn(page, path, { __id: id });
995
+ dispatch(
996
+ saveFragment({
997
+ fragmentId: id,
998
+ data: node
999
+ })
1000
+ );
1001
+ });
1002
+ if (nextPage.action === "graft") {
1003
+ if (typeof nextPage.fragmentContext === "string") {
573
1004
  dispatch(
574
- updateFragments({
575
- name: type,
576
- pageKey,
577
- value: currentFragment,
578
- path
1005
+ handleFragmentGraft({
1006
+ fragmentId: nextPage.fragmentContext,
1007
+ response: nextPage
579
1008
  })
580
1009
  );
581
- });
1010
+ } else {
1011
+ dispatch(handleGraft({ pageKey, page: nextPage }));
1012
+ }
1013
+ } else if (nextPage.action === "handleStreamResponse") {
1014
+ dispatch(handleStreamResponse(nextPage));
1015
+ return Promise.resolve();
1016
+ } else {
1017
+ dispatch(saveResponse({ pageKey, page: nextPage }));
582
1018
  }
583
1019
  const hasFetch = typeof fetch != "undefined";
584
1020
  if (hasFetch) {
585
- return dispatch(fetchDeferments(pageKey, defers)).then(
1021
+ return dispatch(fetchDeferments(pageKey, nextPage.defers)).then(
586
1022
  () => Promise.resolve()
587
1023
  );
588
1024
  } else {