@khanacademy/wonder-blocks-data 5.0.1 → 6.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.
Files changed (85) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/dist/es/index.js +771 -372
  3. package/dist/index.js +1191 -550
  4. package/legacy-docs.md +3 -0
  5. package/package.json +2 -2
  6. package/src/__docs__/_overview_.stories.mdx +18 -0
  7. package/src/__docs__/_overview_graphql.stories.mdx +35 -0
  8. package/src/__docs__/_overview_ssr_.stories.mdx +185 -0
  9. package/src/__docs__/_overview_testing_.stories.mdx +123 -0
  10. package/src/__docs__/exports.clear-shared-cache.stories.mdx +20 -0
  11. package/src/__docs__/exports.data-error.stories.mdx +23 -0
  12. package/src/__docs__/exports.data-errors.stories.mdx +23 -0
  13. package/src/{components/data.md → __docs__/exports.data.stories.mdx} +15 -18
  14. package/src/__docs__/exports.fulfill-all-data-requests.stories.mdx +24 -0
  15. package/src/__docs__/exports.gql-error.stories.mdx +23 -0
  16. package/src/__docs__/exports.gql-errors.stories.mdx +20 -0
  17. package/src/__docs__/exports.gql-router.stories.mdx +29 -0
  18. package/src/__docs__/exports.has-unfulfilled-requests.stories.mdx +20 -0
  19. package/src/{components/intercept-requests.md → __docs__/exports.intercept-requests.stories.mdx} +16 -1
  20. package/src/__docs__/exports.intialize-cache.stories.mdx +29 -0
  21. package/src/__docs__/exports.remove-all-from-cache.stories.mdx +24 -0
  22. package/src/__docs__/exports.remove-from-cache.stories.mdx +25 -0
  23. package/src/__docs__/exports.request-fulfillment.stories.mdx +36 -0
  24. package/src/__docs__/exports.scoped-in-memory-cache.stories.mdx +92 -0
  25. package/src/__docs__/exports.serializable-in-memory-cache.stories.mdx +112 -0
  26. package/src/__docs__/exports.status.stories.mdx +31 -0
  27. package/src/{components/track-data.md → __docs__/exports.track-data.stories.mdx} +15 -0
  28. package/src/__docs__/exports.use-cached-effect.stories.mdx +41 -0
  29. package/src/__docs__/exports.use-gql.stories.mdx +73 -0
  30. package/src/__docs__/exports.use-hydratable-effect.stories.mdx +43 -0
  31. package/src/__docs__/exports.use-server-effect.stories.mdx +38 -0
  32. package/src/__docs__/exports.use-shared-cache.stories.mdx +30 -0
  33. package/src/__docs__/exports.when-client-side.stories.mdx +33 -0
  34. package/src/__docs__/types.cached-response.stories.mdx +29 -0
  35. package/src/__docs__/types.error-options.stories.mdx +21 -0
  36. package/src/__docs__/types.gql-context.stories.mdx +20 -0
  37. package/src/__docs__/types.gql-fetch-fn.stories.mdx +24 -0
  38. package/src/__docs__/types.gql-fetch-options.stories.mdx +24 -0
  39. package/src/__docs__/types.gql-operation-type.stories.mdx +24 -0
  40. package/src/__docs__/types.gql-operation.stories.mdx +67 -0
  41. package/src/__docs__/types.response-cache.stories.mdx +33 -0
  42. package/src/__docs__/types.result.stories.mdx +39 -0
  43. package/src/__docs__/types.scoped-cache.stories.mdx +27 -0
  44. package/src/__docs__/types.valid-cache-data.stories.mdx +23 -0
  45. package/src/__tests__/__snapshots__/generated-snapshot.test.js.snap +0 -80
  46. package/src/__tests__/generated-snapshot.test.js +0 -24
  47. package/src/components/__tests__/data.test.js +149 -128
  48. package/src/components/data.js +22 -112
  49. package/src/components/intercept-requests.js +1 -1
  50. package/src/hooks/__tests__/__snapshots__/use-shared-cache.test.js.snap +8 -8
  51. package/src/hooks/__tests__/use-cached-effect.test.js +507 -0
  52. package/src/hooks/__tests__/use-gql-router-context.test.js +133 -0
  53. package/src/hooks/__tests__/use-gql.test.js +1 -30
  54. package/src/hooks/__tests__/use-hydratable-effect.test.js +708 -0
  55. package/src/hooks/__tests__/use-server-effect.test.js +39 -11
  56. package/src/hooks/use-cached-effect.js +225 -0
  57. package/src/hooks/use-gql-router-context.js +50 -0
  58. package/src/hooks/use-gql.js +22 -52
  59. package/src/hooks/use-hydratable-effect.js +206 -0
  60. package/src/hooks/use-request-interception.js +20 -23
  61. package/src/hooks/use-server-effect.js +12 -5
  62. package/src/hooks/use-shared-cache.js +13 -11
  63. package/src/index.js +53 -3
  64. package/src/util/__tests__/__snapshots__/serializable-in-memory-cache.test.js.snap +19 -0
  65. package/src/util/__tests__/merge-gql-context.test.js +74 -0
  66. package/src/util/__tests__/request-fulfillment.test.js +23 -42
  67. package/src/util/__tests__/request-tracking.test.js +26 -7
  68. package/src/util/__tests__/result-from-cache-response.test.js +19 -5
  69. package/src/util/__tests__/scoped-in-memory-cache.test.js +6 -85
  70. package/src/util/__tests__/serializable-in-memory-cache.test.js +398 -0
  71. package/src/util/__tests__/ssr-cache.test.js +52 -52
  72. package/src/util/abort-error.js +15 -0
  73. package/src/util/data-error.js +58 -0
  74. package/src/util/get-gql-data-from-response.js +3 -2
  75. package/src/util/gql-error.js +19 -11
  76. package/src/util/merge-gql-context.js +34 -0
  77. package/src/util/request-fulfillment.js +49 -46
  78. package/src/util/request-tracking.js +69 -15
  79. package/src/util/result-from-cache-response.js +12 -16
  80. package/src/util/scoped-in-memory-cache.js +24 -47
  81. package/src/util/serializable-in-memory-cache.js +49 -0
  82. package/src/util/ssr-cache.js +9 -8
  83. package/src/util/status.js +30 -0
  84. package/src/util/types.js +18 -1
  85. package/docs.md +0 -122
@@ -1,7 +1,7 @@
1
1
  // @flow
2
2
  import {Server} from "@khanacademy/wonder-blocks-core";
3
3
  import {SsrCache} from "../ssr-cache.js";
4
- import {ScopedInMemoryCache} from "../scoped-in-memory-cache.js";
4
+ import {SerializableInMemoryCache} from "../serializable-in-memory-cache.js";
5
5
 
6
6
  describe("../ssr-cache.js", () => {
7
7
  afterEach(() => {
@@ -56,7 +56,7 @@ describe("../ssr-cache.js", () => {
56
56
 
57
57
  it("should throw if the cache is already intialized", () => {
58
58
  // Arrange
59
- const internalCache = new ScopedInMemoryCache({
59
+ const internalCache = new SerializableInMemoryCache({
60
60
  MY_KEY: {data: "THE_DATA"},
61
61
  });
62
62
  const cache = new SsrCache(internalCache);
@@ -95,7 +95,7 @@ describe("../ssr-cache.js", () => {
95
95
  describe("when client-side", () => {
96
96
  it("should not store the entry in the hydration cache", () => {
97
97
  // Arrange
98
- const hydrationCache = new ScopedInMemoryCache();
98
+ const hydrationCache = new SerializableInMemoryCache();
99
99
  const cache = new SsrCache(hydrationCache);
100
100
  const hydrationStoreSpy = jest.spyOn(hydrationCache, "set");
101
101
 
@@ -108,8 +108,8 @@ describe("../ssr-cache.js", () => {
108
108
 
109
109
  it("should not store the entry in the ssrOnly cache", () => {
110
110
  // Arrange
111
- const hydrationCache = new ScopedInMemoryCache();
112
- const ssrOnlyCache = new ScopedInMemoryCache();
111
+ const hydrationCache = new SerializableInMemoryCache();
112
+ const ssrOnlyCache = new SerializableInMemoryCache();
113
113
  const cache = new SsrCache(hydrationCache, ssrOnlyCache);
114
114
  const ssrOnlyStoreSpy = jest.spyOn(ssrOnlyCache, "set");
115
115
 
@@ -129,7 +129,7 @@ describe("../ssr-cache.js", () => {
129
129
  describe("when hydrate is true", () => {
130
130
  it("should store the entry in the hydration cache", () => {
131
131
  // Arrange
132
- const hydrationCache = new ScopedInMemoryCache();
132
+ const hydrationCache = new SerializableInMemoryCache();
133
133
  const cache = new SsrCache(hydrationCache);
134
134
  const hydrationStoreSpy = jest.spyOn(hydrationCache, "set");
135
135
 
@@ -148,8 +148,8 @@ describe("../ssr-cache.js", () => {
148
148
 
149
149
  it("should not store the entry in the ssrOnly cache", () => {
150
150
  // Arrange
151
- const hydrationCache = new ScopedInMemoryCache();
152
- const ssrOnlyCache = new ScopedInMemoryCache();
151
+ const hydrationCache = new SerializableInMemoryCache();
152
+ const ssrOnlyCache = new SerializableInMemoryCache();
153
153
  const cache = new SsrCache(hydrationCache, ssrOnlyCache);
154
154
  const ssrOnlyStoreSpy = jest.spyOn(ssrOnlyCache, "set");
155
155
 
@@ -164,8 +164,8 @@ describe("../ssr-cache.js", () => {
164
164
  describe("when hydrate is false", () => {
165
165
  it("should store the entry in the ssr-only cache", () => {
166
166
  // Arrange
167
- const hydrationCache = new ScopedInMemoryCache();
168
- const ssrOnlyCache = new ScopedInMemoryCache();
167
+ const hydrationCache = new SerializableInMemoryCache();
168
+ const ssrOnlyCache = new SerializableInMemoryCache();
169
169
  const cache = new SsrCache(hydrationCache, ssrOnlyCache);
170
170
  const ssrOnlyStoreSpy = jest.spyOn(ssrOnlyCache, "set");
171
171
 
@@ -184,8 +184,8 @@ describe("../ssr-cache.js", () => {
184
184
 
185
185
  it("should not store the entry in the hydration cache", () => {
186
186
  // Arrange
187
- const hydrationCache = new ScopedInMemoryCache();
188
- const ssrOnlyCache = new ScopedInMemoryCache();
187
+ const hydrationCache = new SerializableInMemoryCache();
188
+ const ssrOnlyCache = new SerializableInMemoryCache();
189
189
  const cache = new SsrCache(hydrationCache, ssrOnlyCache);
190
190
  const hydrationStoreSpy = jest.spyOn(hydrationCache, "set");
191
191
 
@@ -203,7 +203,7 @@ describe("../ssr-cache.js", () => {
203
203
  describe("when client-side", () => {
204
204
  it("should not store the entry in the hydration cache", () => {
205
205
  // Arrange
206
- const hydrationCache = new ScopedInMemoryCache();
206
+ const hydrationCache = new SerializableInMemoryCache();
207
207
  const cache = new SsrCache(hydrationCache);
208
208
  const hydrationStoreSpy = jest.spyOn(hydrationCache, "set");
209
209
 
@@ -216,8 +216,8 @@ describe("../ssr-cache.js", () => {
216
216
 
217
217
  it("should not store the entry in the ssrOnly cache", () => {
218
218
  // Arrange
219
- const hydrationCache = new ScopedInMemoryCache();
220
- const ssrOnlyCache = new ScopedInMemoryCache();
219
+ const hydrationCache = new SerializableInMemoryCache();
220
+ const ssrOnlyCache = new SerializableInMemoryCache();
221
221
  const cache = new SsrCache(hydrationCache, ssrOnlyCache);
222
222
  const ssrOnlyStoreSpy = jest.spyOn(ssrOnlyCache, "set");
223
223
 
@@ -237,7 +237,7 @@ describe("../ssr-cache.js", () => {
237
237
  describe("when hydrate is true", () => {
238
238
  it("should store the entry in the hydration cache", () => {
239
239
  // Arrange
240
- const hydrationCache = new ScopedInMemoryCache();
240
+ const hydrationCache = new SerializableInMemoryCache();
241
241
  const cache = new SsrCache(hydrationCache);
242
242
  const hydrationStoreSpy = jest.spyOn(hydrationCache, "set");
243
243
 
@@ -256,8 +256,8 @@ describe("../ssr-cache.js", () => {
256
256
 
257
257
  it("should not store the entry in the ssrOnly cache", () => {
258
258
  // Arrange
259
- const hydrationCache = new ScopedInMemoryCache();
260
- const ssrOnlyCache = new ScopedInMemoryCache();
259
+ const hydrationCache = new SerializableInMemoryCache();
260
+ const ssrOnlyCache = new SerializableInMemoryCache();
261
261
  const cache = new SsrCache(hydrationCache, ssrOnlyCache);
262
262
  const ssrOnlyStoreSpy = jest.spyOn(ssrOnlyCache, "set");
263
263
 
@@ -272,8 +272,8 @@ describe("../ssr-cache.js", () => {
272
272
  describe("when hydrate is false", () => {
273
273
  it("should store the entry in the ssr-only cache", () => {
274
274
  // Arrange
275
- const hydrationCache = new ScopedInMemoryCache();
276
- const ssrOnlyCache = new ScopedInMemoryCache();
275
+ const hydrationCache = new SerializableInMemoryCache();
276
+ const ssrOnlyCache = new SerializableInMemoryCache();
277
277
  const cache = new SsrCache(hydrationCache, ssrOnlyCache);
278
278
  const ssrOnlyStoreSpy = jest.spyOn(ssrOnlyCache, "set");
279
279
 
@@ -292,8 +292,8 @@ describe("../ssr-cache.js", () => {
292
292
 
293
293
  it("should not store the entry in the hydration cache", () => {
294
294
  // Arrange
295
- const hydrationCache = new ScopedInMemoryCache();
296
- const ssrOnlyCache = new ScopedInMemoryCache();
295
+ const hydrationCache = new SerializableInMemoryCache();
296
+ const ssrOnlyCache = new SerializableInMemoryCache();
297
297
  const cache = new SsrCache(hydrationCache, ssrOnlyCache);
298
298
  const hydrationStoreSpy = jest.spyOn(hydrationCache, "set");
299
299
 
@@ -315,7 +315,7 @@ describe("../ssr-cache.js", () => {
315
315
 
316
316
  it("should return null if not in the hydration cache", () => {
317
317
  // Arrange
318
- const hydrationCache = new ScopedInMemoryCache();
318
+ const hydrationCache = new SerializableInMemoryCache();
319
319
  jest.spyOn(hydrationCache, "get").mockReturnValue(null);
320
320
  const cache = new SsrCache(hydrationCache);
321
321
 
@@ -328,7 +328,7 @@ describe("../ssr-cache.js", () => {
328
328
 
329
329
  it("should return the cached entry if in the hydration cache", () => {
330
330
  // Arrange
331
- const hydrationCache = new ScopedInMemoryCache();
331
+ const hydrationCache = new SerializableInMemoryCache();
332
332
  jest.spyOn(hydrationCache, "get").mockReturnValue({
333
333
  data: "data!",
334
334
  });
@@ -349,8 +349,8 @@ describe("../ssr-cache.js", () => {
349
349
 
350
350
  it("should return null in any cache", () => {
351
351
  // Arrange
352
- const hydrationCache = new ScopedInMemoryCache();
353
- const ssrOnlyCache = new ScopedInMemoryCache();
352
+ const hydrationCache = new SerializableInMemoryCache();
353
+ const ssrOnlyCache = new SerializableInMemoryCache();
354
354
  const cache = new SsrCache(hydrationCache, ssrOnlyCache);
355
355
 
356
356
  // Act
@@ -362,7 +362,7 @@ describe("../ssr-cache.js", () => {
362
362
 
363
363
  it("should return the cached entry if in the hydration cache", () => {
364
364
  // Arrange
365
- const hydrationCache = new ScopedInMemoryCache();
365
+ const hydrationCache = new SerializableInMemoryCache();
366
366
  jest.spyOn(hydrationCache, "get").mockReturnValue({
367
367
  data: "data!",
368
368
  });
@@ -377,8 +377,8 @@ describe("../ssr-cache.js", () => {
377
377
 
378
378
  it("should return the cached entry if in the ssr-only cache", () => {
379
379
  // Arrange
380
- const hydrationCache = new ScopedInMemoryCache();
381
- const ssrOnlyCache = new ScopedInMemoryCache();
380
+ const hydrationCache = new SerializableInMemoryCache();
381
+ const ssrOnlyCache = new SerializableInMemoryCache();
382
382
  jest.spyOn(ssrOnlyCache, "get").mockReturnValue({
383
383
  data: "data!",
384
384
  });
@@ -396,8 +396,8 @@ describe("../ssr-cache.js", () => {
396
396
  describe("#remove", () => {
397
397
  it("should return false if nothing was removed", () => {
398
398
  // Arrange
399
- const hydrationCache = new ScopedInMemoryCache();
400
- const ssrOnlycache = new ScopedInMemoryCache();
399
+ const hydrationCache = new SerializableInMemoryCache();
400
+ const ssrOnlycache = new SerializableInMemoryCache();
401
401
  jest.spyOn(hydrationCache, "purge").mockReturnValue(false);
402
402
  jest.spyOn(ssrOnlycache, "purge").mockReturnValue(false);
403
403
  const cache = new SsrCache(hydrationCache, ssrOnlycache);
@@ -411,7 +411,7 @@ describe("../ssr-cache.js", () => {
411
411
 
412
412
  it("should return true if something was removed from hydration cache", () => {
413
413
  // Arrange
414
- const hydrationCache = new ScopedInMemoryCache();
414
+ const hydrationCache = new SerializableInMemoryCache();
415
415
  jest.spyOn(hydrationCache, "purge").mockReturnValue(true);
416
416
  const cache = new SsrCache(hydrationCache);
417
417
 
@@ -429,8 +429,8 @@ describe("../ssr-cache.js", () => {
429
429
 
430
430
  it("should return true if something was removed from ssr-only cache", () => {
431
431
  // Arrange
432
- const hydrationCache = new ScopedInMemoryCache();
433
- const ssrOnlyCache = new ScopedInMemoryCache();
432
+ const hydrationCache = new SerializableInMemoryCache();
433
+ const ssrOnlyCache = new SerializableInMemoryCache();
434
434
  jest.spyOn(ssrOnlyCache, "purge").mockReturnValue(true);
435
435
  const cache = new SsrCache(hydrationCache, ssrOnlyCache);
436
436
 
@@ -446,7 +446,7 @@ describe("../ssr-cache.js", () => {
446
446
  describe("#cloneHydratableData", () => {
447
447
  it("should clone the hydration cache", () => {
448
448
  // Arrange
449
- const hydrationCache = new ScopedInMemoryCache();
449
+ const hydrationCache = new SerializableInMemoryCache();
450
450
  const cloneSpy = jest
451
451
  .spyOn(hydrationCache, "clone")
452
452
  .mockReturnValue({
@@ -475,7 +475,7 @@ describe("../ssr-cache.js", () => {
475
475
 
476
476
  it("should remove all entries from the hydration cache when client-side without predicate", () => {
477
477
  // Arrange
478
- const hydrationCache = new ScopedInMemoryCache();
478
+ const hydrationCache = new SerializableInMemoryCache();
479
479
  const purgeAllSpy = jest.spyOn(hydrationCache, "purgeAll");
480
480
  const cache = new SsrCache(hydrationCache);
481
481
 
@@ -488,11 +488,11 @@ describe("../ssr-cache.js", () => {
488
488
 
489
489
  it("should pass a predicate to hydration cache purge if a predicate is passed", () => {
490
490
  // Arrange
491
- const hydrationCache = new ScopedInMemoryCache();
491
+ const hydrationCache = new SerializableInMemoryCache();
492
492
  const purgeAllSpy = jest.spyOn(hydrationCache, "purgeAll");
493
493
  const cache = new SsrCache(
494
494
  hydrationCache,
495
- new ScopedInMemoryCache(),
495
+ new SerializableInMemoryCache(),
496
496
  );
497
497
 
498
498
  // Act
@@ -504,7 +504,7 @@ describe("../ssr-cache.js", () => {
504
504
 
505
505
  it("should pass a predicate to the hydration cache that calls the predicate it was given", () => {
506
506
  // Arrange
507
- const hydrationCache = new ScopedInMemoryCache({
507
+ const hydrationCache = new SerializableInMemoryCache({
508
508
  default: {
509
509
  KEY1: {
510
510
  data: "DATA",
@@ -513,7 +513,7 @@ describe("../ssr-cache.js", () => {
513
513
  });
514
514
  const cache = new SsrCache(
515
515
  hydrationCache,
516
- new ScopedInMemoryCache(),
516
+ new SerializableInMemoryCache(),
517
517
  );
518
518
  const predicate = jest.fn().mockReturnValue(false);
519
519
 
@@ -532,14 +532,14 @@ describe("../ssr-cache.js", () => {
532
532
 
533
533
  it("should remove all entries from hydration cache when server-side without predicate", () => {
534
534
  // Arrange
535
- const hydrationCache = new ScopedInMemoryCache();
535
+ const hydrationCache = new SerializableInMemoryCache();
536
536
  const hydrationPurgeAllSpy = jest.spyOn(
537
537
  hydrationCache,
538
538
  "purgeAll",
539
539
  );
540
540
  const cache = new SsrCache(
541
541
  hydrationCache,
542
- new ScopedInMemoryCache(),
542
+ new SerializableInMemoryCache(),
543
543
  );
544
544
 
545
545
  // Act
@@ -551,10 +551,10 @@ describe("../ssr-cache.js", () => {
551
551
 
552
552
  it("should remove all entries from ssr cache when server-side without predicate", () => {
553
553
  // Arrange
554
- const ssrOnlyCache = new ScopedInMemoryCache();
554
+ const ssrOnlyCache = new SerializableInMemoryCache();
555
555
  const ssrPurgeAllSpy = jest.spyOn(ssrOnlyCache, "purgeAll");
556
556
  const cache = new SsrCache(
557
- new ScopedInMemoryCache(),
557
+ new SerializableInMemoryCache(),
558
558
  ssrOnlyCache,
559
559
  );
560
560
 
@@ -567,11 +567,11 @@ describe("../ssr-cache.js", () => {
567
567
 
568
568
  it("should pass a predicate to hydration cache purge if a predicate is passed", () => {
569
569
  // Arrange
570
- const hydrationCache = new ScopedInMemoryCache();
570
+ const hydrationCache = new SerializableInMemoryCache();
571
571
  const purgeAllSpy = jest.spyOn(hydrationCache, "purgeAll");
572
572
  const cache = new SsrCache(
573
573
  hydrationCache,
574
- new ScopedInMemoryCache(),
574
+ new SerializableInMemoryCache(),
575
575
  );
576
576
 
577
577
  // Act
@@ -583,10 +583,10 @@ describe("../ssr-cache.js", () => {
583
583
 
584
584
  it("should pass a predicate to srr cache purge if a predicate is passed", () => {
585
585
  // Arrange
586
- const ssrOnlyCache = new ScopedInMemoryCache();
586
+ const ssrOnlyCache = new SerializableInMemoryCache();
587
587
  const purgeAllSpy = jest.spyOn(ssrOnlyCache, "purgeAll");
588
588
  const cache = new SsrCache(
589
- new ScopedInMemoryCache(),
589
+ new SerializableInMemoryCache(),
590
590
  ssrOnlyCache,
591
591
  );
592
592
 
@@ -599,10 +599,10 @@ describe("../ssr-cache.js", () => {
599
599
 
600
600
  it("should pass a predicate to the hydration cache that calls the predicate it was given", () => {
601
601
  // Arrange
602
- const hydrationCache = new ScopedInMemoryCache();
602
+ const hydrationCache = new SerializableInMemoryCache();
603
603
  const cache = new SsrCache(
604
604
  hydrationCache,
605
- new ScopedInMemoryCache(),
605
+ new SerializableInMemoryCache(),
606
606
  );
607
607
  cache.cacheData("KEY1", "DATA", true);
608
608
  const predicate = jest.fn().mockReturnValue(false);
@@ -616,9 +616,9 @@ describe("../ssr-cache.js", () => {
616
616
 
617
617
  it("should pass a predicate to the ssr cache that calls the predicate it was given", () => {
618
618
  // Arrange
619
- const ssrOnlyCache = new ScopedInMemoryCache();
619
+ const ssrOnlyCache = new SerializableInMemoryCache();
620
620
  const cache = new SsrCache(
621
- new ScopedInMemoryCache(),
621
+ new SerializableInMemoryCache(),
622
622
  ssrOnlyCache,
623
623
  );
624
624
  cache.cacheData(
@@ -0,0 +1,15 @@
1
+ // @flow
2
+
3
+ /**
4
+ * Simple implementation to represent aborting.
5
+ *
6
+ * Other frameworks may provide this too, so we won't be sharing this with
7
+ * the outside world. It's just a utility for test and internal use whenever
8
+ * we need to represent the concept of aborted things.
9
+ */
10
+ export class AbortError extends Error {
11
+ constructor(message: string) {
12
+ super(message);
13
+ this.name = "AbortError";
14
+ }
15
+ }
@@ -0,0 +1,58 @@
1
+ // @flow
2
+ import {KindError} from "@khanacademy/wonder-stuff-core";
3
+ import type {ErrorOptions} from "./types.js";
4
+
5
+ /**
6
+ * Error kinds for DataError.
7
+ */
8
+ export const DataErrors = Object.freeze({
9
+ /**
10
+ * The kind of error is not known.
11
+ */
12
+ Unknown: "Unknown",
13
+
14
+ /**
15
+ * The error is internal to the executing code.
16
+ */
17
+ Internal: "Internal",
18
+
19
+ /**
20
+ * There was a problem with the provided input.
21
+ */
22
+ InvalidInput: "InvalidInput",
23
+
24
+ /**
25
+ * A network error occurred.
26
+ */
27
+ Network: "Network",
28
+
29
+ /**
30
+ * Response could not be parsed.
31
+ */
32
+ Parse: "Parse",
33
+
34
+ /**
35
+ * An error that occurred during SSR and was hydrated from cache
36
+ */
37
+ Hydrated: "Hydrated",
38
+ });
39
+
40
+ /**
41
+ * An error from the Wonder Blocks Data API.
42
+ *
43
+ * Errors of this type will have names of the format:
44
+ * `${kind}DataError`
45
+ */
46
+ export class DataError extends KindError {
47
+ constructor(
48
+ message: string,
49
+ kind: $Values<typeof DataErrors>,
50
+ {metadata, cause}: ErrorOptions = ({}: $Shape<ErrorOptions>),
51
+ ) {
52
+ super(message, kind, {
53
+ metadata,
54
+ cause,
55
+ name: "Data",
56
+ });
57
+ }
58
+ }
@@ -1,4 +1,5 @@
1
1
  // @flow
2
+ import {DataError, DataErrors} from "./data-error.js";
2
3
  import {GqlError, GqlErrors} from "./gql-error.js";
3
4
 
4
5
  /**
@@ -14,7 +15,7 @@ export const getGqlDataFromResponse = async <TData>(
14
15
  try {
15
16
  result = JSON.parse(bodyText);
16
17
  } catch (e) {
17
- throw new GqlError("Failed to parse response", GqlErrors.Parse, {
18
+ throw new DataError("Failed to parse response", DataErrors.Parse, {
18
19
  metadata: {
19
20
  statusCode: response.status,
20
21
  bodyText,
@@ -25,7 +26,7 @@ export const getGqlDataFromResponse = async <TData>(
25
26
 
26
27
  // Check for a bad status code.
27
28
  if (response.status >= 300) {
28
- throw new GqlError("Response unsuccessful", GqlErrors.Network, {
29
+ throw new DataError("Response unsuccessful", DataErrors.Network, {
29
30
  metadata: {
30
31
  statusCode: response.status,
31
32
  result,
@@ -1,36 +1,44 @@
1
1
  // @flow
2
- import {KindError, Errors} from "@khanacademy/wonder-stuff-core";
3
- import type {Metadata} from "@khanacademy/wonder-stuff-core";
2
+ import {KindError} from "@khanacademy/wonder-stuff-core";
4
3
 
5
- type GqlErrorOptions = {|
6
- metadata?: ?Metadata,
7
- cause?: ?Error,
8
- |};
4
+ import type {ErrorOptions} from "./types.js";
9
5
 
10
6
  /**
11
7
  * Error kinds for GqlError.
12
8
  */
13
9
  export const GqlErrors = Object.freeze({
14
- ...Errors,
15
- Network: "Network",
16
- Parse: "Parse",
10
+ /**
11
+ * An internal framework error.
12
+ */
13
+ Internal: "Internal",
14
+
15
+ /**
16
+ * Response does not have the correct structure for a GraphQL response.
17
+ */
17
18
  BadResponse: "BadResponse",
19
+
20
+ /**
21
+ * A valid GraphQL result with errors field in the payload.
22
+ */
18
23
  ErrorResult: "ErrorResult",
19
24
  });
20
25
 
21
26
  /**
22
27
  * An error from the GQL API.
28
+ *
29
+ * Errors of this type will have names of the format:
30
+ * `${kind}GqlError`
23
31
  */
24
32
  export class GqlError extends KindError {
25
33
  constructor(
26
34
  message: string,
27
35
  kind: $Values<typeof GqlErrors>,
28
- {metadata, cause}: GqlErrorOptions = ({}: $Shape<GqlErrorOptions>),
36
+ {metadata, cause}: ErrorOptions = ({}: $Shape<ErrorOptions>),
29
37
  ) {
30
38
  super(message, kind, {
31
39
  metadata,
32
40
  cause,
33
- prefix: "Gql",
41
+ name: "Gql",
34
42
  });
35
43
  }
36
44
  }
@@ -0,0 +1,34 @@
1
+ // @flow
2
+ import type {GqlContext} from "./gql-types.js";
3
+
4
+ /**
5
+ * Construct a complete GqlContext from current defaults and a partial context.
6
+ *
7
+ * Values in the partial context that are `undefined` will be ignored.
8
+ * Values in the partial context that are `null` will be deleted.
9
+ */
10
+ export const mergeGqlContext = <TContext: GqlContext>(
11
+ defaultContext: TContext,
12
+ overrides: Partial<TContext>,
13
+ ): TContext => {
14
+ // Let's merge the partial context default context. We deliberately
15
+ // don't spread because spreading would overwrite default context
16
+ // values with undefined or null if the partial context includes a value
17
+ // explicitly set to undefined or null.
18
+ return Object.keys(overrides).reduce(
19
+ (acc, key) => {
20
+ // Undefined values are ignored.
21
+ if (overrides[key] !== undefined) {
22
+ if (overrides[key] === null) {
23
+ // Null indicates we delete this context value.
24
+ delete acc[key];
25
+ } else {
26
+ // Otherwise, we set it.
27
+ acc[key] = overrides[key];
28
+ }
29
+ }
30
+ return acc;
31
+ },
32
+ {...defaultContext},
33
+ );
34
+ };