@thoughtbot/superglue 1.0.3 → 2.0.0-alpha.10
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/.tool-versions +1 -2
- package/dist/action_creators.d.mts +1 -1
- package/dist/action_creators.mjs +1 -1
- package/dist/{chunk-ENOVWJUC.mjs → chunk-J2XH5QTK.mjs} +565 -68
- package/dist/chunk-J2XH5QTK.mjs.map +1 -0
- package/dist/cjs/action_creators.cjs +535 -66
- package/dist/cjs/action_creators.cjs.map +1 -1
- package/dist/cjs/superglue.cjs +780 -112
- package/dist/cjs/superglue.cjs.map +1 -1
- package/dist/{index-BYr1PoYr.d.mts → index-j0c-9ZLt.d.mts} +69 -18
- package/dist/superglue.d.mts +46 -14
- package/dist/superglue.mjs +267 -54
- package/dist/superglue.mjs.map +1 -1
- package/package.json +16 -8
- package/thoughtbot-superglue-2.0.0-alpha.10.tgz +0 -0
- package/typedoc.json +1 -0
- package/dist/chunk-ENOVWJUC.mjs.map +0 -1
|
@@ -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
|
}
|
|
@@ -250,7 +303,7 @@ function argsForFetch(getState, pathQuery2, {
|
|
|
250
303
|
return [fetchPath.toString(), { ...options, ...rest }];
|
|
251
304
|
}
|
|
252
305
|
function extractJSON(rsp) {
|
|
253
|
-
return rsp.
|
|
306
|
+
return rsp.json().then((json) => {
|
|
254
307
|
return { rsp, json };
|
|
255
308
|
}).catch((e) => {
|
|
256
309
|
e.response = rsp;
|
|
@@ -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,24 +369,285 @@ 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
|
+
);
|
|
431
|
+
|
|
432
|
+
// lib/utils/proxy.ts
|
|
433
|
+
var ORIGINAL_TARGET = Symbol("@@originalTarget");
|
|
434
|
+
var ARRAY_GETTER_METHODS = /* @__PURE__ */ new Set([
|
|
435
|
+
Symbol.iterator,
|
|
436
|
+
"at",
|
|
437
|
+
"concat",
|
|
438
|
+
"entries",
|
|
439
|
+
"every",
|
|
440
|
+
"filter",
|
|
441
|
+
"find",
|
|
442
|
+
"findIndex",
|
|
443
|
+
"flat",
|
|
444
|
+
"flatMap",
|
|
445
|
+
"forEach",
|
|
446
|
+
"includes",
|
|
447
|
+
"indexOf",
|
|
448
|
+
"join",
|
|
449
|
+
"keys",
|
|
450
|
+
"lastIndexOf",
|
|
451
|
+
"map",
|
|
452
|
+
"reduce",
|
|
453
|
+
"reduceRight",
|
|
454
|
+
"slice",
|
|
455
|
+
"some",
|
|
456
|
+
"toString",
|
|
457
|
+
"values"
|
|
458
|
+
]);
|
|
459
|
+
var ARRAY_SETTER_METHODS = /* @__PURE__ */ new Set([
|
|
460
|
+
"copyWithin",
|
|
461
|
+
"fill",
|
|
462
|
+
"pop",
|
|
463
|
+
"push",
|
|
464
|
+
"reverse",
|
|
465
|
+
"shift",
|
|
466
|
+
"sort",
|
|
467
|
+
"splice",
|
|
468
|
+
"unshift"
|
|
469
|
+
]);
|
|
470
|
+
function isArraySetter(prop) {
|
|
471
|
+
return ARRAY_SETTER_METHODS.has(prop);
|
|
472
|
+
}
|
|
473
|
+
function isArrayGetter(prop) {
|
|
474
|
+
return ARRAY_GETTER_METHODS.has(prop);
|
|
475
|
+
}
|
|
476
|
+
function convertToInt(prop) {
|
|
477
|
+
if (typeof prop === "symbol") return null;
|
|
478
|
+
const num = Number(prop);
|
|
479
|
+
return Number.isInteger(num) ? num : null;
|
|
480
|
+
}
|
|
481
|
+
function isFragmentReference(value) {
|
|
482
|
+
return !!value && typeof value === "object" && "__id" in value && typeof value.__id === "string";
|
|
483
|
+
}
|
|
484
|
+
function createArrayProxy(arrayData, fragments, dependencies, proxyCache) {
|
|
485
|
+
if (proxyCache && proxyCache.has(arrayData)) {
|
|
486
|
+
return proxyCache.get(arrayData);
|
|
487
|
+
}
|
|
488
|
+
const proxy = new Proxy(arrayData, {
|
|
489
|
+
get(target, prop) {
|
|
490
|
+
if (prop === ORIGINAL_TARGET) {
|
|
491
|
+
return target;
|
|
492
|
+
}
|
|
493
|
+
if (isArrayGetter(prop)) {
|
|
494
|
+
const method = target[prop];
|
|
495
|
+
if (typeof method === "function") {
|
|
496
|
+
return function(...args) {
|
|
497
|
+
return Reflect.apply(method, proxy, args);
|
|
498
|
+
};
|
|
499
|
+
}
|
|
500
|
+
return method;
|
|
501
|
+
}
|
|
502
|
+
if (isArraySetter(prop)) {
|
|
503
|
+
throw new Error(
|
|
504
|
+
`Cannot mutate proxy array. Use useSetFragment to update state.`
|
|
505
|
+
);
|
|
506
|
+
}
|
|
507
|
+
const index = convertToInt(prop);
|
|
508
|
+
if (index !== null && index >= 0 && index < target.length) {
|
|
509
|
+
const item = target[index];
|
|
510
|
+
if (isFragmentReference(item)) {
|
|
511
|
+
dependencies.add(item.__id);
|
|
512
|
+
const fragmentData = fragments.current[item.__id];
|
|
513
|
+
if (!fragmentData) {
|
|
514
|
+
return void 0;
|
|
515
|
+
}
|
|
516
|
+
return createProxy(fragmentData, fragments, dependencies, proxyCache);
|
|
517
|
+
}
|
|
518
|
+
if (typeof item === "object" && item !== null) {
|
|
519
|
+
if ("$$typeof" in item) {
|
|
520
|
+
return item;
|
|
521
|
+
} else {
|
|
522
|
+
return createProxy(
|
|
523
|
+
item,
|
|
524
|
+
fragments,
|
|
525
|
+
dependencies,
|
|
526
|
+
proxyCache
|
|
527
|
+
);
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
return item;
|
|
531
|
+
}
|
|
532
|
+
return Reflect.get(target, prop);
|
|
533
|
+
},
|
|
534
|
+
has(target, prop) {
|
|
535
|
+
if (prop === ORIGINAL_TARGET) {
|
|
536
|
+
return true;
|
|
537
|
+
}
|
|
538
|
+
return Reflect.has(target, prop);
|
|
539
|
+
},
|
|
540
|
+
set() {
|
|
541
|
+
throw new Error(
|
|
542
|
+
"Cannot mutate proxy array. Use useSetFragment to update state."
|
|
543
|
+
);
|
|
544
|
+
},
|
|
545
|
+
deleteProperty() {
|
|
546
|
+
throw new Error(
|
|
547
|
+
"Cannot delete properties on proxy array. Use useSetFragment to update state."
|
|
548
|
+
);
|
|
549
|
+
},
|
|
550
|
+
defineProperty() {
|
|
551
|
+
throw new Error(
|
|
552
|
+
"Cannot define properties on proxy array. Use useSetFragment to update state."
|
|
553
|
+
);
|
|
554
|
+
}
|
|
555
|
+
});
|
|
556
|
+
if (proxyCache) {
|
|
557
|
+
proxyCache.set(arrayData, proxy);
|
|
558
|
+
}
|
|
559
|
+
return proxy;
|
|
560
|
+
}
|
|
561
|
+
function createObjectProxy(objectData, fragments, dependencies, proxyCache) {
|
|
562
|
+
if (proxyCache && proxyCache.has(objectData)) {
|
|
563
|
+
return proxyCache.get(objectData);
|
|
564
|
+
}
|
|
565
|
+
const proxy = new Proxy(objectData, {
|
|
566
|
+
get(target, prop) {
|
|
567
|
+
if (prop === ORIGINAL_TARGET) {
|
|
568
|
+
return target;
|
|
569
|
+
}
|
|
570
|
+
const value = target[prop];
|
|
571
|
+
if (isFragmentReference(value)) {
|
|
572
|
+
dependencies.add(value.__id);
|
|
573
|
+
const fragmentData = fragments.current[value.__id];
|
|
574
|
+
if (!fragmentData) {
|
|
575
|
+
return void 0;
|
|
576
|
+
}
|
|
577
|
+
return createProxy(fragmentData, fragments, dependencies, proxyCache);
|
|
578
|
+
}
|
|
579
|
+
if (typeof value === "object" && value !== null) {
|
|
580
|
+
if ("$$typeof" in value) {
|
|
581
|
+
return value;
|
|
582
|
+
} else if (Array.isArray(value)) {
|
|
583
|
+
return createArrayProxy(value, fragments, dependencies, proxyCache);
|
|
584
|
+
} else {
|
|
585
|
+
return createObjectProxy(value, fragments, dependencies, proxyCache);
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
return value;
|
|
589
|
+
},
|
|
590
|
+
has(target, prop) {
|
|
591
|
+
if (prop === ORIGINAL_TARGET) {
|
|
592
|
+
return true;
|
|
593
|
+
}
|
|
594
|
+
return Reflect.has(target, prop);
|
|
595
|
+
},
|
|
596
|
+
set() {
|
|
597
|
+
throw new Error(
|
|
598
|
+
"Cannot mutate proxy object. Use useSetFragment to update state."
|
|
599
|
+
);
|
|
600
|
+
},
|
|
601
|
+
deleteProperty() {
|
|
602
|
+
throw new Error(
|
|
603
|
+
"Cannot delete properties on proxy object. Use useSetFragment to update state."
|
|
604
|
+
);
|
|
605
|
+
},
|
|
606
|
+
defineProperty() {
|
|
607
|
+
throw new Error(
|
|
608
|
+
"Cannot define properties on proxy object. Use useSetFragment to update state."
|
|
609
|
+
);
|
|
610
|
+
}
|
|
611
|
+
});
|
|
612
|
+
if (proxyCache) {
|
|
613
|
+
proxyCache.set(objectData, proxy);
|
|
614
|
+
}
|
|
615
|
+
return proxy;
|
|
616
|
+
}
|
|
617
|
+
function createProxy(content, fragments, dependencies, proxyCache) {
|
|
618
|
+
if (!content || typeof content !== "object") {
|
|
619
|
+
return content;
|
|
620
|
+
}
|
|
621
|
+
if ("$$typeof" in content) {
|
|
622
|
+
return content;
|
|
623
|
+
}
|
|
624
|
+
if (Array.isArray(content)) {
|
|
625
|
+
return createArrayProxy(content, fragments, dependencies, proxyCache);
|
|
626
|
+
}
|
|
627
|
+
return createObjectProxy(content, fragments, dependencies, proxyCache);
|
|
628
|
+
}
|
|
320
629
|
|
|
321
630
|
// lib/action_creators/requests.ts
|
|
322
631
|
function handleFetchErr(err, fetchArgs, dispatch) {
|
|
323
632
|
dispatch(superglueError({ message: err.message }));
|
|
633
|
+
console.error(err);
|
|
324
634
|
throw err;
|
|
325
635
|
}
|
|
326
636
|
function buildMeta(pageKey, page, state, rsp, fetchArgs) {
|
|
327
637
|
const { assets: prevAssets } = state;
|
|
328
638
|
const { assets: nextAssets } = page;
|
|
329
|
-
|
|
639
|
+
const meta = {
|
|
330
640
|
pageKey,
|
|
331
641
|
page,
|
|
332
642
|
redirected: rsp.redirected,
|
|
333
643
|
rsp,
|
|
334
644
|
fetchArgs,
|
|
335
|
-
componentIdentifier: page.componentIdentifier,
|
|
336
645
|
needsRefresh: needsRefresh(prevAssets, nextAssets)
|
|
337
646
|
};
|
|
647
|
+
if (page.action !== "handleStreamResponse") {
|
|
648
|
+
meta.componentIdentifier = page.componentIdentifier;
|
|
649
|
+
}
|
|
650
|
+
return meta;
|
|
338
651
|
}
|
|
339
652
|
var MismatchedComponentError = class extends Error {
|
|
340
653
|
constructor(message) {
|
|
@@ -342,10 +655,11 @@ var MismatchedComponentError = class extends Error {
|
|
|
342
655
|
this.name = "MismatchedComponentError";
|
|
343
656
|
}
|
|
344
657
|
};
|
|
658
|
+
var defaultBeforeSave = (prevPage, receivedPage) => receivedPage;
|
|
345
659
|
var remote = (path, {
|
|
346
660
|
pageKey: targetPageKey,
|
|
347
661
|
force = false,
|
|
348
|
-
beforeSave =
|
|
662
|
+
beforeSave = defaultBeforeSave,
|
|
349
663
|
...rest
|
|
350
664
|
} = {}) => {
|
|
351
665
|
targetPageKey = targetPageKey && urlToPageKey(targetPageKey);
|
|
@@ -355,7 +669,7 @@ var remote = (path, {
|
|
|
355
669
|
dispatch(beforeRemote({ currentPageKey, fetchArgs }));
|
|
356
670
|
dispatch(beforeFetch({ fetchArgs }));
|
|
357
671
|
return fetch(...fetchArgs).then(parseResponse).then(({ rsp, json }) => {
|
|
358
|
-
const { superglue, pages = {} } = getState();
|
|
672
|
+
const { superglue, pages = {}, fragments } = getState();
|
|
359
673
|
let pageKey;
|
|
360
674
|
if (targetPageKey === void 0) {
|
|
361
675
|
const isGet = fetchArgs[1].method === "GET";
|
|
@@ -364,10 +678,11 @@ var remote = (path, {
|
|
|
364
678
|
pageKey = targetPageKey;
|
|
365
679
|
}
|
|
366
680
|
const meta = buildMeta(pageKey, json, superglue, rsp, fetchArgs);
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
681
|
+
if (json.action !== "handleStreamResponse") {
|
|
682
|
+
const existingId = pages[pageKey]?.componentIdentifier;
|
|
683
|
+
const receivedId = json.componentIdentifier;
|
|
684
|
+
if (!!existingId && existingId != receivedId && !force) {
|
|
685
|
+
const message = `You cannot replace or update an existing page
|
|
371
686
|
located at pages["${currentPageKey}"] that has a componentIdentifier
|
|
372
687
|
of "${existingId}" with the contents of a page response that has a
|
|
373
688
|
componentIdentifier of "${receivedId}".
|
|
@@ -383,9 +698,27 @@ compatible with the page component associated with "${existingId}".
|
|
|
383
698
|
Consider using data-sg-visit, the visit function, or redirect_back to
|
|
384
699
|
the same page. Or if you're sure you want to proceed, use force: true.
|
|
385
700
|
`;
|
|
386
|
-
|
|
701
|
+
throw new MismatchedComponentError(message);
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
dispatch(
|
|
705
|
+
receiveResponse({
|
|
706
|
+
pageKey,
|
|
707
|
+
response: JSON.parse(JSON.stringify(json))
|
|
708
|
+
})
|
|
709
|
+
);
|
|
710
|
+
const existingPage = createProxy(
|
|
711
|
+
pages[pageKey],
|
|
712
|
+
{ current: fragments },
|
|
713
|
+
/* @__PURE__ */ new Set(),
|
|
714
|
+
/* @__PURE__ */ new WeakMap()
|
|
715
|
+
);
|
|
716
|
+
let page = json;
|
|
717
|
+
if (json.action === "savePage" || json.action === "graft") {
|
|
718
|
+
page = JSON.parse(
|
|
719
|
+
JSON.stringify(beforeSave(existingPage, json))
|
|
720
|
+
);
|
|
387
721
|
}
|
|
388
|
-
const page = beforeSave(pages[pageKey], json);
|
|
389
722
|
return dispatch(saveAndProcessPage(pageKey, page)).then(() => meta);
|
|
390
723
|
}).catch((e) => handleFetchErr(e, fetchArgs, dispatch));
|
|
391
724
|
};
|
|
@@ -424,14 +757,16 @@ var visit = (path, {
|
|
|
424
757
|
);
|
|
425
758
|
lastVisitController = controller;
|
|
426
759
|
return fetch(...fetchArgs).then(parseResponse).then(({ rsp, json }) => {
|
|
427
|
-
const { superglue, pages = {} } = getState();
|
|
760
|
+
const { superglue, pages = {}, fragments } = getState();
|
|
428
761
|
const isGet = fetchArgs[1].method === "GET";
|
|
429
762
|
const pageKey = calculatePageKey(rsp, isGet, currentPageKey);
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
const
|
|
763
|
+
const meta = buildMeta(pageKey, json, superglue, rsp, fetchArgs);
|
|
764
|
+
if (json.action !== "handleStreamResponse") {
|
|
765
|
+
if (placeholderKey && hasPropsAt(path) && hasPlaceholder) {
|
|
766
|
+
const existingId = pages[placeholderKey]?.componentIdentifier;
|
|
767
|
+
const receivedId = json.componentIdentifier;
|
|
768
|
+
if (!!existingId && existingId != receivedId) {
|
|
769
|
+
const message = `You received a page response with a
|
|
435
770
|
componentIdentifier "${receivedId}" that is different than the
|
|
436
771
|
componentIdentifier "${existingId}" located at ${placeholderKey}.
|
|
437
772
|
|
|
@@ -447,29 +782,50 @@ Check that you're rendering a page with a matching
|
|
|
447
782
|
componentIdentifier, or consider using redirect_back_with_props_at
|
|
448
783
|
to the same page.
|
|
449
784
|
`;
|
|
450
|
-
|
|
785
|
+
throw new MismatchedComponentError(message);
|
|
786
|
+
}
|
|
787
|
+
dispatch(copyPage({ from: placeholderKey, to: pageKey }));
|
|
451
788
|
}
|
|
452
|
-
dispatch(copyPage({ from: placeholderKey, to: pageKey }));
|
|
453
789
|
}
|
|
454
|
-
const meta = buildMeta(pageKey, json, superglue, rsp, fetchArgs);
|
|
455
790
|
const visitMeta = {
|
|
456
791
|
...meta,
|
|
457
792
|
navigationAction: calculateNavAction(
|
|
458
793
|
meta,
|
|
459
794
|
rsp,
|
|
795
|
+
json,
|
|
460
796
|
isGet,
|
|
461
797
|
pageKey,
|
|
462
798
|
currentPageKey,
|
|
463
799
|
revisit
|
|
464
800
|
)
|
|
465
801
|
};
|
|
466
|
-
|
|
802
|
+
dispatch(
|
|
803
|
+
receiveResponse({
|
|
804
|
+
pageKey,
|
|
805
|
+
response: JSON.parse(JSON.stringify(json))
|
|
806
|
+
})
|
|
807
|
+
);
|
|
808
|
+
const existingPage = createProxy(
|
|
809
|
+
pages[pageKey],
|
|
810
|
+
{ current: fragments },
|
|
811
|
+
/* @__PURE__ */ new Set(),
|
|
812
|
+
/* @__PURE__ */ new WeakMap()
|
|
813
|
+
);
|
|
814
|
+
let page = json;
|
|
815
|
+
if (json.action === "savePage" || json.action === "graft") {
|
|
816
|
+
page = JSON.parse(
|
|
817
|
+
JSON.stringify(beforeSave(existingPage, json))
|
|
818
|
+
);
|
|
819
|
+
}
|
|
467
820
|
return dispatch(saveAndProcessPage(pageKey, page)).then(() => visitMeta);
|
|
468
821
|
}).catch((e) => handleFetchErr(e, fetchArgs, dispatch));
|
|
469
822
|
};
|
|
470
823
|
};
|
|
471
|
-
function calculateNavAction(meta, rsp, isGet, pageKey, currentPageKey, revisit) {
|
|
824
|
+
function calculateNavAction(meta, rsp, json, isGet, pageKey, currentPageKey, revisit) {
|
|
472
825
|
let navigationAction = "push";
|
|
826
|
+
if (json.action === "handleStreamResponse") {
|
|
827
|
+
return "none";
|
|
828
|
+
}
|
|
473
829
|
if (!rsp.redirected && !isGet) {
|
|
474
830
|
navigationAction = "replace";
|
|
475
831
|
}
|
|
@@ -498,6 +854,113 @@ function calculatePageKey(rsp, isGet, currentPageKey) {
|
|
|
498
854
|
return pageKey;
|
|
499
855
|
}
|
|
500
856
|
|
|
857
|
+
// lib/action_creators/stream.ts
|
|
858
|
+
var streamPrepend = (fragments, data, options = {}) => {
|
|
859
|
+
return (dispatch) => {
|
|
860
|
+
if (options.saveAs) {
|
|
861
|
+
const { saveAs } = options;
|
|
862
|
+
dispatch(
|
|
863
|
+
saveFragment({
|
|
864
|
+
fragmentId: saveAs,
|
|
865
|
+
data
|
|
866
|
+
})
|
|
867
|
+
);
|
|
868
|
+
fragments.forEach((fragmentId) => {
|
|
869
|
+
dispatch(
|
|
870
|
+
prependToFragment({
|
|
871
|
+
fragmentId,
|
|
872
|
+
data: {
|
|
873
|
+
__id: saveAs
|
|
874
|
+
}
|
|
875
|
+
})
|
|
876
|
+
);
|
|
877
|
+
});
|
|
878
|
+
} else {
|
|
879
|
+
fragments.forEach((fragmentId) => {
|
|
880
|
+
dispatch(
|
|
881
|
+
prependToFragment({
|
|
882
|
+
fragmentId,
|
|
883
|
+
data
|
|
884
|
+
})
|
|
885
|
+
);
|
|
886
|
+
});
|
|
887
|
+
}
|
|
888
|
+
};
|
|
889
|
+
};
|
|
890
|
+
var streamAppend = (fragments, data, options = {}) => {
|
|
891
|
+
return (dispatch) => {
|
|
892
|
+
if (options.saveAs) {
|
|
893
|
+
const { saveAs } = options;
|
|
894
|
+
dispatch(
|
|
895
|
+
saveFragment({
|
|
896
|
+
fragmentId: saveAs,
|
|
897
|
+
data
|
|
898
|
+
})
|
|
899
|
+
);
|
|
900
|
+
fragments.forEach((fragmentId) => {
|
|
901
|
+
dispatch(
|
|
902
|
+
appendToFragment({
|
|
903
|
+
fragmentId,
|
|
904
|
+
data: {
|
|
905
|
+
__id: saveAs
|
|
906
|
+
}
|
|
907
|
+
})
|
|
908
|
+
);
|
|
909
|
+
});
|
|
910
|
+
} else {
|
|
911
|
+
fragments.forEach((fragmentId) => {
|
|
912
|
+
dispatch(
|
|
913
|
+
appendToFragment({
|
|
914
|
+
fragmentId,
|
|
915
|
+
data
|
|
916
|
+
})
|
|
917
|
+
);
|
|
918
|
+
});
|
|
919
|
+
}
|
|
920
|
+
};
|
|
921
|
+
};
|
|
922
|
+
var streamSave = (fragment, data) => {
|
|
923
|
+
return (dispatch) => {
|
|
924
|
+
dispatch(
|
|
925
|
+
saveFragment({
|
|
926
|
+
fragmentId: fragment,
|
|
927
|
+
data
|
|
928
|
+
})
|
|
929
|
+
);
|
|
930
|
+
};
|
|
931
|
+
};
|
|
932
|
+
var handleStreamResponse = (response) => {
|
|
933
|
+
return (dispatch) => {
|
|
934
|
+
let nextResponse = response;
|
|
935
|
+
nextResponse.fragments.reverse().forEach((fragment) => {
|
|
936
|
+
const { id, path } = fragment;
|
|
937
|
+
const node = getIn(nextResponse, path);
|
|
938
|
+
nextResponse = setIn(nextResponse, path, { __id: id });
|
|
939
|
+
dispatch(
|
|
940
|
+
saveFragment({
|
|
941
|
+
fragmentId: id,
|
|
942
|
+
data: node
|
|
943
|
+
})
|
|
944
|
+
);
|
|
945
|
+
});
|
|
946
|
+
nextResponse.data.forEach((message) => {
|
|
947
|
+
if (message.handler === "append") {
|
|
948
|
+
dispatch(
|
|
949
|
+
streamAppend(message.fragmentIds, message.data, message.options)
|
|
950
|
+
);
|
|
951
|
+
}
|
|
952
|
+
if (message.handler === "prepend") {
|
|
953
|
+
dispatch(
|
|
954
|
+
streamPrepend(message.fragmentIds, message.data, message.options)
|
|
955
|
+
);
|
|
956
|
+
}
|
|
957
|
+
if (message.handler === "save") {
|
|
958
|
+
dispatch(streamSave(message.fragmentIds[0], message.data));
|
|
959
|
+
}
|
|
960
|
+
});
|
|
961
|
+
};
|
|
962
|
+
};
|
|
963
|
+
|
|
501
964
|
// lib/action_creators/index.ts
|
|
502
965
|
function fetchDeferments(pageKey, defers = []) {
|
|
503
966
|
pageKey = urlToPageKey(pageKey);
|
|
@@ -531,58 +994,64 @@ function fetchDeferments(pageKey, defers = []) {
|
|
|
531
994
|
return Promise.all(fetches);
|
|
532
995
|
};
|
|
533
996
|
}
|
|
997
|
+
function addPlaceholdersToDeferredNodes(existingPage, page) {
|
|
998
|
+
const { defers = [] } = existingPage;
|
|
999
|
+
const prevDefers = defers.map(({ path }) => {
|
|
1000
|
+
const node = getIn(existingPage, path);
|
|
1001
|
+
const copy = JSON.stringify(node);
|
|
1002
|
+
return [path, JSON.parse(copy)];
|
|
1003
|
+
});
|
|
1004
|
+
return prevDefers.reduce((memo, [path, node]) => {
|
|
1005
|
+
return setIn(page, path, node);
|
|
1006
|
+
}, page);
|
|
1007
|
+
}
|
|
534
1008
|
function saveAndProcessPage(pageKey, page) {
|
|
535
1009
|
return (dispatch, getState) => {
|
|
536
1010
|
pageKey = urlToPageKey(pageKey);
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
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);
|
|
1011
|
+
let nextPage = page;
|
|
1012
|
+
const state = getState();
|
|
1013
|
+
if (page.action === "savePage" && state.pages[pageKey]) {
|
|
1014
|
+
const existingPage = createProxy(
|
|
1015
|
+
state.pages[pageKey],
|
|
1016
|
+
{ current: state.fragments },
|
|
1017
|
+
/* @__PURE__ */ new Set(),
|
|
1018
|
+
/* @__PURE__ */ new WeakMap()
|
|
1019
|
+
);
|
|
1020
|
+
nextPage = JSON.parse(
|
|
1021
|
+
JSON.stringify(addPlaceholdersToDeferredNodes(existingPage, nextPage))
|
|
1022
|
+
);
|
|
1023
|
+
}
|
|
1024
|
+
page.fragments.slice().reverse().forEach((fragment) => {
|
|
1025
|
+
const { id, path } = fragment;
|
|
1026
|
+
const node = getIn(nextPage, path);
|
|
1027
|
+
nextPage = setIn(nextPage, path, { __id: id });
|
|
1028
|
+
dispatch(
|
|
1029
|
+
saveFragment({
|
|
1030
|
+
fragmentId: id,
|
|
1031
|
+
data: node
|
|
1032
|
+
})
|
|
1033
|
+
);
|
|
1034
|
+
});
|
|
1035
|
+
if (nextPage.action === "graft") {
|
|
1036
|
+
if (typeof nextPage.fragmentContext === "string") {
|
|
573
1037
|
dispatch(
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
value: currentFragment,
|
|
578
|
-
path
|
|
1038
|
+
handleFragmentGraft({
|
|
1039
|
+
fragmentId: nextPage.fragmentContext,
|
|
1040
|
+
response: nextPage
|
|
579
1041
|
})
|
|
580
1042
|
);
|
|
581
|
-
}
|
|
1043
|
+
} else {
|
|
1044
|
+
dispatch(handleGraft({ pageKey, page: nextPage }));
|
|
1045
|
+
}
|
|
1046
|
+
} else if (nextPage.action === "handleStreamResponse") {
|
|
1047
|
+
dispatch(handleStreamResponse(nextPage));
|
|
1048
|
+
return Promise.resolve();
|
|
1049
|
+
} else {
|
|
1050
|
+
dispatch(saveResponse({ pageKey, page: nextPage }));
|
|
582
1051
|
}
|
|
583
1052
|
const hasFetch = typeof fetch != "undefined";
|
|
584
1053
|
if (hasFetch) {
|
|
585
|
-
return dispatch(fetchDeferments(pageKey, defers)).then(
|
|
1054
|
+
return dispatch(fetchDeferments(pageKey, nextPage.defers)).then(
|
|
586
1055
|
() => Promise.resolve()
|
|
587
1056
|
);
|
|
588
1057
|
} else {
|