@learncard/learn-cloud-plugin 2.3.29 → 2.3.31
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/README.md +11 -6
- package/dist/learn-cloud-plugin.cjs.development.cjs +117 -102
- package/dist/learn-cloud-plugin.cjs.development.cjs.map +3 -3
- package/dist/learn-cloud-plugin.cjs.production.min.cjs +42 -42
- package/dist/learn-cloud-plugin.cjs.production.min.cjs.map +4 -4
- package/dist/learn-cloud-plugin.esm.js +117 -102
- package/dist/learn-cloud-plugin.esm.js.map +3 -3
- package/package.json +65 -63
- package/src/helpers.ts +87 -0
- package/src/index.ts +2 -0
- package/src/plugin.ts +711 -0
- package/src/test/index.test.ts +84 -0
- package/src/test/mocks/sample-vcs.ts +579 -0
- package/src/types.ts +69 -0
package/src/plugin.ts
ADDED
|
@@ -0,0 +1,711 @@
|
|
|
1
|
+
import { chunk } from 'lodash';
|
|
2
|
+
import { getClient, LearnCloudClient } from '@learncard/learn-cloud-client';
|
|
3
|
+
import { LearnCard } from '@learncard/core';
|
|
4
|
+
import { isEncrypted, resolveStorageReadResult } from '@learncard/helpers';
|
|
5
|
+
import {
|
|
6
|
+
CredentialRecord,
|
|
7
|
+
JWE,
|
|
8
|
+
PaginatedEncryptedRecordsType,
|
|
9
|
+
PaginatedEncryptedCredentialRecordsType,
|
|
10
|
+
StoredCredentialEnvelope,
|
|
11
|
+
StoredCredentialEnvelopeValidator,
|
|
12
|
+
VCValidator,
|
|
13
|
+
VPValidator,
|
|
14
|
+
isStoredCredentialEnvelope,
|
|
15
|
+
} from '@learncard/types';
|
|
16
|
+
|
|
17
|
+
import { LearnCloudPluginDependentMethods, LearnCloudPlugin } from './types';
|
|
18
|
+
import {
|
|
19
|
+
generateEncryptedRecord,
|
|
20
|
+
generateEncryptedFieldsArray,
|
|
21
|
+
generateJWE,
|
|
22
|
+
decryptJWE,
|
|
23
|
+
hash,
|
|
24
|
+
} from './helpers';
|
|
25
|
+
|
|
26
|
+
export * from './types';
|
|
27
|
+
|
|
28
|
+
const uint8ArrayToBase64Url = (bytes: Uint8Array): string => {
|
|
29
|
+
let binary = '';
|
|
30
|
+
for (let i = 0; i < bytes.length; i += 1) binary += String.fromCharCode(bytes[i]!);
|
|
31
|
+
const base64 =
|
|
32
|
+
typeof btoa === 'function'
|
|
33
|
+
? btoa(binary)
|
|
34
|
+
: Buffer.from(binary, 'binary').toString('base64');
|
|
35
|
+
return base64.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
type WireEnvelope = StoredCredentialEnvelope & { data: string };
|
|
39
|
+
|
|
40
|
+
const normalizeEnvelopeForTransport = <T>(
|
|
41
|
+
value: T
|
|
42
|
+
): Exclude<T, StoredCredentialEnvelope> | WireEnvelope => {
|
|
43
|
+
if (!isStoredCredentialEnvelope(value)) {
|
|
44
|
+
return value as Exclude<T, StoredCredentialEnvelope>;
|
|
45
|
+
}
|
|
46
|
+
if (typeof value.data === 'string') return value as WireEnvelope;
|
|
47
|
+
return { ...value, data: uint8ArrayToBase64Url(value.data) };
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
const getLearnCloudClient = async (
|
|
51
|
+
url: string,
|
|
52
|
+
learnCard: LearnCard<any, 'id', LearnCloudPluginDependentMethods>
|
|
53
|
+
) => {
|
|
54
|
+
return getClient(url, async challenge => {
|
|
55
|
+
const jwt = await learnCard.invoke.getDidAuthVp({ proofFormat: 'jwt', challenge });
|
|
56
|
+
|
|
57
|
+
if (typeof jwt !== 'string') throw new Error('Error getting DID-Auth-JWT!');
|
|
58
|
+
|
|
59
|
+
return jwt;
|
|
60
|
+
});
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* @group Plugins
|
|
65
|
+
*/
|
|
66
|
+
export const getLearnCloudPlugin = async (
|
|
67
|
+
initialLearnCard: LearnCard<any, 'id', LearnCloudPluginDependentMethods>,
|
|
68
|
+
url: string,
|
|
69
|
+
unencryptedFields: string[] = [],
|
|
70
|
+
unencryptedCustomFields: string[] = [],
|
|
71
|
+
automaticallyAssociateDids = true
|
|
72
|
+
): Promise<LearnCloudPlugin> => {
|
|
73
|
+
let learnCard = initialLearnCard;
|
|
74
|
+
|
|
75
|
+
learnCard.debug?.('Adding LearnCloud Plugin');
|
|
76
|
+
|
|
77
|
+
let client = await getLearnCloudClient(url, learnCard);
|
|
78
|
+
|
|
79
|
+
let dids: string[] = [learnCard.id.did()];
|
|
80
|
+
|
|
81
|
+
client.user.getDids.query().then(result => (dids = result));
|
|
82
|
+
|
|
83
|
+
let otherClients: Record<string, LearnCloudClient> = {};
|
|
84
|
+
|
|
85
|
+
const learnCloudDid = client.utilities.getDid.query();
|
|
86
|
+
|
|
87
|
+
const getOtherClient = async (url: string) => {
|
|
88
|
+
if (!otherClients[url]) otherClients[url] = await getLearnCloudClient(url, learnCard);
|
|
89
|
+
|
|
90
|
+
return otherClients[url]!;
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
const updateLearnCard = async (
|
|
94
|
+
_learnCard: LearnCard<any, 'id', LearnCloudPluginDependentMethods>
|
|
95
|
+
) => {
|
|
96
|
+
const oldDid = learnCard.id.did();
|
|
97
|
+
const newDid = _learnCard.id.did();
|
|
98
|
+
|
|
99
|
+
if (oldDid !== newDid) {
|
|
100
|
+
if (!dids.includes(newDid) && automaticallyAssociateDids) {
|
|
101
|
+
const presentation = await _learnCard.invoke.getDidAuthVp();
|
|
102
|
+
|
|
103
|
+
await client.user.addDid.mutate({ presentation });
|
|
104
|
+
|
|
105
|
+
if (newDid.split(':')[1] === 'web') {
|
|
106
|
+
await client.user.setPrimaryDid.mutate({ presentation });
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
dids = await client.user.getDids.query();
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
client = await getLearnCloudClient(url, _learnCard);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
learnCard = _learnCard;
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
return {
|
|
119
|
+
name: 'LearnCloud',
|
|
120
|
+
displayName: 'LearnCloud',
|
|
121
|
+
description: 'LearnCloud Integration',
|
|
122
|
+
methods: {
|
|
123
|
+
learnCloudCreate: async (_learnCard, document) => {
|
|
124
|
+
await updateLearnCard(_learnCard);
|
|
125
|
+
|
|
126
|
+
const item = await generateEncryptedRecord(
|
|
127
|
+
_learnCard,
|
|
128
|
+
document,
|
|
129
|
+
unencryptedCustomFields
|
|
130
|
+
);
|
|
131
|
+
|
|
132
|
+
return client.customStorage.create.mutate({
|
|
133
|
+
item: await generateJWE(_learnCard, await learnCloudDid, item),
|
|
134
|
+
});
|
|
135
|
+
},
|
|
136
|
+
learnCloudCreateMany: async (_learnCard, documents) => {
|
|
137
|
+
await updateLearnCard(_learnCard);
|
|
138
|
+
|
|
139
|
+
const items = await Promise.all(
|
|
140
|
+
documents.map(async document =>
|
|
141
|
+
generateEncryptedRecord(_learnCard, document, unencryptedCustomFields)
|
|
142
|
+
)
|
|
143
|
+
);
|
|
144
|
+
|
|
145
|
+
return client.customStorage.createMany.mutate({
|
|
146
|
+
items: await generateJWE(_learnCard, await learnCloudDid, items),
|
|
147
|
+
});
|
|
148
|
+
},
|
|
149
|
+
learnCloudRead: async (_learnCard, query, includeAssociatedDids) => {
|
|
150
|
+
await updateLearnCard(_learnCard);
|
|
151
|
+
|
|
152
|
+
const documents: Record<string, any>[] = [];
|
|
153
|
+
|
|
154
|
+
let result = await _learnCard.invoke.learnCloudReadPage(
|
|
155
|
+
query,
|
|
156
|
+
{},
|
|
157
|
+
includeAssociatedDids
|
|
158
|
+
);
|
|
159
|
+
|
|
160
|
+
documents.push(...result.records);
|
|
161
|
+
|
|
162
|
+
while (result.hasMore) {
|
|
163
|
+
result = await _learnCard.invoke.learnCloudReadPage(
|
|
164
|
+
query,
|
|
165
|
+
{},
|
|
166
|
+
includeAssociatedDids
|
|
167
|
+
);
|
|
168
|
+
|
|
169
|
+
documents.push(...result.records);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
return documents;
|
|
173
|
+
},
|
|
174
|
+
learnCloudReadPage: async (
|
|
175
|
+
_learnCard,
|
|
176
|
+
query,
|
|
177
|
+
paginationOptions,
|
|
178
|
+
includeAssociatedDids
|
|
179
|
+
) => {
|
|
180
|
+
await updateLearnCard(_learnCard);
|
|
181
|
+
|
|
182
|
+
if (!query) {
|
|
183
|
+
const jwe: JWE = (await client.customStorage.read.query({
|
|
184
|
+
includeAssociatedDids,
|
|
185
|
+
...paginationOptions,
|
|
186
|
+
})) as any;
|
|
187
|
+
|
|
188
|
+
const encryptedRecords = isEncrypted(jwe)
|
|
189
|
+
? await decryptJWE<PaginatedEncryptedRecordsType>(_learnCard, jwe)
|
|
190
|
+
: (jwe as PaginatedEncryptedRecordsType);
|
|
191
|
+
|
|
192
|
+
return {
|
|
193
|
+
...encryptedRecords,
|
|
194
|
+
records: await Promise.all(
|
|
195
|
+
encryptedRecords.records.map(async record => ({
|
|
196
|
+
...(await decryptJWE<CredentialRecord>(
|
|
197
|
+
_learnCard,
|
|
198
|
+
record.encryptedRecord
|
|
199
|
+
)),
|
|
200
|
+
_id: record._id,
|
|
201
|
+
}))
|
|
202
|
+
),
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const fields = await generateEncryptedFieldsArray(_learnCard, query, [
|
|
207
|
+
...unencryptedCustomFields,
|
|
208
|
+
'_id',
|
|
209
|
+
]);
|
|
210
|
+
|
|
211
|
+
const unencryptedEntries = Object.fromEntries(
|
|
212
|
+
Object.entries(query).filter(([key]) =>
|
|
213
|
+
[...unencryptedCustomFields, '_id'].includes(key)
|
|
214
|
+
)
|
|
215
|
+
);
|
|
216
|
+
|
|
217
|
+
const jwe: JWE = (await client.customStorage.read.query({
|
|
218
|
+
query: await generateJWE(_learnCard, await learnCloudDid, {
|
|
219
|
+
...unencryptedEntries,
|
|
220
|
+
...(fields.length > 0 ? { fields: { $in: fields } } : {}),
|
|
221
|
+
}),
|
|
222
|
+
...paginationOptions,
|
|
223
|
+
includeAssociatedDids,
|
|
224
|
+
})) as any;
|
|
225
|
+
|
|
226
|
+
const encryptedRecords = isEncrypted(jwe)
|
|
227
|
+
? await decryptJWE<PaginatedEncryptedRecordsType>(_learnCard, jwe)
|
|
228
|
+
: (jwe as PaginatedEncryptedRecordsType);
|
|
229
|
+
|
|
230
|
+
return {
|
|
231
|
+
...encryptedRecords,
|
|
232
|
+
records: await Promise.all(
|
|
233
|
+
encryptedRecords.records.map(async record => ({
|
|
234
|
+
...(await decryptJWE<CredentialRecord>(
|
|
235
|
+
_learnCard,
|
|
236
|
+
record.encryptedRecord
|
|
237
|
+
)),
|
|
238
|
+
_id: record._id,
|
|
239
|
+
}))
|
|
240
|
+
),
|
|
241
|
+
};
|
|
242
|
+
},
|
|
243
|
+
learnCloudCount: async (_learnCard, query, includeAssociatedDids) => {
|
|
244
|
+
await updateLearnCard(_learnCard);
|
|
245
|
+
|
|
246
|
+
if (!query) return client.customStorage.count.query({ includeAssociatedDids });
|
|
247
|
+
|
|
248
|
+
const fields = await generateEncryptedFieldsArray(_learnCard, query, [
|
|
249
|
+
...unencryptedCustomFields,
|
|
250
|
+
'_id',
|
|
251
|
+
]);
|
|
252
|
+
|
|
253
|
+
const unencryptedEntries = Object.fromEntries(
|
|
254
|
+
Object.entries(query).filter(([key]) =>
|
|
255
|
+
[...unencryptedCustomFields, '_id'].includes(key)
|
|
256
|
+
)
|
|
257
|
+
);
|
|
258
|
+
|
|
259
|
+
return client.customStorage.count.query({
|
|
260
|
+
query: await generateJWE(_learnCard, await learnCloudDid, {
|
|
261
|
+
...unencryptedEntries,
|
|
262
|
+
...(fields.length > 0 ? { fields: { $in: fields } } : {}),
|
|
263
|
+
}),
|
|
264
|
+
includeAssociatedDids,
|
|
265
|
+
});
|
|
266
|
+
},
|
|
267
|
+
learnCloudUpdate: async (_learnCard, query, update) => {
|
|
268
|
+
await updateLearnCard(_learnCard);
|
|
269
|
+
|
|
270
|
+
const documents = await _learnCard.invoke.learnCloudRead(query);
|
|
271
|
+
|
|
272
|
+
const updates = await Promise.all(
|
|
273
|
+
documents.map(async document =>
|
|
274
|
+
client.customStorage.update.mutate({
|
|
275
|
+
query: await generateJWE(_learnCard, await learnCloudDid, {
|
|
276
|
+
_id: document._id,
|
|
277
|
+
}),
|
|
278
|
+
update: await generateJWE(
|
|
279
|
+
_learnCard,
|
|
280
|
+
await learnCloudDid,
|
|
281
|
+
await generateEncryptedRecord(
|
|
282
|
+
_learnCard,
|
|
283
|
+
{ ...document, ...update },
|
|
284
|
+
unencryptedCustomFields
|
|
285
|
+
)
|
|
286
|
+
),
|
|
287
|
+
})
|
|
288
|
+
)
|
|
289
|
+
);
|
|
290
|
+
|
|
291
|
+
return updates.reduce((sum, current) => current + sum, 0);
|
|
292
|
+
},
|
|
293
|
+
learnCloudDelete: async (_learnCard, query, includeAssociatedDids) => {
|
|
294
|
+
await updateLearnCard(_learnCard);
|
|
295
|
+
|
|
296
|
+
if (!query) return client.customStorage.delete.mutate({ includeAssociatedDids });
|
|
297
|
+
|
|
298
|
+
const fields = await generateEncryptedFieldsArray(_learnCard, query, [
|
|
299
|
+
...unencryptedCustomFields,
|
|
300
|
+
'_id',
|
|
301
|
+
]);
|
|
302
|
+
|
|
303
|
+
const unencryptedEntries = Object.fromEntries(
|
|
304
|
+
Object.entries(query).filter(([key]) =>
|
|
305
|
+
[...unencryptedCustomFields, '_id'].includes(key)
|
|
306
|
+
)
|
|
307
|
+
);
|
|
308
|
+
|
|
309
|
+
return client.customStorage.delete.mutate({
|
|
310
|
+
query: await generateJWE(_learnCard, await learnCloudDid, {
|
|
311
|
+
...unencryptedEntries,
|
|
312
|
+
...(fields.length > 0 ? { fields: { $in: fields } } : {}),
|
|
313
|
+
}),
|
|
314
|
+
includeAssociatedDids,
|
|
315
|
+
});
|
|
316
|
+
},
|
|
317
|
+
learnCloudBatchResolve: async (_learnCard, uris) => {
|
|
318
|
+
const results = await client.storage.batchResolve.query({ uris });
|
|
319
|
+
|
|
320
|
+
return Promise.all(
|
|
321
|
+
results.map(async result => {
|
|
322
|
+
if (!result) return null;
|
|
323
|
+
|
|
324
|
+
try {
|
|
325
|
+
const decryptedResult = await _learnCard.invoke.decryptDagJwe(result);
|
|
326
|
+
|
|
327
|
+
const parsed = await VCValidator.or(VPValidator)
|
|
328
|
+
.or(StoredCredentialEnvelopeValidator)
|
|
329
|
+
.parseAsync(decryptedResult);
|
|
330
|
+
|
|
331
|
+
const resolved = resolveStorageReadResult(parsed);
|
|
332
|
+
return resolved === undefined || isStoredCredentialEnvelope(resolved)
|
|
333
|
+
? null
|
|
334
|
+
: resolved;
|
|
335
|
+
} catch (error) {
|
|
336
|
+
_learnCard.debug?.(error);
|
|
337
|
+
|
|
338
|
+
return null;
|
|
339
|
+
}
|
|
340
|
+
})
|
|
341
|
+
);
|
|
342
|
+
},
|
|
343
|
+
},
|
|
344
|
+
read: {
|
|
345
|
+
get: async (_learnCard, uri) => {
|
|
346
|
+
learnCard.debug?.('LearnCloud read.get', uri);
|
|
347
|
+
|
|
348
|
+
if (!uri) return undefined;
|
|
349
|
+
|
|
350
|
+
const parts = uri.split(':');
|
|
351
|
+
|
|
352
|
+
learnCard.debug?.('LearnCloud read.get parts:', parts);
|
|
353
|
+
|
|
354
|
+
if (parts.length !== 5) return undefined;
|
|
355
|
+
|
|
356
|
+
const [lc, method, uriUrl] = parts as [string, string, string, string, string];
|
|
357
|
+
|
|
358
|
+
if (lc !== 'lc' || method !== 'cloud') {
|
|
359
|
+
learnCard.debug?.('LearnCloud read.get not cloud URI!', { lc, method });
|
|
360
|
+
|
|
361
|
+
return undefined;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
if (
|
|
365
|
+
uriUrl.replace(/https?:\/\//g, '') !==
|
|
366
|
+
url.replace(/https?:\/\//g, '').replace(/:/g, '%3A')
|
|
367
|
+
) {
|
|
368
|
+
const fullUrl = (
|
|
369
|
+
uriUrl.startsWith('http')
|
|
370
|
+
? uriUrl
|
|
371
|
+
: `http${
|
|
372
|
+
uriUrl.includes('http') || uriUrl.includes('localhost') ? '' : 's'
|
|
373
|
+
}://${uriUrl}`
|
|
374
|
+
).replaceAll('%3A', ':');
|
|
375
|
+
|
|
376
|
+
learnCard.debug?.('LearnCloud read.get different LearnCloud!', {
|
|
377
|
+
uriUrl,
|
|
378
|
+
url,
|
|
379
|
+
fullUrl,
|
|
380
|
+
comparison: {
|
|
381
|
+
a: uriUrl.replace(/https?:\/\//g, ''),
|
|
382
|
+
b: url.replace(/https?:\/\//g, '').replace(/:/g, '%3A'),
|
|
383
|
+
},
|
|
384
|
+
});
|
|
385
|
+
|
|
386
|
+
const otherClient = await getOtherClient(fullUrl);
|
|
387
|
+
|
|
388
|
+
try {
|
|
389
|
+
const result = await otherClient.storage.resolve.query({ uri: uri });
|
|
390
|
+
|
|
391
|
+
learnCard.debug?.('LearnCloud read.get result', result);
|
|
392
|
+
|
|
393
|
+
const decryptedResult = await _learnCard.invoke.decryptDagJwe(result);
|
|
394
|
+
|
|
395
|
+
learnCard.debug?.('LearnCloud read.get decryptedResult', decryptedResult);
|
|
396
|
+
|
|
397
|
+
const parsed = await VCValidator.or(VPValidator)
|
|
398
|
+
.or(StoredCredentialEnvelopeValidator)
|
|
399
|
+
.parseAsync(decryptedResult);
|
|
400
|
+
return resolveStorageReadResult(parsed);
|
|
401
|
+
} catch (error) {
|
|
402
|
+
_learnCard.debug?.(error);
|
|
403
|
+
return undefined;
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
try {
|
|
408
|
+
const result = await client.storage.resolve.query({ uri: uri });
|
|
409
|
+
|
|
410
|
+
learnCard.debug?.('LearnCloud read.get result', result);
|
|
411
|
+
|
|
412
|
+
const decryptedResult = await _learnCard.invoke.decryptDagJwe(result);
|
|
413
|
+
|
|
414
|
+
learnCard.debug?.('LearnCloud read.get decryptedResult', decryptedResult);
|
|
415
|
+
|
|
416
|
+
const parsed = await VCValidator.or(VPValidator)
|
|
417
|
+
.or(StoredCredentialEnvelopeValidator)
|
|
418
|
+
.parseAsync(decryptedResult);
|
|
419
|
+
return resolveStorageReadResult(parsed);
|
|
420
|
+
} catch (error) {
|
|
421
|
+
_learnCard.debug?.(error);
|
|
422
|
+
return undefined;
|
|
423
|
+
}
|
|
424
|
+
},
|
|
425
|
+
},
|
|
426
|
+
store: {
|
|
427
|
+
upload: async (_learnCard, credential) => {
|
|
428
|
+
_learnCard.debug?.("learnCard.store['LearnCard Network'].upload");
|
|
429
|
+
|
|
430
|
+
return client.storage.store.mutate({
|
|
431
|
+
item: normalizeEnvelopeForTransport(credential),
|
|
432
|
+
});
|
|
433
|
+
},
|
|
434
|
+
uploadEncrypted: async (
|
|
435
|
+
_learnCard,
|
|
436
|
+
credential,
|
|
437
|
+
{ recipients = [] } = { recipients: [] }
|
|
438
|
+
) => {
|
|
439
|
+
_learnCard.debug?.("learnCard.store['LearnCard Network'].upload");
|
|
440
|
+
|
|
441
|
+
const jwe = await _learnCard.invoke.createDagJwe(
|
|
442
|
+
normalizeEnvelopeForTransport(credential),
|
|
443
|
+
[_learnCard.id.did(), ...recipients]
|
|
444
|
+
);
|
|
445
|
+
|
|
446
|
+
return client.storage.store.mutate({ item: jwe });
|
|
447
|
+
},
|
|
448
|
+
delete: async (_learnCard, uri) => {
|
|
449
|
+
_learnCard.debug?.("learnCard.store['LearnCloud'].delete", { uri });
|
|
450
|
+
|
|
451
|
+
let deleted = false;
|
|
452
|
+
|
|
453
|
+
try {
|
|
454
|
+
deleted = Boolean(await client.storage.delete.mutate({ uri }));
|
|
455
|
+
} catch (error) {
|
|
456
|
+
_learnCard.debug?.('LearnCloud store.delete storage.delete failed', error);
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
if (!deleted) return false;
|
|
460
|
+
|
|
461
|
+
try {
|
|
462
|
+
const records = await _learnCard.index.LearnCloud.get({ uri });
|
|
463
|
+
for (const record of records) {
|
|
464
|
+
await _learnCard.index.LearnCloud.remove(record.id, {
|
|
465
|
+
cache: 'skip-cache',
|
|
466
|
+
});
|
|
467
|
+
}
|
|
468
|
+
} catch (error) {
|
|
469
|
+
_learnCard.debug?.('LearnCloud store.delete index cleanup failed', error);
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
return true;
|
|
473
|
+
},
|
|
474
|
+
},
|
|
475
|
+
index: {
|
|
476
|
+
get: async (_learnCard, query) => {
|
|
477
|
+
await updateLearnCard(_learnCard);
|
|
478
|
+
|
|
479
|
+
const records: CredentialRecord[] = [];
|
|
480
|
+
|
|
481
|
+
let result: PaginatedEncryptedCredentialRecordsType = await (
|
|
482
|
+
_learnCard.index as any
|
|
483
|
+
).LearnCloud.getPage(query);
|
|
484
|
+
|
|
485
|
+
records.push(...(result.records as any));
|
|
486
|
+
|
|
487
|
+
while (result.hasMore) {
|
|
488
|
+
result = await (_learnCard.index as any).LearnCloud.getPage(query, {
|
|
489
|
+
cursor: result.cursor,
|
|
490
|
+
});
|
|
491
|
+
|
|
492
|
+
records.push(...(result.records as any));
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
return records;
|
|
496
|
+
},
|
|
497
|
+
getPage: async (_learnCard, query, paginationOptions) => {
|
|
498
|
+
await updateLearnCard(_learnCard);
|
|
499
|
+
|
|
500
|
+
_learnCard.debug?.('LearnCloud index.getPaginated', query, paginationOptions);
|
|
501
|
+
|
|
502
|
+
const options = {
|
|
503
|
+
...paginationOptions,
|
|
504
|
+
sort: ['newestFirst', 'oldestFirst'].includes(paginationOptions?.sort ?? '')
|
|
505
|
+
? (paginationOptions?.sort as 'newestFirst' | 'oldestFirst')
|
|
506
|
+
: undefined,
|
|
507
|
+
};
|
|
508
|
+
|
|
509
|
+
if (!query) {
|
|
510
|
+
_learnCard.debug?.('LearnCloud index.get (no query)');
|
|
511
|
+
const jwe: JWE = (await client.index.get.query(options)) as any;
|
|
512
|
+
|
|
513
|
+
_learnCard.debug?.('LearnCloud index.get (no query response)', jwe);
|
|
514
|
+
|
|
515
|
+
const encryptedRecords = isEncrypted(jwe)
|
|
516
|
+
? await decryptJWE<PaginatedEncryptedCredentialRecordsType>(_learnCard, jwe)
|
|
517
|
+
: (jwe as PaginatedEncryptedCredentialRecordsType);
|
|
518
|
+
|
|
519
|
+
_learnCard.debug?.(
|
|
520
|
+
'LearnCloud index.get (no query encryptedRecords)',
|
|
521
|
+
encryptedRecords
|
|
522
|
+
);
|
|
523
|
+
|
|
524
|
+
return {
|
|
525
|
+
...encryptedRecords,
|
|
526
|
+
records: await Promise.all(
|
|
527
|
+
encryptedRecords.records.map(async record =>
|
|
528
|
+
decryptJWE<CredentialRecord>(_learnCard, record.encryptedRecord)
|
|
529
|
+
)
|
|
530
|
+
),
|
|
531
|
+
};
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
_learnCard.debug?.('LearnCloud index.get (query)');
|
|
535
|
+
|
|
536
|
+
const fields = await generateEncryptedFieldsArray(
|
|
537
|
+
_learnCard,
|
|
538
|
+
query,
|
|
539
|
+
unencryptedFields
|
|
540
|
+
);
|
|
541
|
+
|
|
542
|
+
_learnCard.debug?.('LearnCloud index.get (query fields)', fields);
|
|
543
|
+
|
|
544
|
+
const unencryptedEntries = Object.fromEntries(
|
|
545
|
+
Object.entries(query).filter(([key]) => unencryptedFields.includes(key))
|
|
546
|
+
);
|
|
547
|
+
|
|
548
|
+
_learnCard.debug?.(
|
|
549
|
+
'LearnCloud index.get (query unencryptedEntries)',
|
|
550
|
+
unencryptedEntries
|
|
551
|
+
);
|
|
552
|
+
|
|
553
|
+
const jwe: JWE = (await client.index.get.query({
|
|
554
|
+
query: await generateJWE(_learnCard, await learnCloudDid, {
|
|
555
|
+
...unencryptedEntries,
|
|
556
|
+
...(fields.length > 0 ? { fields: { $in: fields } } : {}),
|
|
557
|
+
}),
|
|
558
|
+
...options,
|
|
559
|
+
})) as any;
|
|
560
|
+
|
|
561
|
+
_learnCard.debug?.('LearnCloud index.get (query jwe)', jwe);
|
|
562
|
+
|
|
563
|
+
const encryptedRecords = isEncrypted(jwe)
|
|
564
|
+
? await decryptJWE<PaginatedEncryptedCredentialRecordsType>(_learnCard, jwe)
|
|
565
|
+
: (jwe as PaginatedEncryptedCredentialRecordsType);
|
|
566
|
+
|
|
567
|
+
_learnCard.debug?.(
|
|
568
|
+
'LearnCloud index.get (query encryptedRecords)',
|
|
569
|
+
encryptedRecords
|
|
570
|
+
);
|
|
571
|
+
|
|
572
|
+
return {
|
|
573
|
+
...encryptedRecords,
|
|
574
|
+
records: await Promise.all(
|
|
575
|
+
encryptedRecords.records.map(async record =>
|
|
576
|
+
decryptJWE<CredentialRecord>(_learnCard, record.encryptedRecord)
|
|
577
|
+
)
|
|
578
|
+
),
|
|
579
|
+
};
|
|
580
|
+
},
|
|
581
|
+
getCount: async (_learnCard, query) => {
|
|
582
|
+
await updateLearnCard(_learnCard);
|
|
583
|
+
|
|
584
|
+
if (!query) {
|
|
585
|
+
_learnCard.debug?.('LearnCloud index.count (no query)');
|
|
586
|
+
const jwe = await client.index.count.query();
|
|
587
|
+
|
|
588
|
+
_learnCard.debug?.('LearnCloud index.count (no query response)', jwe);
|
|
589
|
+
|
|
590
|
+
const count = isEncrypted(jwe as any)
|
|
591
|
+
? await decryptJWE<number>(_learnCard, jwe as JWE)
|
|
592
|
+
: (jwe as number);
|
|
593
|
+
|
|
594
|
+
_learnCard.debug?.('LearnCloud index.count (no query count)', count);
|
|
595
|
+
|
|
596
|
+
return count;
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
_learnCard.debug?.('LearnCloud index.getCount (query)');
|
|
600
|
+
|
|
601
|
+
const fields = await generateEncryptedFieldsArray(
|
|
602
|
+
_learnCard,
|
|
603
|
+
query,
|
|
604
|
+
unencryptedFields
|
|
605
|
+
);
|
|
606
|
+
|
|
607
|
+
_learnCard.debug?.('LearnCloud index.getCount (query fields)', fields);
|
|
608
|
+
|
|
609
|
+
const unencryptedEntries = Object.fromEntries(
|
|
610
|
+
Object.entries(query).filter(([key]) => unencryptedFields.includes(key))
|
|
611
|
+
);
|
|
612
|
+
|
|
613
|
+
_learnCard.debug?.(
|
|
614
|
+
'LearnCloud index.getCount (query unencryptedEntries)',
|
|
615
|
+
unencryptedEntries
|
|
616
|
+
);
|
|
617
|
+
|
|
618
|
+
const jwe = await client.index.count.query({
|
|
619
|
+
query: await generateJWE(_learnCard, await learnCloudDid, {
|
|
620
|
+
...unencryptedEntries,
|
|
621
|
+
...(fields.length > 0 ? { fields: { $in: fields } } : {}),
|
|
622
|
+
}),
|
|
623
|
+
});
|
|
624
|
+
|
|
625
|
+
_learnCard.debug?.('LearnCloud index.count (query response)', jwe);
|
|
626
|
+
|
|
627
|
+
const count = isEncrypted(jwe as any)
|
|
628
|
+
? await decryptJWE<number>(_learnCard, jwe as JWE)
|
|
629
|
+
: (jwe as number);
|
|
630
|
+
|
|
631
|
+
_learnCard.debug?.('LearnCloud index.count (query count)', count);
|
|
632
|
+
|
|
633
|
+
return count;
|
|
634
|
+
},
|
|
635
|
+
add: async (_learnCard, record) => {
|
|
636
|
+
await updateLearnCard(_learnCard);
|
|
637
|
+
|
|
638
|
+
const id = record.id || _learnCard.invoke.crypto().randomUUID();
|
|
639
|
+
|
|
640
|
+
return client.index.add.mutate({
|
|
641
|
+
record: await generateJWE(_learnCard, await learnCloudDid, {
|
|
642
|
+
...(await generateEncryptedRecord(
|
|
643
|
+
_learnCard,
|
|
644
|
+
{ ...record, id },
|
|
645
|
+
unencryptedFields
|
|
646
|
+
)),
|
|
647
|
+
id: await hash(_learnCard, id),
|
|
648
|
+
}),
|
|
649
|
+
});
|
|
650
|
+
},
|
|
651
|
+
addMany: async (_learnCard, _records) => {
|
|
652
|
+
await updateLearnCard(_learnCard);
|
|
653
|
+
|
|
654
|
+
const results = await Promise.all(
|
|
655
|
+
chunk(_records, 25).map(async batch => {
|
|
656
|
+
const records = await Promise.all(
|
|
657
|
+
batch.map(async record => {
|
|
658
|
+
const id = record.id || _learnCard.invoke.crypto().randomUUID();
|
|
659
|
+
|
|
660
|
+
return {
|
|
661
|
+
...(await generateEncryptedRecord(
|
|
662
|
+
_learnCard,
|
|
663
|
+
{ ...record, id },
|
|
664
|
+
unencryptedFields
|
|
665
|
+
)),
|
|
666
|
+
id: await hash(_learnCard, id),
|
|
667
|
+
};
|
|
668
|
+
})
|
|
669
|
+
);
|
|
670
|
+
|
|
671
|
+
return client.index.addMany.mutate({
|
|
672
|
+
records: await generateJWE(_learnCard, await learnCloudDid, records),
|
|
673
|
+
});
|
|
674
|
+
})
|
|
675
|
+
);
|
|
676
|
+
|
|
677
|
+
return results.every(Boolean);
|
|
678
|
+
},
|
|
679
|
+
update: async (_learnCard, id, updates) => {
|
|
680
|
+
await updateLearnCard(_learnCard);
|
|
681
|
+
|
|
682
|
+
const records = await _learnCard.index.LearnCloud.get({ id });
|
|
683
|
+
|
|
684
|
+
if (records.length !== 1) return false;
|
|
685
|
+
|
|
686
|
+
const record = records[0];
|
|
687
|
+
|
|
688
|
+
const newRecord = { ...record, ...updates };
|
|
689
|
+
|
|
690
|
+
return client.index.update.mutate({
|
|
691
|
+
id: await hash(_learnCard, id),
|
|
692
|
+
updates: await generateJWE(
|
|
693
|
+
_learnCard,
|
|
694
|
+
await learnCloudDid,
|
|
695
|
+
await generateEncryptedRecord(_learnCard, newRecord, unencryptedFields)
|
|
696
|
+
),
|
|
697
|
+
});
|
|
698
|
+
},
|
|
699
|
+
remove: async (_learnCard, id) => {
|
|
700
|
+
await updateLearnCard(_learnCard);
|
|
701
|
+
|
|
702
|
+
return client.index.remove.mutate({ id: await hash(_learnCard, id) });
|
|
703
|
+
},
|
|
704
|
+
removeAll: async _learnCard => {
|
|
705
|
+
await updateLearnCard(_learnCard);
|
|
706
|
+
|
|
707
|
+
return client.index.removeAll.mutate();
|
|
708
|
+
},
|
|
709
|
+
},
|
|
710
|
+
};
|
|
711
|
+
};
|