@learncard/learn-cloud-plugin 2.3.38 → 2.3.40
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/dist/learn-cloud-plugin.cjs.development.cjs +336 -86
- package/dist/learn-cloud-plugin.cjs.development.cjs.map +2 -2
- package/dist/learn-cloud-plugin.cjs.production.min.cjs +37 -37
- package/dist/learn-cloud-plugin.cjs.production.min.cjs.map +3 -3
- package/dist/learn-cloud-plugin.esm.js +336 -86
- package/dist/learn-cloud-plugin.esm.js.map +2 -2
- package/dist/plugin.d.ts.map +1 -1
- package/package.json +7 -7
- package/src/plugin.ts +146 -84
- package/src/test/index.test.ts +187 -19
package/src/test/index.test.ts
CHANGED
|
@@ -9,6 +9,7 @@ const makeMockClient = (learnCloudDid = 'did:key:z6MkLearnCloud') => ({
|
|
|
9
9
|
getDid: { query: vi.fn().mockResolvedValue(learnCloudDid) },
|
|
10
10
|
},
|
|
11
11
|
customStorage: {
|
|
12
|
+
count: { query: vi.fn().mockResolvedValue(0) },
|
|
12
13
|
create: { mutate: vi.fn().mockResolvedValue('lc:cloud:credential') },
|
|
13
14
|
},
|
|
14
15
|
storage: {
|
|
@@ -45,8 +46,8 @@ const makeLearnCard = (did = 'did:key:z6MkHolder') => ({
|
|
|
45
46
|
invoke: {
|
|
46
47
|
getDidAuthVp: vi.fn().mockResolvedValue('did-auth-jwt'),
|
|
47
48
|
decryptDagJwe: vi.fn(async value => value),
|
|
48
|
-
createDagJwe: vi.fn(async (
|
|
49
|
-
hash: vi.fn()
|
|
49
|
+
createDagJwe: vi.fn(async () => ({ ciphertext: 'encrypted' })),
|
|
50
|
+
hash: vi.fn(async value => `hash:${value}`),
|
|
50
51
|
},
|
|
51
52
|
debug: vi.fn(),
|
|
52
53
|
});
|
|
@@ -57,38 +58,205 @@ describe('LearnCloud Plugin', () => {
|
|
|
57
58
|
mockGetClient.mockResolvedValue(mockClient);
|
|
58
59
|
});
|
|
59
60
|
|
|
60
|
-
it('
|
|
61
|
-
|
|
61
|
+
it('defers remote identity requests until a method needs them', async () => {
|
|
62
|
+
await getLearnCloudPlugin(makeLearnCard() as never, 'https://cloud.example');
|
|
63
|
+
|
|
64
|
+
expect(mockClient.user.getDids.query).not.toHaveBeenCalled();
|
|
65
|
+
expect(mockClient.utilities.getDid.query).not.toHaveBeenCalled();
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it('does not load associated DIDs when automatic association is disabled', async () => {
|
|
69
|
+
const learnCard = makeLearnCard();
|
|
70
|
+
const plugin = await getLearnCloudPlugin(
|
|
71
|
+
learnCard as never,
|
|
72
|
+
'https://cloud.example',
|
|
73
|
+
[],
|
|
74
|
+
[],
|
|
75
|
+
false
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
await plugin.methods.learnCloudCount(
|
|
79
|
+
makeLearnCard('did:web:holder.example') as never,
|
|
80
|
+
undefined as never,
|
|
81
|
+
false
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
expect(mockClient.user.getDids.query).not.toHaveBeenCalled();
|
|
85
|
+
expect(mockClient.customStorage.count.query).toHaveBeenCalledWith({
|
|
86
|
+
includeAssociatedDids: false,
|
|
87
|
+
});
|
|
62
88
|
});
|
|
63
89
|
|
|
64
|
-
it('refreshes the LearnCloud DID after
|
|
65
|
-
const
|
|
66
|
-
|
|
67
|
-
|
|
90
|
+
it('refreshes the LearnCloud recipient DID after the active wallet changes', async () => {
|
|
91
|
+
const firstClient = {
|
|
92
|
+
...mockClient,
|
|
93
|
+
utilities: {
|
|
94
|
+
getDid: { query: vi.fn().mockResolvedValue('did:key:z6MkFirstLearnCloud') },
|
|
95
|
+
},
|
|
96
|
+
customStorage: {
|
|
97
|
+
...mockClient.customStorage,
|
|
98
|
+
create: { mutate: vi.fn().mockResolvedValue('first-record') },
|
|
99
|
+
},
|
|
100
|
+
};
|
|
101
|
+
const secondClient = {
|
|
102
|
+
...mockClient,
|
|
103
|
+
utilities: {
|
|
104
|
+
getDid: { query: vi.fn().mockResolvedValue('did:key:z6MkSecondLearnCloud') },
|
|
105
|
+
},
|
|
106
|
+
customStorage: {
|
|
107
|
+
...mockClient.customStorage,
|
|
108
|
+
create: { mutate: vi.fn().mockResolvedValue('second-record') },
|
|
109
|
+
},
|
|
110
|
+
};
|
|
111
|
+
mockGetClient.mockResolvedValueOnce(firstClient).mockResolvedValueOnce(secondClient);
|
|
68
112
|
|
|
69
|
-
const
|
|
70
|
-
const
|
|
113
|
+
const firstLearnCard = makeLearnCard('did:key:z6MkFirstHolder');
|
|
114
|
+
const secondLearnCard = makeLearnCard('did:key:z6MkSecondHolder');
|
|
71
115
|
const plugin = await getLearnCloudPlugin(
|
|
72
|
-
|
|
116
|
+
firstLearnCard as never,
|
|
73
117
|
'https://cloud.example',
|
|
74
118
|
[],
|
|
75
119
|
[],
|
|
76
120
|
false
|
|
77
121
|
);
|
|
78
122
|
|
|
79
|
-
await plugin.methods.learnCloudCreate(
|
|
80
|
-
await plugin.methods.learnCloudCreate(
|
|
123
|
+
await plugin.methods.learnCloudCreate(firstLearnCard as never, { id: 'first' });
|
|
124
|
+
await plugin.methods.learnCloudCreate(secondLearnCard as never, { id: 'second' });
|
|
81
125
|
|
|
82
|
-
expect(
|
|
83
|
-
expect(
|
|
84
|
-
expect(
|
|
85
|
-
'did:
|
|
126
|
+
expect(firstClient.utilities.getDid.query).toHaveBeenCalledTimes(1);
|
|
127
|
+
expect(secondClient.utilities.getDid.query).toHaveBeenCalledTimes(1);
|
|
128
|
+
expect(firstLearnCard.invoke.createDagJwe).toHaveBeenLastCalledWith(expect.anything(), [
|
|
129
|
+
'did:key:z6MkFirstLearnCloud',
|
|
86
130
|
]);
|
|
87
|
-
expect(
|
|
88
|
-
'did:
|
|
131
|
+
expect(secondLearnCard.invoke.createDagJwe).toHaveBeenLastCalledWith(expect.anything(), [
|
|
132
|
+
'did:key:z6MkSecondLearnCloud',
|
|
89
133
|
]);
|
|
90
134
|
});
|
|
91
135
|
|
|
136
|
+
it('keeps an in-flight write bound to the wallet client that encrypted it', async () => {
|
|
137
|
+
const firstClient = {
|
|
138
|
+
...mockClient,
|
|
139
|
+
utilities: {
|
|
140
|
+
getDid: { query: vi.fn().mockResolvedValue('did:key:z6MkFirstLearnCloud') },
|
|
141
|
+
},
|
|
142
|
+
customStorage: {
|
|
143
|
+
...mockClient.customStorage,
|
|
144
|
+
create: { mutate: vi.fn().mockResolvedValue('first-record') },
|
|
145
|
+
},
|
|
146
|
+
};
|
|
147
|
+
const secondClient = {
|
|
148
|
+
...mockClient,
|
|
149
|
+
utilities: {
|
|
150
|
+
getDid: { query: vi.fn().mockResolvedValue('did:key:z6MkSecondLearnCloud') },
|
|
151
|
+
},
|
|
152
|
+
customStorage: {
|
|
153
|
+
...mockClient.customStorage,
|
|
154
|
+
create: { mutate: vi.fn().mockResolvedValue('second-record') },
|
|
155
|
+
},
|
|
156
|
+
};
|
|
157
|
+
mockGetClient.mockResolvedValueOnce(firstClient).mockResolvedValueOnce(secondClient);
|
|
158
|
+
|
|
159
|
+
const firstLearnCard = makeLearnCard('did:key:z6MkFirstHolder');
|
|
160
|
+
const secondLearnCard = makeLearnCard('did:key:z6MkSecondHolder');
|
|
161
|
+
let releaseFirstEncryption: (() => void) | undefined;
|
|
162
|
+
let markFirstEncryptionStarted: (() => void) | undefined;
|
|
163
|
+
const firstEncryptionStarted = new Promise<void>(resolve => {
|
|
164
|
+
markFirstEncryptionStarted = resolve;
|
|
165
|
+
});
|
|
166
|
+
const firstEncryptionReleased = new Promise<void>(resolve => {
|
|
167
|
+
releaseFirstEncryption = resolve;
|
|
168
|
+
});
|
|
169
|
+
let firstEncryptionCalls = 0;
|
|
170
|
+
|
|
171
|
+
firstLearnCard.invoke.createDagJwe.mockImplementation(async () => {
|
|
172
|
+
firstEncryptionCalls += 1;
|
|
173
|
+
|
|
174
|
+
if (firstEncryptionCalls === 2) {
|
|
175
|
+
markFirstEncryptionStarted?.();
|
|
176
|
+
await firstEncryptionReleased;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
return { ciphertext: `first-${firstEncryptionCalls}` };
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
const plugin = await getLearnCloudPlugin(
|
|
183
|
+
firstLearnCard as never,
|
|
184
|
+
'https://cloud.example',
|
|
185
|
+
[],
|
|
186
|
+
[],
|
|
187
|
+
false
|
|
188
|
+
);
|
|
189
|
+
const firstWrite = plugin.methods.learnCloudCreate(firstLearnCard as never, {
|
|
190
|
+
id: 'first',
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
await firstEncryptionStarted;
|
|
194
|
+
|
|
195
|
+
const secondWrite = plugin.methods.learnCloudCreate(secondLearnCard as never, {
|
|
196
|
+
id: 'second',
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
await secondWrite;
|
|
200
|
+
releaseFirstEncryption?.();
|
|
201
|
+
await firstWrite;
|
|
202
|
+
|
|
203
|
+
expect(firstClient.customStorage.create.mutate).toHaveBeenCalledWith({
|
|
204
|
+
item: { ciphertext: 'first-2' },
|
|
205
|
+
});
|
|
206
|
+
expect(secondClient.customStorage.create.mutate).toHaveBeenCalledTimes(1);
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
it('keys cross-cloud clients by both URL and active wallet DID', async () => {
|
|
210
|
+
const otherAuthCallbacks: Array<(challenge: string) => Promise<string>> = [];
|
|
211
|
+
const crossCloudClient = {
|
|
212
|
+
...mockClient,
|
|
213
|
+
storage: {
|
|
214
|
+
...mockClient.storage,
|
|
215
|
+
resolve: { query: vi.fn().mockResolvedValue(makeW3cVc()) },
|
|
216
|
+
},
|
|
217
|
+
};
|
|
218
|
+
|
|
219
|
+
mockGetClient.mockImplementation(
|
|
220
|
+
async (clientUrl: string, authenticate: (challenge: string) => Promise<string>) => {
|
|
221
|
+
if (clientUrl === 'https://other.example') {
|
|
222
|
+
otherAuthCallbacks.push(authenticate);
|
|
223
|
+
|
|
224
|
+
return crossCloudClient;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
return mockClient;
|
|
228
|
+
}
|
|
229
|
+
);
|
|
230
|
+
|
|
231
|
+
const firstLearnCard = makeLearnCard('did:key:z6MkFirstHolder');
|
|
232
|
+
const secondLearnCard = makeLearnCard('did:key:z6MkSecondHolder');
|
|
233
|
+
const plugin = await getLearnCloudPlugin(
|
|
234
|
+
firstLearnCard as never,
|
|
235
|
+
'https://cloud.example',
|
|
236
|
+
[],
|
|
237
|
+
[],
|
|
238
|
+
false
|
|
239
|
+
);
|
|
240
|
+
const uri = 'lc:cloud:other.example:credential:1';
|
|
241
|
+
|
|
242
|
+
await plugin.read.get(firstLearnCard as never, uri);
|
|
243
|
+
await plugin.read.get(secondLearnCard as never, uri);
|
|
244
|
+
|
|
245
|
+
expect(otherAuthCallbacks).toHaveLength(2);
|
|
246
|
+
|
|
247
|
+
await otherAuthCallbacks[0]!('first-challenge');
|
|
248
|
+
await otherAuthCallbacks[1]!('second-challenge');
|
|
249
|
+
|
|
250
|
+
expect(firstLearnCard.invoke.getDidAuthVp).toHaveBeenCalledWith({
|
|
251
|
+
proofFormat: 'jwt',
|
|
252
|
+
challenge: 'first-challenge',
|
|
253
|
+
});
|
|
254
|
+
expect(secondLearnCard.invoke.getDidAuthVp).toHaveBeenCalledWith({
|
|
255
|
+
proofFormat: 'jwt',
|
|
256
|
+
challenge: 'second-challenge',
|
|
257
|
+
});
|
|
258
|
+
});
|
|
259
|
+
|
|
92
260
|
it('projects envelope-backed credentials in learnCloudBatchResolve', async () => {
|
|
93
261
|
const compact =
|
|
94
262
|
'eyJhbGciOiJFZERTQSIsInR5cCI6ImRjK3NkLWp3dCJ9.eyJpc3MiOiJkaWQ6d2ViOmlzc3Vlci5leGFtcGxlIiwiaWF0IjoxNzAwMDAwMDAwLCJ2Y3QiOiJodHRwczovL2V4YW1wbGUuY29tL2NyZWRlbnRpYWxzL2VtcGxveW1lbnQiLCJuYW1lIjoiQWRhIn0.AAAA~';
|