@learncard/learn-cloud-plugin 2.3.51 → 2.3.52

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.
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAc5C,OAAO,EAAE,gCAAgC,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAS7E,cAAc,SAAS,CAAC;AAqCxB;;GAEG;AACH,eAAO,MAAM,mBAAmB,GAC5B,kBAAkB,SAAS,CAAC,GAAG,EAAE,IAAI,EAAE,gCAAgC,CAAC,EACxE,KAAK,MAAM,EACX,oBAAmB,MAAM,EAAO,EAChC,0BAAyB,MAAM,EAAO,EACtC,oCAAiC,KAClC,OAAO,CAAC,gBAAgB,CAssB1B,CAAC"}
1
+ {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAc5C,OAAO,EAAE,gCAAgC,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAS7E,cAAc,SAAS,CAAC;AAqCxB;;GAEG;AACH,eAAO,MAAM,mBAAmB,GAC5B,kBAAkB,SAAS,CAAC,GAAG,EAAE,IAAI,EAAE,gCAAgC,CAAC,EACxE,KAAK,MAAM,EACX,oBAAmB,MAAM,EAAO,EAChC,0BAAyB,MAAM,EAAO,EACtC,oCAAiC,KAClC,OAAO,CAAC,gBAAgB,CA+sB1B,CAAC"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@learncard/learn-cloud-plugin",
3
- "version": "2.3.51",
4
- "description": "",
3
+ "version": "2.3.52",
4
+ "description": "Stores, indexes, and syncs encrypted credentials in LearnCloud.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
7
7
  "module": "./dist/learn-cloud-plugin.esm.js",
@@ -39,8 +39,8 @@
39
39
  "url": "https://github.com/learningeconomy/LearnCard/issues"
40
40
  },
41
41
  "devDependencies": {
42
- "@learncard/learn-cloud-service": "^2.5.43",
43
- "@learncard/types": "5.24.2",
42
+ "@learncard/learn-cloud-service": "^2.5.44",
43
+ "@learncard/types": "6.0.0",
44
44
  "@types/lodash": "^4.14.191",
45
45
  "@types/node": "^17.0.31",
46
46
  "aqu": "0.4.3",
@@ -50,10 +50,10 @@
50
50
  },
51
51
  "types": "./dist/index.d.ts",
52
52
  "dependencies": {
53
- "@learncard/core": "9.4.43",
54
- "@learncard/didkit-plugin": "1.10.9",
55
- "@learncard/helpers": "^1.7.3",
56
- "@learncard/learn-cloud-client": "1.6.51",
53
+ "@learncard/core": "9.4.44",
54
+ "@learncard/didkit-plugin": "1.10.10",
55
+ "@learncard/helpers": "^1.8.0",
56
+ "@learncard/learn-cloud-client": "1.6.52",
57
57
  "json-stringify-deterministic": "^1.0.8",
58
58
  "lodash": "^4.17.21",
59
59
  "pbkdf2-hmac": "^1.2.1"
package/src/plugin.ts CHANGED
@@ -89,7 +89,7 @@ export const getLearnCloudPlugin = async (
89
89
  const otherClients = new Map<string, Promise<LearnCloudClient>>();
90
90
  const learnCloudDids = new WeakMap<LearnCloudClient, Promise<string>>();
91
91
 
92
- const getLearnCloudDid = (currentClient: LearnCloudClient): Promise<string> => {
92
+ const getLearnCloudDid = async (currentClient: LearnCloudClient): Promise<string> => {
93
93
  let learnCloudDid = learnCloudDids.get(currentClient);
94
94
 
95
95
  if (!learnCloudDid) {
@@ -97,7 +97,14 @@ export const getLearnCloudPlugin = async (
97
97
  learnCloudDids.set(currentClient, learnCloudDid);
98
98
  }
99
99
 
100
- return learnCloudDid;
100
+ try {
101
+ return await learnCloudDid;
102
+ } catch (error) {
103
+ if (learnCloudDids.get(currentClient) === learnCloudDid) {
104
+ learnCloudDids.delete(currentClient);
105
+ }
106
+ throw error;
107
+ }
101
108
  };
102
109
 
103
110
  const getOtherClient = async (
@@ -568,6 +575,8 @@ export const getLearnCloudPlugin = async (
568
575
 
569
576
  const options = {
570
577
  ...paginationOptions,
578
+ // JSON-persisted infinite-query page parameters may restore as null.
579
+ cursor: paginationOptions?.cursor ?? undefined,
571
580
  sort: ['newestFirst', 'oldestFirst'].includes(paginationOptions?.sort ?? '')
572
581
  ? (paginationOptions?.sort as 'newestFirst' | 'oldestFirst')
573
582
  : undefined,
@@ -15,6 +15,9 @@ const makeMockClient = (learnCloudDid = 'did:key:z6MkLearnCloud') => ({
15
15
  storage: {
16
16
  batchResolve: { query: vi.fn() },
17
17
  },
18
+ index: {
19
+ get: { query: vi.fn().mockResolvedValue({ records: [], hasMore: false }) },
20
+ },
18
21
  });
19
22
 
20
23
  const mockClient = makeMockClient();
@@ -58,6 +61,27 @@ describe('LearnCloud Plugin', () => {
58
61
  mockGetClient.mockResolvedValue(mockClient);
59
62
  });
60
63
 
64
+ it.each([undefined, { category: 'Achievement' }])(
65
+ 'normalizes a restored null cursor before querying the index with %j',
66
+ async query => {
67
+ const learnCard = makeLearnCard();
68
+ const plugin = await getLearnCloudPlugin(
69
+ learnCard as never,
70
+ 'https://cloud.example',
71
+ [],
72
+ [],
73
+ false
74
+ );
75
+ // Persisted infinite-query page parameters can restore undefined as null.
76
+ const pagination = JSON.parse('{"cursor":null,"limit":12}');
77
+ await plugin.index.getPage!(learnCard as never, query, pagination);
78
+
79
+ expect(mockClient.index.get.query).toHaveBeenCalledWith(
80
+ expect.objectContaining({ cursor: undefined, limit: 12 })
81
+ );
82
+ }
83
+ );
84
+
61
85
  it('defers remote identity requests until a method needs them', async () => {
62
86
  await getLearnCloudPlugin(makeLearnCard() as never, 'https://cloud.example');
63
87
 
@@ -65,6 +89,39 @@ describe('LearnCloud Plugin', () => {
65
89
  expect(mockClient.utilities.getDid.query).not.toHaveBeenCalled();
66
90
  });
67
91
 
92
+ it('shares discovery in flight, but allows an index read to recover after discovery fails', async () => {
93
+ let rejectDiscovery!: (reason: Error) => void;
94
+ const discovery = new Promise<string>((_resolve, reject) => {
95
+ rejectDiscovery = reject;
96
+ });
97
+ const getDid = vi
98
+ .fn()
99
+ .mockReturnValueOnce(discovery)
100
+ .mockResolvedValue('did:key:z6MkLearnCloud');
101
+ const getPage = vi.fn().mockResolvedValue({ records: [], hasMore: false });
102
+ mockGetClient.mockResolvedValue({
103
+ ...mockClient,
104
+ utilities: { getDid: { query: getDid } },
105
+ index: { get: { query: getPage } },
106
+ });
107
+ const learnCard = makeLearnCard();
108
+ const plugin = await getLearnCloudPlugin(learnCard as never, 'https://cloud.example');
109
+ const first = plugin.index!.getPage!(learnCard as never, { category: 'Social Badge' });
110
+ const second = plugin.index!.getPage!(learnCard as never, { category: 'Achievement' });
111
+ const failures = Promise.allSettled([first, second]);
112
+ await vi.waitFor(() => expect(getDid).toHaveBeenCalledTimes(1));
113
+ rejectDiscovery(new Error('Temporarily offline'));
114
+ expect((await failures).map(result => result.status)).toEqual(['rejected', 'rejected']);
115
+ await expect(
116
+ plugin.index!.getPage!(learnCard as never, { category: 'Social Badge' })
117
+ ).resolves.toEqual({ records: [], hasMore: false });
118
+ await expect(
119
+ plugin.index!.getPage!(learnCard as never, { category: 'Achievement' })
120
+ ).resolves.toEqual({ records: [], hasMore: false });
121
+ expect(getDid).toHaveBeenCalledTimes(2);
122
+ expect(getPage).toHaveBeenCalledTimes(2);
123
+ });
124
+
68
125
  it('does not load associated DIDs when automatic association is disabled', async () => {
69
126
  const learnCard = makeLearnCard();
70
127
  const plugin = await getLearnCloudPlugin(