@thoughtbot/superglue 0.53.4 → 1.0.0

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.
@@ -0,0 +1,790 @@
1
+ // lib/utils/url.ts
2
+ import parse from "url-parse";
3
+ function pathQuery(url) {
4
+ const { pathname, query } = new parse(url, {});
5
+ return pathname + query;
6
+ }
7
+ function pathQueryHash(url) {
8
+ const { pathname, query, hash } = new parse(url, {});
9
+ return pathname + query + hash;
10
+ }
11
+ function hasPropsAt(url) {
12
+ const parsed = new parse(url, {}, true);
13
+ const query = parsed.query;
14
+ return !!query["props_at"];
15
+ }
16
+ function propsAtParam(url) {
17
+ const parsed = new parse(url, {}, true);
18
+ const query = parsed.query;
19
+ return query["props_at"];
20
+ }
21
+ function withFormatJson(url) {
22
+ const parsed = new parse(url, {}, true);
23
+ parsed.query["format"] = "json";
24
+ return parsed.toString();
25
+ }
26
+ function pathWithoutBZParams(url) {
27
+ const parsed = new parse(url, {}, true);
28
+ const query = parsed.query;
29
+ delete query["props_at"];
30
+ delete query["format"];
31
+ parsed.set("query", query);
32
+ return pathQueryHash(parsed.toString());
33
+ }
34
+ function removePropsAt(url) {
35
+ const parsed = new parse(url, {}, true);
36
+ const query = parsed.query;
37
+ delete query["props_at"];
38
+ parsed.set("query", query);
39
+ return parsed.toString();
40
+ }
41
+ function urlToPageKey(url) {
42
+ const parsed = new parse(url, {}, true);
43
+ const query = parsed.query;
44
+ delete query["props_at"];
45
+ delete query["format"];
46
+ parsed.set("query", query);
47
+ return pathQuery(parsed.toString());
48
+ }
49
+ function withoutHash(url) {
50
+ const parsed = new parse(url, {}, true);
51
+ parsed.set("hash", "");
52
+ return parsed.toString();
53
+ }
54
+ function withoutBusters(url) {
55
+ const parsed = new parse(url, {}, true);
56
+ const query = parsed.query;
57
+ delete query["format"];
58
+ parsed.set("query", query);
59
+ return pathQuery(parsed.toString());
60
+ }
61
+ function formatForXHR(url) {
62
+ const formats = [withoutHash, withFormatJson];
63
+ return formats.reduce((memo, f) => f(memo), url);
64
+ }
65
+ function parsePageKey(pageKey) {
66
+ const { pathname, query } = new parse(pageKey, {}, true);
67
+ return {
68
+ pathname,
69
+ search: query
70
+ };
71
+ }
72
+
73
+ // lib/utils/helpers.ts
74
+ function argsForHistory(path) {
75
+ const pageKey = urlToPageKey(path);
76
+ return [
77
+ path,
78
+ {
79
+ superglue: true,
80
+ pageKey,
81
+ posX: 0,
82
+ posY: 0
83
+ }
84
+ ];
85
+ }
86
+
87
+ // lib/utils/immutability.ts
88
+ var canLookAhead = /^[\da-zA-Z\-_]+=[\da-zA-Z\-_]+$/;
89
+ var KeyPathError = class extends Error {
90
+ constructor(message) {
91
+ super(message);
92
+ this.name = "KeyPathError";
93
+ }
94
+ };
95
+ function getIn(node, path) {
96
+ const keyPath = normalizeKeyPath(path);
97
+ let result = node;
98
+ let i;
99
+ for (i = 0; i < keyPath.length; i++) {
100
+ const key = keyPath[i];
101
+ if (typeof result === "object" && result !== null) {
102
+ if (!Array.isArray(result) && canLookAhead.test(key)) {
103
+ throw new KeyPathError(
104
+ `Expected to find an Array when using the key: ${key}`
105
+ );
106
+ }
107
+ result = atKey(result, key);
108
+ } else {
109
+ throw new KeyPathError(
110
+ `Expected to traverse an Array or Obj, got ${JSON.stringify(result)}`
111
+ );
112
+ }
113
+ }
114
+ if (i === keyPath.length) {
115
+ return result;
116
+ } else {
117
+ return void 0;
118
+ }
119
+ }
120
+ function clone(node) {
121
+ return Array.isArray(node) ? [].slice.call(node) : { ...node };
122
+ }
123
+ function getKey(node, key) {
124
+ if (Array.isArray(node) && Number.isNaN(Number(key))) {
125
+ const key_parts = Array.from(key.split("="));
126
+ const attr = key_parts[0];
127
+ const id = key_parts[1];
128
+ if (!id || !attr) {
129
+ return key;
130
+ }
131
+ let i;
132
+ let child;
133
+ for (i = 0; i < node.length; i++) {
134
+ child = node[i];
135
+ if (typeof child === "object" && !Array.isArray(child) && child !== null) {
136
+ const val = child[attr];
137
+ if (val && val.toString() === id) {
138
+ break;
139
+ }
140
+ } else {
141
+ throw new KeyPathError(`Could not look ahead ${key} at ${child}`);
142
+ }
143
+ }
144
+ if (i === node.length) {
145
+ throw new KeyPathError(`Could not find ${key} while looking ahead`);
146
+ }
147
+ return i;
148
+ } else {
149
+ return key;
150
+ }
151
+ }
152
+ function atKey(node, key) {
153
+ const actualKey = getKey(node, key);
154
+ if (Array.isArray(node)) {
155
+ return node[actualKey];
156
+ } else {
157
+ return node[actualKey];
158
+ }
159
+ }
160
+ function normalizeKeyPath(path) {
161
+ if (typeof path === "string") {
162
+ path = path.replace(/ /g, "");
163
+ if (path === "") {
164
+ return [];
165
+ }
166
+ return path.split(".");
167
+ } else {
168
+ return path;
169
+ }
170
+ }
171
+ function setIn(object, path, value) {
172
+ const keypath = normalizeKeyPath(path);
173
+ const results = { 0: object };
174
+ const parents = { 0: object };
175
+ let i;
176
+ for (i = 0; i < keypath.length; i++) {
177
+ const parent = parents[i];
178
+ if (!(typeof parent === "object" && parent !== null)) {
179
+ throw new KeyPathError(
180
+ `Expected to traverse an Array or Obj, got ${JSON.stringify(parent)}`
181
+ );
182
+ }
183
+ const child = atKey(parent, keypath[i]);
184
+ parents[i + 1] = child;
185
+ }
186
+ results[keypath.length] = value;
187
+ for (i = keypath.length - 1; i >= 0; i--) {
188
+ const target = clone(parents[i]);
189
+ results[i] = target;
190
+ const key = getKey(results[i], keypath[i]);
191
+ if (Array.isArray(target)) {
192
+ target[key] = results[i + 1];
193
+ } else {
194
+ target[key] = results[i + 1];
195
+ }
196
+ }
197
+ return results[0];
198
+ }
199
+
200
+ // lib/utils/request.ts
201
+ import parse2 from "url-parse";
202
+
203
+ // lib/config.ts
204
+ var config = {
205
+ baseUrl: "",
206
+ maxPages: 20
207
+ };
208
+
209
+ // lib/utils/request.ts
210
+ function isValidResponse(xhr) {
211
+ return isValidContent(xhr) && !downloadingFile(xhr);
212
+ }
213
+ function isValidContent(rsp) {
214
+ const contentType = rsp.headers.get("content-type");
215
+ const jsContent = /^(?:application\/json)(?:;|$)/;
216
+ return !!(contentType && contentType.match(jsContent));
217
+ }
218
+ function downloadingFile(xhr) {
219
+ const disposition = xhr.headers.get("content-disposition");
220
+ return !!(disposition && disposition.match(/^attachment/) !== null);
221
+ }
222
+ var SuperglueResponseError = class extends Error {
223
+ constructor(message) {
224
+ super(message);
225
+ this.name = "SuperglueResponseError";
226
+ }
227
+ };
228
+ function validateResponse(args) {
229
+ const { rsp } = args;
230
+ if (isValidResponse(rsp)) {
231
+ return args;
232
+ } else {
233
+ const error = new SuperglueResponseError("Invalid Superglue Response");
234
+ error.response = rsp;
235
+ throw error;
236
+ }
237
+ }
238
+ function handleServerErrors(args) {
239
+ const { rsp } = args;
240
+ if (!rsp.ok && rsp.status !== 422) {
241
+ if (rsp.status === 406) {
242
+ console.error(
243
+ "Superglue encountered a 406 Not Acceptable response. This can happen if you used respond_to and didn't specify format.json in the block. Try adding it to your respond_to. For example:\n\nrespond_to do |format|\n format.html\n format.json\n format.csv\nend"
244
+ );
245
+ }
246
+ const error = new SuperglueResponseError(rsp.statusText);
247
+ error.response = rsp;
248
+ throw error;
249
+ }
250
+ return args;
251
+ }
252
+ function argsForFetch(getState, pathQuery2, {
253
+ method = "GET",
254
+ headers = {},
255
+ body = "",
256
+ signal,
257
+ ...rest
258
+ } = {}) {
259
+ method = method.toUpperCase();
260
+ const currentState = getState().superglue;
261
+ const nextHeaders = { ...headers };
262
+ nextHeaders["x-requested-with"] = "XMLHttpRequest";
263
+ nextHeaders["accept"] = "application/json";
264
+ nextHeaders["x-superglue-request"] = "true";
265
+ if (method != "GET" && method != "HEAD") {
266
+ nextHeaders["content-type"] = "application/json";
267
+ }
268
+ if (body instanceof FormData) {
269
+ delete nextHeaders["content-type"];
270
+ }
271
+ if (currentState.csrfToken) {
272
+ nextHeaders["x-csrf-token"] = currentState.csrfToken;
273
+ }
274
+ const fetchPath = new parse2(
275
+ formatForXHR(pathQuery2),
276
+ config.baseUrl || {},
277
+ true
278
+ );
279
+ const credentials = "same-origin";
280
+ if (!(method == "GET" || method == "HEAD")) {
281
+ nextHeaders["x-http-method-override"] = method;
282
+ method = "POST";
283
+ }
284
+ const options = {
285
+ method,
286
+ headers: nextHeaders,
287
+ body,
288
+ credentials,
289
+ signal
290
+ };
291
+ if (currentState.currentPageKey) {
292
+ const referrer = new parse2(
293
+ currentState.currentPageKey,
294
+ config.baseUrl || {},
295
+ false
296
+ ).href;
297
+ options.referrer = referrer;
298
+ }
299
+ if (method == "GET" || method == "HEAD") {
300
+ if (options.body instanceof FormData) {
301
+ const allData = new URLSearchParams(
302
+ options.body
303
+ );
304
+ const nextQuery = { ...fetchPath.query, ...Object.fromEntries(allData) };
305
+ fetchPath.set("query", nextQuery);
306
+ }
307
+ delete options.body;
308
+ }
309
+ return [fetchPath.toString(), { ...options, ...rest }];
310
+ }
311
+ function extractJSON(rsp) {
312
+ return rsp.json().then((json) => {
313
+ return { rsp, json };
314
+ }).catch((e) => {
315
+ e.response = rsp;
316
+ throw e;
317
+ });
318
+ }
319
+ function parseResponse(prm) {
320
+ return Promise.resolve(prm).then(extractJSON).then(handleServerErrors).then(validateResponse);
321
+ }
322
+
323
+ // lib/utils/ujs.ts
324
+ var HandlerBuilder = class {
325
+ constructor({
326
+ ujsAttributePrefix,
327
+ visit: visit2,
328
+ remote: remote2,
329
+ store
330
+ }) {
331
+ this.attributePrefix = ujsAttributePrefix;
332
+ this.isUJS = this.isUJS.bind(this);
333
+ this.store = store;
334
+ this.handleSubmit = this.handleSubmit.bind(this);
335
+ this.handleClick = this.handleClick.bind(this);
336
+ this.visit = visit2;
337
+ this.remote = remote2;
338
+ this.visitOrRemote = this.visitOrRemote.bind(this);
339
+ }
340
+ retrieveLink(target) {
341
+ const link = target.closest("a");
342
+ if (link && link.href.length !== 0) {
343
+ return link;
344
+ }
345
+ }
346
+ isNonStandardClick(event) {
347
+ return event.button > 0 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey;
348
+ }
349
+ isUJS(node) {
350
+ const hasVisit = !!node.getAttribute(this.attributePrefix + "-visit");
351
+ const hasRemote = !!node.getAttribute(this.attributePrefix + "-remote");
352
+ return hasVisit || hasRemote;
353
+ }
354
+ handleSubmit(event) {
355
+ const form = event.target;
356
+ if (!(form instanceof HTMLFormElement)) {
357
+ return;
358
+ }
359
+ if (!this.isUJS(form)) {
360
+ return;
361
+ }
362
+ event.preventDefault();
363
+ let url = form.getAttribute("action");
364
+ if (!url) {
365
+ return;
366
+ }
367
+ const method = (form.getAttribute("method") || "POST").toUpperCase();
368
+ url = withoutBusters(url);
369
+ this.visitOrRemote(form, url, {
370
+ method,
371
+ body: new FormData(form)
372
+ });
373
+ }
374
+ handleClick(event) {
375
+ if (!(event.target instanceof Element)) {
376
+ return;
377
+ }
378
+ const link = this.retrieveLink(event.target);
379
+ const isNonStandard = this.isNonStandardClick(event);
380
+ if (!link || isNonStandard || !this.isUJS(link)) {
381
+ return;
382
+ }
383
+ event.preventDefault();
384
+ let url = link.getAttribute("href");
385
+ if (!url) {
386
+ return;
387
+ }
388
+ url = withoutBusters(url);
389
+ this.visitOrRemote(link, url, { method: "GET" });
390
+ }
391
+ visitOrRemote(linkOrForm, url, opts) {
392
+ const dataset = { ...linkOrForm.dataset };
393
+ if (linkOrForm.getAttribute(this.attributePrefix + "-visit")) {
394
+ this.visit(url, { ...opts, dataset });
395
+ }
396
+ if (linkOrForm.getAttribute(this.attributePrefix + "-remote")) {
397
+ const { currentPageKey } = this.store.getState().superglue;
398
+ this.remote(url, {
399
+ ...opts,
400
+ pageKey: currentPageKey,
401
+ dataset
402
+ });
403
+ }
404
+ }
405
+ handlers() {
406
+ return {
407
+ onClick: this.handleClick,
408
+ onSubmit: this.handleSubmit
409
+ };
410
+ }
411
+ };
412
+ var ujsHandlers = ({
413
+ ujsAttributePrefix,
414
+ visit: visit2,
415
+ remote: remote2,
416
+ store
417
+ }) => {
418
+ const builder = new HandlerBuilder({
419
+ visit: visit2,
420
+ remote: remote2,
421
+ ujsAttributePrefix,
422
+ store
423
+ });
424
+ return builder.handlers();
425
+ };
426
+
427
+ // lib/utils/window.ts
428
+ function needsRefresh(prevAssets, newAssets) {
429
+ if (prevAssets && newAssets) {
430
+ const hasNewAssets = !newAssets.every((asset) => prevAssets.includes(asset));
431
+ return hasNewAssets;
432
+ } else {
433
+ return false;
434
+ }
435
+ }
436
+
437
+ // lib/action_creators/index.ts
438
+ import parse3 from "url-parse";
439
+
440
+ // lib/actions.ts
441
+ import { createAction } from "@reduxjs/toolkit";
442
+ var GRAFTING_ERROR = "@@superglue/GRAFTING_ERROR";
443
+ var GRAFTING_SUCCESS = "@@superglue/GRAFTING_SUCCESS";
444
+ var saveResponse = createAction(
445
+ "@@superglue/SAVE_RESPONSE",
446
+ ({ pageKey, page }) => {
447
+ pageKey = urlToPageKey(pageKey);
448
+ return {
449
+ payload: {
450
+ pageKey,
451
+ page
452
+ }
453
+ };
454
+ }
455
+ );
456
+ var handleGraft = createAction(
457
+ "@@superglue/HANDLE_GRAFT",
458
+ ({ pageKey, page }) => {
459
+ pageKey = urlToPageKey(pageKey);
460
+ return {
461
+ payload: {
462
+ page,
463
+ pageKey
464
+ }
465
+ };
466
+ }
467
+ );
468
+ var superglueError = createAction(
469
+ "@@superglue/ERROR"
470
+ );
471
+ var updateFragments = createAction("@@superglue/UPDATE_FRAGMENTS");
472
+ var copyPage = createAction(
473
+ "@@superglue/COPY_PAGE"
474
+ );
475
+ var removePage = createAction(
476
+ "@@superglue/REMOVE_PAGE"
477
+ );
478
+ var beforeFetch = createAction(
479
+ "@@superglue/BEFORE_FETCH"
480
+ );
481
+ var beforeVisit = createAction("@@superglue/BEFORE_VISIT");
482
+ var beforeRemote = createAction("@@superglue/BEFORE_REMOTE");
483
+ var setCSRFToken = createAction("@@superglue/SET_CSRF_TOKEN");
484
+ var historyChange = createAction("@@superglue/HISTORY_CHANGE");
485
+ var setActivePage = createAction("@@superglue/SET_ACTIVE_PAGE");
486
+
487
+ // lib/action_creators/requests.ts
488
+ function handleFetchErr(err, fetchArgs, dispatch) {
489
+ dispatch(superglueError({ message: err.message }));
490
+ throw err;
491
+ }
492
+ function buildMeta(pageKey, page, state, rsp, fetchArgs) {
493
+ const { assets: prevAssets } = state;
494
+ const { assets: nextAssets } = page;
495
+ return {
496
+ pageKey,
497
+ page,
498
+ redirected: rsp.redirected,
499
+ rsp,
500
+ fetchArgs,
501
+ componentIdentifier: page.componentIdentifier,
502
+ needsRefresh: needsRefresh(prevAssets, nextAssets)
503
+ };
504
+ }
505
+ var MismatchedComponentError = class extends Error {
506
+ constructor(message) {
507
+ super(message);
508
+ this.name = "MismatchedComponentError";
509
+ }
510
+ };
511
+ var remote = (path, {
512
+ pageKey: targetPageKey,
513
+ force = false,
514
+ beforeSave = (prevPage, receivedPage) => receivedPage,
515
+ ...rest
516
+ } = {}) => {
517
+ path = withoutBusters(path);
518
+ targetPageKey = targetPageKey && urlToPageKey(targetPageKey);
519
+ return (dispatch, getState) => {
520
+ const fetchArgs = argsForFetch(getState, path, rest);
521
+ const currentPageKey = getState().superglue.currentPageKey;
522
+ dispatch(beforeRemote({ currentPageKey, fetchArgs }));
523
+ dispatch(beforeFetch({ fetchArgs }));
524
+ return fetch(...fetchArgs).then(parseResponse).then(({ rsp, json }) => {
525
+ const { superglue, pages = {} } = getState();
526
+ let pageKey;
527
+ if (targetPageKey === void 0) {
528
+ const isGet = fetchArgs[1].method === "GET";
529
+ pageKey = calculatePageKey(rsp, isGet, currentPageKey);
530
+ } else {
531
+ pageKey = targetPageKey;
532
+ }
533
+ const meta = buildMeta(pageKey, json, superglue, rsp, fetchArgs);
534
+ const existingId = pages[pageKey]?.componentIdentifier;
535
+ const receivedId = json.componentIdentifier;
536
+ if (!!existingId && existingId != receivedId && !force) {
537
+ const message = `You cannot replace or update an existing page
538
+ located at pages["${currentPageKey}"] that has a componentIdentifier
539
+ of "${existingId}" with the contents of a page response that has a
540
+ componentIdentifier of "${receivedId}".
541
+
542
+ This can happen if you're using data-sg-remote or remote but your
543
+ response redirected to a page with a different componentIdentifier
544
+ than the target page.
545
+
546
+ This limitation exists because the resulting page shape from grafting
547
+ "${receivedId}"'s "${propsAtParam(path)}" into "${existingId}" may not be
548
+ compatible with the page component associated with "${existingId}".
549
+
550
+ Consider using data-sg-visit, the visit function, or redirect_back to
551
+ the same page. Or if you're sure you want to proceed, use force: true.
552
+ `;
553
+ throw new MismatchedComponentError(message);
554
+ }
555
+ const page = beforeSave(pages[pageKey], json);
556
+ return dispatch(saveAndProcessPage(pageKey, page)).then(() => meta);
557
+ }).catch((e) => handleFetchErr(e, fetchArgs, dispatch));
558
+ };
559
+ };
560
+ var lastVisitController = {
561
+ /* eslint-disable-next-line @typescript-eslint/no-unused-vars */
562
+ abort: (_reason) => {
563
+ }
564
+ };
565
+ var visit = (path, {
566
+ placeholderKey,
567
+ beforeSave = (prevPage, receivedPage) => receivedPage,
568
+ revisit = false,
569
+ ...rest
570
+ } = {}) => {
571
+ path = withoutBusters(path);
572
+ return (dispatch, getState) => {
573
+ const currentPageKey = getState().superglue.currentPageKey;
574
+ placeholderKey = placeholderKey && urlToPageKey(placeholderKey) || currentPageKey;
575
+ const hasPlaceholder = placeholderKey in getState().pages;
576
+ if (hasPropsAt(path) && !hasPlaceholder) {
577
+ console.warn(
578
+ `Could not find placeholder with key ${placeholderKey} in state. The props_at param will be ignored`
579
+ );
580
+ path = removePropsAt(path);
581
+ }
582
+ const controller = new AbortController();
583
+ const { signal } = controller;
584
+ const fetchArgs = argsForFetch(getState, path, {
585
+ ...rest,
586
+ signal
587
+ });
588
+ dispatch(beforeVisit({ currentPageKey, fetchArgs }));
589
+ dispatch(beforeFetch({ fetchArgs }));
590
+ lastVisitController.abort(
591
+ "Aborting the previous `visit`. There can be one visit at a time. Use `remote` if there is a need for async requests."
592
+ );
593
+ lastVisitController = controller;
594
+ return fetch(...fetchArgs).then(parseResponse).then(({ rsp, json }) => {
595
+ const { superglue, pages = {} } = getState();
596
+ const isGet = fetchArgs[1].method === "GET";
597
+ const pageKey = calculatePageKey(rsp, isGet, currentPageKey);
598
+ if (placeholderKey && hasPropsAt(path) && hasPlaceholder) {
599
+ const existingId = pages[placeholderKey]?.componentIdentifier;
600
+ const receivedId = json.componentIdentifier;
601
+ if (!!existingId && existingId != receivedId) {
602
+ const message = `You received a page response with a
603
+ componentIdentifier "${receivedId}" that is different than the
604
+ componentIdentifier "${existingId}" located at ${placeholderKey}.
605
+
606
+ This can happen if you're using data-sg-visit or visit with a
607
+ props_at param, but the response redirected to a page with a
608
+ different componentIdentifier than the target page.
609
+
610
+ This limitation exists because the resulting page shape from grafting
611
+ "${receivedId}"'s "${propsAtParam(path)}" into "${existingId}" may not be
612
+ compatible with the page component associated with "${existingId}".
613
+
614
+ Check that you're rendering a page with a matching
615
+ componentIdentifier, or consider using redirect_back_with_props_at
616
+ to the same page.
617
+ `;
618
+ throw new MismatchedComponentError(message);
619
+ }
620
+ dispatch(copyPage({ from: placeholderKey, to: pageKey }));
621
+ }
622
+ const meta = buildMeta(pageKey, json, superglue, rsp, fetchArgs);
623
+ const visitMeta = {
624
+ ...meta,
625
+ navigationAction: calculateNavAction(
626
+ meta,
627
+ rsp,
628
+ isGet,
629
+ pageKey,
630
+ currentPageKey,
631
+ revisit
632
+ )
633
+ };
634
+ const page = beforeSave(pages[pageKey], json);
635
+ return dispatch(saveAndProcessPage(pageKey, page)).then(() => visitMeta);
636
+ }).catch((e) => handleFetchErr(e, fetchArgs, dispatch));
637
+ };
638
+ };
639
+ function calculateNavAction(meta, rsp, isGet, pageKey, currentPageKey, revisit) {
640
+ let navigationAction = "push";
641
+ if (!rsp.redirected && !isGet) {
642
+ navigationAction = "replace";
643
+ }
644
+ const isSamePage = pageKey == currentPageKey;
645
+ if (isSamePage) {
646
+ navigationAction = "none";
647
+ }
648
+ if (revisit && isGet) {
649
+ if (rsp.redirected) {
650
+ navigationAction = "replace";
651
+ } else {
652
+ navigationAction = "none";
653
+ }
654
+ }
655
+ return navigationAction;
656
+ }
657
+ function calculatePageKey(rsp, isGet, currentPageKey) {
658
+ let pageKey = urlToPageKey(rsp.url);
659
+ if (!isGet && !rsp.redirected) {
660
+ pageKey = currentPageKey;
661
+ }
662
+ const contentLocation = rsp.headers.get("content-location");
663
+ if (contentLocation) {
664
+ pageKey = urlToPageKey(contentLocation);
665
+ }
666
+ return pageKey;
667
+ }
668
+
669
+ // lib/action_creators/index.ts
670
+ function fetchDeferments(pageKey, defers = []) {
671
+ pageKey = urlToPageKey(pageKey);
672
+ return (dispatch) => {
673
+ const fetches = defers.filter(({ type }) => type === "auto").map(function({
674
+ url,
675
+ successAction = GRAFTING_SUCCESS,
676
+ failAction = GRAFTING_ERROR
677
+ }) {
678
+ const parsedUrl = new parse3(url, true);
679
+ const keyPath = parsedUrl.query.props_at;
680
+ return dispatch(remote(url, { pageKey })).then(() => {
681
+ dispatch({
682
+ type: successAction,
683
+ payload: {
684
+ pageKey,
685
+ keyPath
686
+ }
687
+ });
688
+ }).catch((err) => {
689
+ dispatch({
690
+ type: failAction,
691
+ payload: {
692
+ url,
693
+ err,
694
+ pageKey,
695
+ keyPath
696
+ }
697
+ });
698
+ });
699
+ });
700
+ return Promise.all(fetches);
701
+ };
702
+ }
703
+ function saveAndProcessPage(pageKey, page) {
704
+ return (dispatch, getState) => {
705
+ pageKey = urlToPageKey(pageKey);
706
+ const { defers = [] } = page;
707
+ if ("action" in page) {
708
+ const prevPage = getState().pages[pageKey];
709
+ dispatch(handleGraft({ pageKey, page }));
710
+ const currentPage = getState().pages[pageKey];
711
+ currentPage.fragments.forEach((fragment) => {
712
+ const { type, path } = fragment;
713
+ const currentFragment = getIn(currentPage, path);
714
+ const prevFragment = getIn(prevPage, path);
715
+ if (!prevFragment) {
716
+ dispatch(
717
+ updateFragments({
718
+ name: type,
719
+ pageKey,
720
+ value: currentFragment,
721
+ path
722
+ })
723
+ );
724
+ } else if (currentFragment !== prevFragment) {
725
+ dispatch(
726
+ updateFragments({
727
+ name: type,
728
+ pageKey,
729
+ value: currentFragment,
730
+ previousValue: prevFragment,
731
+ path
732
+ })
733
+ );
734
+ }
735
+ });
736
+ } else {
737
+ dispatch(saveResponse({ pageKey, page }));
738
+ const currentPage = getState().pages[pageKey];
739
+ currentPage.fragments.forEach((fragment) => {
740
+ const { type, path } = fragment;
741
+ const currentFragment = getIn(currentPage, path);
742
+ dispatch(
743
+ updateFragments({
744
+ name: type,
745
+ pageKey,
746
+ value: currentFragment,
747
+ path
748
+ })
749
+ );
750
+ });
751
+ }
752
+ const hasFetch = typeof fetch != "undefined";
753
+ if (hasFetch) {
754
+ return dispatch(fetchDeferments(pageKey, defers)).then(
755
+ () => Promise.resolve()
756
+ );
757
+ } else {
758
+ return Promise.resolve();
759
+ }
760
+ };
761
+ }
762
+
763
+ export {
764
+ config,
765
+ pathWithoutBZParams,
766
+ urlToPageKey,
767
+ parsePageKey,
768
+ argsForHistory,
769
+ getIn,
770
+ setIn,
771
+ ujsHandlers,
772
+ GRAFTING_ERROR,
773
+ GRAFTING_SUCCESS,
774
+ saveResponse,
775
+ handleGraft,
776
+ updateFragments,
777
+ copyPage,
778
+ removePage,
779
+ beforeFetch,
780
+ beforeVisit,
781
+ beforeRemote,
782
+ setCSRFToken,
783
+ historyChange,
784
+ setActivePage,
785
+ MismatchedComponentError,
786
+ remote,
787
+ visit,
788
+ saveAndProcessPage
789
+ };
790
+ //# sourceMappingURL=chunk-LGUVOEZ3.mjs.map