@learncard/partner-connect 0.2.0 → 0.2.3
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/index.d.ts +84 -8
- package/dist/partner-connect.esm.js +89 -29
- package/dist/partner-connect.esm.js.map +1 -1
- package/dist/partner-connect.js +89 -29
- package/dist/partner-connect.js.map +1 -1
- package/package.json +8 -1
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
|
+
import { AppEvent, AppEventResponse } from '@learncard/types';
|
|
2
|
+
export { AppEvent, AppEventResponse, SendCredentialEvent } from '@learncard/types';
|
|
3
|
+
|
|
1
4
|
/**
|
|
2
5
|
* LearnCard Partner Connect SDK - Type Definitions
|
|
3
6
|
*/
|
|
7
|
+
|
|
4
8
|
/**
|
|
5
9
|
* Configuration options for initializing the SDK
|
|
6
10
|
*/
|
|
@@ -70,12 +74,32 @@ interface IdentityResponse {
|
|
|
70
74
|
};
|
|
71
75
|
}
|
|
72
76
|
/**
|
|
73
|
-
* Response from SEND_CREDENTIAL action
|
|
77
|
+
* Response from SEND_CREDENTIAL action (raw credential)
|
|
74
78
|
*/
|
|
75
79
|
interface SendCredentialResponse {
|
|
76
80
|
credentialId: string;
|
|
77
81
|
[key: string]: unknown;
|
|
78
82
|
}
|
|
83
|
+
/**
|
|
84
|
+
* Template-based credential input for sendCredential
|
|
85
|
+
* Uses a pre-configured boost template to issue a credential
|
|
86
|
+
*/
|
|
87
|
+
interface TemplateCredentialInput {
|
|
88
|
+
/** Alias of the boost template configured for this app */
|
|
89
|
+
templateAlias: string;
|
|
90
|
+
/** Optional template data for Mustache-style variable substitution */
|
|
91
|
+
templateData?: Record<string, unknown>;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Response from template-based credential issuance
|
|
95
|
+
*/
|
|
96
|
+
interface TemplateCredentialResponse {
|
|
97
|
+
/** URI of the issued credential */
|
|
98
|
+
credentialUri: string;
|
|
99
|
+
/** URI of the boost template used */
|
|
100
|
+
boostUri: string;
|
|
101
|
+
[key: string]: unknown;
|
|
102
|
+
}
|
|
79
103
|
/**
|
|
80
104
|
* Verifiable Presentation Request Query types
|
|
81
105
|
*/
|
|
@@ -112,6 +136,16 @@ interface CredentialSearchResponse {
|
|
|
112
136
|
interface CredentialSpecificResponse {
|
|
113
137
|
credential?: unknown;
|
|
114
138
|
}
|
|
139
|
+
/**
|
|
140
|
+
* Options for REQUEST_CONSENT action
|
|
141
|
+
*/
|
|
142
|
+
interface RequestConsentOptions {
|
|
143
|
+
/**
|
|
144
|
+
* If true, redirect to contract's redirectUrl with VP after consent.
|
|
145
|
+
* Default: false
|
|
146
|
+
*/
|
|
147
|
+
redirect?: boolean;
|
|
148
|
+
}
|
|
115
149
|
/**
|
|
116
150
|
* Response from REQUEST_CONSENT action
|
|
117
151
|
*/
|
|
@@ -162,7 +196,7 @@ interface PostMessageResponse {
|
|
|
162
196
|
interface PendingRequest {
|
|
163
197
|
resolve: (value: unknown) => void;
|
|
164
198
|
reject: (error: LearnCardError) => void;
|
|
165
|
-
timeoutId:
|
|
199
|
+
timeoutId: ReturnType<typeof setTimeout>;
|
|
166
200
|
}
|
|
167
201
|
|
|
168
202
|
/**
|
|
@@ -253,10 +287,14 @@ declare class PartnerConnect {
|
|
|
253
287
|
/**
|
|
254
288
|
* Send a credential to the user's LearnCard wallet
|
|
255
289
|
*
|
|
256
|
-
*
|
|
257
|
-
*
|
|
290
|
+
* Supports two modes:
|
|
291
|
+
* 1. **Raw credential**: Pass a full verifiable credential object
|
|
292
|
+
* 2. **Template-based**: Pass `{ templateAlias, templateData }` to issue from a pre-configured boost template
|
|
258
293
|
*
|
|
259
|
-
* @
|
|
294
|
+
* @param input - Either a verifiable credential or a template-based input
|
|
295
|
+
* @returns Promise resolving to credential response
|
|
296
|
+
*
|
|
297
|
+
* @example Raw credential
|
|
260
298
|
* ```typescript
|
|
261
299
|
* const response = await learnCard.sendCredential({
|
|
262
300
|
* '@context': ['https://www.w3.org/2018/credentials/v1'],
|
|
@@ -265,8 +303,17 @@ declare class PartnerConnect {
|
|
|
265
303
|
* });
|
|
266
304
|
* console.log('Credential ID:', response.credentialId);
|
|
267
305
|
* ```
|
|
306
|
+
*
|
|
307
|
+
* @example Template-based (for App Store apps)
|
|
308
|
+
* ```typescript
|
|
309
|
+
* const response = await learnCard.sendCredential({
|
|
310
|
+
* templateAlias: 'achievement-badge',
|
|
311
|
+
* templateData: { score: '95', courseName: 'Web Dev 101' }
|
|
312
|
+
* });
|
|
313
|
+
* console.log('Credential URI:', response.credentialUri);
|
|
314
|
+
* ```
|
|
268
315
|
*/
|
|
269
|
-
sendCredential(
|
|
316
|
+
sendCredential(input: unknown | TemplateCredentialInput): Promise<SendCredentialResponse | TemplateCredentialResponse>;
|
|
270
317
|
/**
|
|
271
318
|
* Launch a feature in the LearnCard host application
|
|
272
319
|
*
|
|
@@ -331,13 +378,20 @@ declare class PartnerConnect {
|
|
|
331
378
|
*
|
|
332
379
|
* @example
|
|
333
380
|
* ```typescript
|
|
381
|
+
* // Without redirect (default) - returns VP in response if app owns the contract
|
|
334
382
|
* const response = await learnCard.requestConsent('lc:network:network.learncard.com/trpc:contract:abc123');
|
|
335
383
|
* if (response.granted) {
|
|
336
384
|
* console.log('User granted consent');
|
|
385
|
+
* if (response.vp) {
|
|
386
|
+
* console.log('VP:', response.vp);
|
|
387
|
+
* }
|
|
337
388
|
* }
|
|
389
|
+
*
|
|
390
|
+
* // With redirect - redirects to contract's redirectUrl with VP in URL params
|
|
391
|
+
* const response = await learnCard.requestConsent('lc:network:network.learncard.com/trpc:contract:abc123', { redirect: true });
|
|
338
392
|
* ```
|
|
339
393
|
*/
|
|
340
|
-
requestConsent(contractUri: string): Promise<ConsentResponse>;
|
|
394
|
+
requestConsent(contractUri: string, options?: RequestConsentOptions): Promise<ConsentResponse>;
|
|
341
395
|
/**
|
|
342
396
|
* Initiate a template-based credential issuance flow
|
|
343
397
|
*
|
|
@@ -358,6 +412,28 @@ declare class PartnerConnect {
|
|
|
358
412
|
* ```
|
|
359
413
|
*/
|
|
360
414
|
initiateTemplateIssue(templateId: string, draftRecipients?: string[]): Promise<TemplateIssueResponse>;
|
|
415
|
+
/**
|
|
416
|
+
* Send a generic event to be processed by the brain service on behalf of this app.
|
|
417
|
+
* This is used for backend-like operations such as issuing credentials.
|
|
418
|
+
*
|
|
419
|
+
* @param event - The event payload to send
|
|
420
|
+
* @returns Promise resolving to the event response
|
|
421
|
+
*
|
|
422
|
+
* @example
|
|
423
|
+
* ```typescript
|
|
424
|
+
* // Issue a credential to the current user
|
|
425
|
+
* const response = await learnCard.sendAppEvent({
|
|
426
|
+
* type: 'send-credential',
|
|
427
|
+
* templateAlias: 'achievement-badge',
|
|
428
|
+
* templateData: { score: '95' }
|
|
429
|
+
* });
|
|
430
|
+
*
|
|
431
|
+
* if (response.credentialUri) {
|
|
432
|
+
* console.log('Credential issued:', response.credentialUri);
|
|
433
|
+
* }
|
|
434
|
+
* ```
|
|
435
|
+
*/
|
|
436
|
+
sendAppEvent(event: AppEvent): Promise<AppEventResponse>;
|
|
361
437
|
/**
|
|
362
438
|
* Clean up the SDK and remove event listeners
|
|
363
439
|
*/
|
|
@@ -380,4 +456,4 @@ declare class PartnerConnect {
|
|
|
380
456
|
*/
|
|
381
457
|
declare function createPartnerConnect(options: PartnerConnectOptions): PartnerConnect;
|
|
382
458
|
|
|
383
|
-
export { ConsentResponse, CredentialSearchResponse, CredentialSpecificResponse, ErrorCode, IdentityResponse, LearnCardError, PartnerConnect, PartnerConnectOptions, PendingRequest, PostMessageRequest, PostMessageResponse, SendCredentialResponse, TemplateIssueResponse, VPRQuery, VerifiablePresentationRequest, createPartnerConnect, createPartnerConnect as default };
|
|
459
|
+
export { ConsentResponse, CredentialSearchResponse, CredentialSpecificResponse, ErrorCode, IdentityResponse, LearnCardError, PartnerConnect, PartnerConnectOptions, PendingRequest, PostMessageRequest, PostMessageResponse, RequestConsentOptions, SendCredentialResponse, TemplateCredentialInput, TemplateCredentialResponse, TemplateIssueResponse, VPRQuery, VerifiablePresentationRequest, createPartnerConnect, createPartnerConnect as default };
|
|
@@ -26,7 +26,7 @@ const _PartnerConnect = class _PartnerConnect {
|
|
|
26
26
|
* 1. Check for `lc_host_override` query parameter (for staging/testing)
|
|
27
27
|
* 2. Fall back to first configured origin
|
|
28
28
|
* 3. Fall back to DEFAULT_HOST_ORIGIN
|
|
29
|
-
*
|
|
29
|
+
*
|
|
30
30
|
* This origin will be used for all outgoing messages and incoming message validation.
|
|
31
31
|
*/
|
|
32
32
|
configureActiveOrigin() {
|
|
@@ -60,7 +60,7 @@ const _PartnerConnect = class _PartnerConnect {
|
|
|
60
60
|
}
|
|
61
61
|
}
|
|
62
62
|
isOriginNativeApp(origin) {
|
|
63
|
-
return origin.startsWith("capacitor://") || origin.startsWith("ionic://") || origin.startsWith("https://localhost") || origin.startsWith("http://localhost") || origin.startsWith("http://127.0.0.1")
|
|
63
|
+
return origin.startsWith("capacitor://") || origin.startsWith("ionic://") || origin.startsWith("https://localhost") || origin.startsWith("http://localhost") || origin.startsWith("http://127.0.0.1");
|
|
64
64
|
}
|
|
65
65
|
/**
|
|
66
66
|
* Check if an origin is in the configured whitelist
|
|
@@ -70,10 +70,10 @@ const _PartnerConnect = class _PartnerConnect {
|
|
|
70
70
|
}
|
|
71
71
|
/**
|
|
72
72
|
* Check if an event origin is valid against the active host origin
|
|
73
|
-
*
|
|
73
|
+
*
|
|
74
74
|
* Security Rule: Incoming messages must exactly match the active host origin.
|
|
75
75
|
* This prevents malicious actors from spoofing origins via query parameters.
|
|
76
|
-
*
|
|
76
|
+
*
|
|
77
77
|
* @param eventOrigin - The origin from the MessageEvent
|
|
78
78
|
* @returns true if the origin is valid
|
|
79
79
|
*/
|
|
@@ -104,7 +104,9 @@ const _PartnerConnect = class _PartnerConnect {
|
|
|
104
104
|
if (data.type === "SUCCESS") {
|
|
105
105
|
pending.resolve(data.data);
|
|
106
106
|
} else if (data.type === "ERROR") {
|
|
107
|
-
pending.reject(
|
|
107
|
+
pending.reject(
|
|
108
|
+
data.error || { code: "UNKNOWN_ERROR", message: "An unknown error occurred" }
|
|
109
|
+
);
|
|
108
110
|
}
|
|
109
111
|
};
|
|
110
112
|
window.addEventListener("message", this.messageListener);
|
|
@@ -153,10 +155,10 @@ const _PartnerConnect = class _PartnerConnect {
|
|
|
153
155
|
}
|
|
154
156
|
/**
|
|
155
157
|
* Request user identity (Single Sign-On)
|
|
156
|
-
*
|
|
158
|
+
*
|
|
157
159
|
* @returns Promise resolving to user identity including DID and JWT token
|
|
158
160
|
* @throws {LearnCardError} When user is not authenticated or request fails
|
|
159
|
-
*
|
|
161
|
+
*
|
|
160
162
|
* @example
|
|
161
163
|
* ```typescript
|
|
162
164
|
* const identity = await learnCard.requestIdentity();
|
|
@@ -165,15 +167,21 @@ const _PartnerConnect = class _PartnerConnect {
|
|
|
165
167
|
* ```
|
|
166
168
|
*/
|
|
167
169
|
requestIdentity() {
|
|
168
|
-
return this.sendMessage("REQUEST_IDENTITY", {
|
|
170
|
+
return this.sendMessage("REQUEST_IDENTITY", {
|
|
171
|
+
challenge: `${Date.now()}-${Math.random().toString(36).substring(2, 9)}`
|
|
172
|
+
});
|
|
169
173
|
}
|
|
170
174
|
/**
|
|
171
175
|
* Send a credential to the user's LearnCard wallet
|
|
172
|
-
*
|
|
173
|
-
*
|
|
174
|
-
*
|
|
175
|
-
*
|
|
176
|
-
*
|
|
176
|
+
*
|
|
177
|
+
* Supports two modes:
|
|
178
|
+
* 1. **Raw credential**: Pass a full verifiable credential object
|
|
179
|
+
* 2. **Template-based**: Pass `{ templateAlias, templateData }` to issue from a pre-configured boost template
|
|
180
|
+
*
|
|
181
|
+
* @param input - Either a verifiable credential or a template-based input
|
|
182
|
+
* @returns Promise resolving to credential response
|
|
183
|
+
*
|
|
184
|
+
* @example Raw credential
|
|
177
185
|
* ```typescript
|
|
178
186
|
* const response = await learnCard.sendCredential({
|
|
179
187
|
* '@context': ['https://www.w3.org/2018/credentials/v1'],
|
|
@@ -182,16 +190,33 @@ const _PartnerConnect = class _PartnerConnect {
|
|
|
182
190
|
* });
|
|
183
191
|
* console.log('Credential ID:', response.credentialId);
|
|
184
192
|
* ```
|
|
193
|
+
*
|
|
194
|
+
* @example Template-based (for App Store apps)
|
|
195
|
+
* ```typescript
|
|
196
|
+
* const response = await learnCard.sendCredential({
|
|
197
|
+
* templateAlias: 'achievement-badge',
|
|
198
|
+
* templateData: { score: '95', courseName: 'Web Dev 101' }
|
|
199
|
+
* });
|
|
200
|
+
* console.log('Credential URI:', response.credentialUri);
|
|
201
|
+
* ```
|
|
185
202
|
*/
|
|
186
|
-
sendCredential(
|
|
187
|
-
|
|
203
|
+
sendCredential(input) {
|
|
204
|
+
if (input && typeof input === "object" && "templateAlias" in input && typeof input.templateAlias === "string") {
|
|
205
|
+
const templateInput = input;
|
|
206
|
+
return this.sendAppEvent({
|
|
207
|
+
type: "send-credential",
|
|
208
|
+
templateAlias: templateInput.templateAlias,
|
|
209
|
+
templateData: templateInput.templateData
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
return this.sendMessage("SEND_CREDENTIAL", { credential: input });
|
|
188
213
|
}
|
|
189
214
|
/**
|
|
190
215
|
* Launch a feature in the LearnCard host application
|
|
191
|
-
*
|
|
216
|
+
*
|
|
192
217
|
* @param featurePath - Path to the feature (e.g., '/ai/topics')
|
|
193
218
|
* @param initialPrompt - Optional initial prompt or data
|
|
194
|
-
*
|
|
219
|
+
*
|
|
195
220
|
* @example
|
|
196
221
|
* ```typescript
|
|
197
222
|
* await learnCard.launchFeature(
|
|
@@ -205,10 +230,10 @@ const _PartnerConnect = class _PartnerConnect {
|
|
|
205
230
|
}
|
|
206
231
|
/**
|
|
207
232
|
* Request credentials from the user's wallet using a query
|
|
208
|
-
*
|
|
233
|
+
*
|
|
209
234
|
* @param verifiablePresentationRequest - VPR with query criteria
|
|
210
235
|
* @returns Promise resolving to verifiable presentation
|
|
211
|
-
*
|
|
236
|
+
*
|
|
212
237
|
* @example
|
|
213
238
|
* ```typescript
|
|
214
239
|
* const response = await learnCard.askCredentialSearch({
|
|
@@ -222,7 +247,7 @@ const _PartnerConnect = class _PartnerConnect {
|
|
|
222
247
|
* challenge: 'challenge-123',
|
|
223
248
|
* domain: window.location.hostname
|
|
224
249
|
* });
|
|
225
|
-
*
|
|
250
|
+
*
|
|
226
251
|
* if (response.verifiablePresentation) {
|
|
227
252
|
* console.log('Received credentials:', response.verifiablePresentation.verifiableCredential);
|
|
228
253
|
* }
|
|
@@ -235,10 +260,10 @@ const _PartnerConnect = class _PartnerConnect {
|
|
|
235
260
|
}
|
|
236
261
|
/**
|
|
237
262
|
* Request a specific credential by ID
|
|
238
|
-
*
|
|
263
|
+
*
|
|
239
264
|
* @param credentialId - The ID of the credential to request
|
|
240
265
|
* @returns Promise resolving to the credential
|
|
241
|
-
*
|
|
266
|
+
*
|
|
242
267
|
* @example
|
|
243
268
|
* ```typescript
|
|
244
269
|
* const response = await learnCard.askCredentialSpecific('credential-id-123');
|
|
@@ -254,35 +279,46 @@ const _PartnerConnect = class _PartnerConnect {
|
|
|
254
279
|
}
|
|
255
280
|
/**
|
|
256
281
|
* Request user consent for permissions
|
|
257
|
-
*
|
|
282
|
+
*
|
|
258
283
|
* @param contractUri - URI of the consent contract
|
|
259
284
|
* @returns Promise resolving to consent response
|
|
260
|
-
*
|
|
285
|
+
*
|
|
261
286
|
* @example
|
|
262
287
|
* ```typescript
|
|
288
|
+
* // Without redirect (default) - returns VP in response if app owns the contract
|
|
263
289
|
* const response = await learnCard.requestConsent('lc:network:network.learncard.com/trpc:contract:abc123');
|
|
264
290
|
* if (response.granted) {
|
|
265
291
|
* console.log('User granted consent');
|
|
292
|
+
* if (response.vp) {
|
|
293
|
+
* console.log('VP:', response.vp);
|
|
294
|
+
* }
|
|
266
295
|
* }
|
|
296
|
+
*
|
|
297
|
+
* // With redirect - redirects to contract's redirectUrl with VP in URL params
|
|
298
|
+
* const response = await learnCard.requestConsent('lc:network:network.learncard.com/trpc:contract:abc123', { redirect: true });
|
|
267
299
|
* ```
|
|
268
300
|
*/
|
|
269
|
-
requestConsent(contractUri) {
|
|
270
|
-
|
|
301
|
+
requestConsent(contractUri, options = {}) {
|
|
302
|
+
const { redirect = false } = options;
|
|
303
|
+
return this.sendMessage("REQUEST_CONSENT", {
|
|
304
|
+
contractUri,
|
|
305
|
+
redirect
|
|
306
|
+
});
|
|
271
307
|
}
|
|
272
308
|
/**
|
|
273
309
|
* Initiate a template-based credential issuance flow
|
|
274
|
-
*
|
|
310
|
+
*
|
|
275
311
|
* @param templateId - ID of the template/boost to issue
|
|
276
312
|
* @param draftRecipients - Optional array of recipient DIDs
|
|
277
313
|
* @returns Promise resolving to template issue response
|
|
278
|
-
*
|
|
314
|
+
*
|
|
279
315
|
* @example
|
|
280
316
|
* ```typescript
|
|
281
317
|
* const response = await learnCard.initiateTemplateIssue(
|
|
282
318
|
* 'lc:network:network.learncard.com/trpc:boost:xyz789',
|
|
283
319
|
* ['did:key:abc', 'did:key:def']
|
|
284
320
|
* );
|
|
285
|
-
*
|
|
321
|
+
*
|
|
286
322
|
* if (response.issued) {
|
|
287
323
|
* console.log('Template issued successfully');
|
|
288
324
|
* }
|
|
@@ -294,6 +330,30 @@ const _PartnerConnect = class _PartnerConnect {
|
|
|
294
330
|
draftRecipients: draftRecipients || []
|
|
295
331
|
});
|
|
296
332
|
}
|
|
333
|
+
/**
|
|
334
|
+
* Send a generic event to be processed by the brain service on behalf of this app.
|
|
335
|
+
* This is used for backend-like operations such as issuing credentials.
|
|
336
|
+
*
|
|
337
|
+
* @param event - The event payload to send
|
|
338
|
+
* @returns Promise resolving to the event response
|
|
339
|
+
*
|
|
340
|
+
* @example
|
|
341
|
+
* ```typescript
|
|
342
|
+
* // Issue a credential to the current user
|
|
343
|
+
* const response = await learnCard.sendAppEvent({
|
|
344
|
+
* type: 'send-credential',
|
|
345
|
+
* templateAlias: 'achievement-badge',
|
|
346
|
+
* templateData: { score: '95' }
|
|
347
|
+
* });
|
|
348
|
+
*
|
|
349
|
+
* if (response.credentialUri) {
|
|
350
|
+
* console.log('Credential issued:', response.credentialUri);
|
|
351
|
+
* }
|
|
352
|
+
* ```
|
|
353
|
+
*/
|
|
354
|
+
sendAppEvent(event) {
|
|
355
|
+
return this.sendMessage("APP_EVENT", event);
|
|
356
|
+
}
|
|
297
357
|
/**
|
|
298
358
|
* Clean up the SDK and remove event listeners
|
|
299
359
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"partner-connect.esm.js","sources":["../src/index.ts"],"sourcesContent":["/**\n * LearnCard Partner Connect SDK\n * \n * A Promise-based JavaScript utility for managing cross-origin message communication\n * between partner apps and the LearnCard host application.\n * \n * @example\n * ```typescript\n * import { createPartnerConnect } from '@learncard/partner-connect';\n * \n * const learnCard = createPartnerConnect({\n * hostOrigin: 'https://learncard.app'\n * });\n * \n * // Request user identity (SSO)\n * const identity = await learnCard.requestIdentity();\n * console.log('User DID:', identity.user.did);\n * ```\n */\n\nimport type {\n PartnerConnectOptions,\n IdentityResponse,\n SendCredentialResponse,\n VerifiablePresentationRequest,\n CredentialSearchResponse,\n CredentialSpecificResponse,\n ConsentResponse,\n TemplateIssueResponse,\n LearnCardError,\n PostMessageRequest,\n PostMessageResponse,\n PendingRequest,\n} from './types';\n\nexport type * from './types';\n\n/**\n * LearnCard Partner Connect SDK class\n */\nexport class PartnerConnect {\n /** Default host origin (security anchor) */\n public static readonly DEFAULT_HOST_ORIGIN = 'https://learncard.app';\n \n private hostOrigins: string[] = ['https://learncard.app'];\n private activeHostOrigin: string = 'https://learncard.app';\n private allowNativeAppOrigins: boolean = true;\n private protocol: string = 'LEARNCARD_V1';\n private requestTimeout: number = 30000;\n private pendingRequests: Map<string, PendingRequest>;\n private messageListener: ((event: MessageEvent) => void) | null = null;\n private isInitialized = false;\n\n constructor(options?: PartnerConnectOptions) {\n // Normalize hostOrigin to an array for whitelist validation\n const hostOrigin = options?.hostOrigin || PartnerConnect.DEFAULT_HOST_ORIGIN;\n this.hostOrigins = Array.isArray(hostOrigin) ? hostOrigin : [hostOrigin];\n \n this.protocol = options?.protocol || 'LEARNCARD_V1';\n this.requestTimeout = options?.requestTimeout || 30000;\n this.allowNativeAppOrigins = options?.allowNativeAppOrigins ?? true;\n this.pendingRequests = new Map();\n this.configureActiveOrigin();\n this.setupMessageListener();\n }\n\n /**\n * Configure the active host origin using the following hierarchy:\n * 1. Check for `lc_host_override` query parameter (for staging/testing)\n * 2. Fall back to first configured origin\n * 3. Fall back to DEFAULT_HOST_ORIGIN\n * \n * This origin will be used for all outgoing messages and incoming message validation.\n */\n private configureActiveOrigin(): void {\n if (typeof window === 'undefined') {\n this.activeHostOrigin = this.hostOrigins[0] || PartnerConnect.DEFAULT_HOST_ORIGIN;\n return;\n }\n\n try {\n // Check for lc_host_override query parameter\n const urlParams = new URLSearchParams(window.location.search);\n const hostOverride = urlParams.get('lc_host_override');\n\n if (hostOverride) {\n // Validate override against whitelist (if provided)\n if (this.hostOrigins.length > 0 && !this.isOriginInWhitelist(hostOverride)) {\n console.warn(\n '[LearnCard SDK] lc_host_override value is not in the configured whitelist:',\n hostOverride,\n 'Allowed:',\n this.hostOrigins\n );\n this.activeHostOrigin = this.hostOrigins[0] || PartnerConnect.DEFAULT_HOST_ORIGIN;\n } else {\n this.activeHostOrigin = hostOverride;\n console.log('[LearnCard SDK] Using lc_host_override:', hostOverride);\n }\n } else {\n // Use first configured origin or default\n this.activeHostOrigin = this.hostOrigins[0] || PartnerConnect.DEFAULT_HOST_ORIGIN;\n console.log('[LearnCard SDK] Using configured origin:', this.activeHostOrigin);\n }\n } catch (error) {\n console.error('[LearnCard SDK] Error configuring active origin:', error);\n this.activeHostOrigin = this.hostOrigins[0] || PartnerConnect.DEFAULT_HOST_ORIGIN;\n }\n }\n\n private isOriginNativeApp(origin: string): boolean {\n return origin.startsWith('capacitor://') || origin.startsWith('ionic://') || origin.startsWith('https://localhost') || origin.startsWith('http://localhost') || origin.startsWith('http://127.0.0.1') || origin.startsWith('http://192.168.1.164')\n }\n\n /**\n * Check if an origin is in the configured whitelist\n */\n private isOriginInWhitelist(origin: string): boolean {\n return this.hostOrigins.includes(origin) || (this.allowNativeAppOrigins && this.isOriginNativeApp(origin));\n }\n\n /**\n * Check if an event origin is valid against the active host origin\n * \n * Security Rule: Incoming messages must exactly match the active host origin.\n * This prevents malicious actors from spoofing origins via query parameters.\n * \n * @param eventOrigin - The origin from the MessageEvent\n * @returns true if the origin is valid\n */\n private isValidOrigin(eventOrigin: string): boolean {\n // STRICT: Exact match with active host origin only\n return eventOrigin === this.activeHostOrigin;\n }\n\n /**\n * Set up the central message listener to handle responses from the LearnCard host\n */\n private setupMessageListener(): void {\n if (typeof window === 'undefined') {\n throw new Error('PartnerConnect SDK can only be used in a browser environment');\n }\n\n this.messageListener = (event: MessageEvent) => {\n // SECURITY CHECK 1: Strict origin validation - must exactly match active host origin\n if (!this.isValidOrigin(event.origin)) {\n // Silently ignore messages from unauthorized origins\n return;\n }\n\n const data = event.data as PostMessageResponse;\n\n // SECURITY CHECK 2: Validate protocol and requestId\n if (data.protocol !== this.protocol || !data.requestId) {\n return;\n }\n\n // Look up the pending request\n const pending = this.pendingRequests.get(data.requestId);\n if (!pending) {\n return; // Ignore stale or unrecognized responses\n }\n\n // Clean up\n clearTimeout(pending.timeoutId);\n this.pendingRequests.delete(data.requestId);\n\n // Resolve or reject the promise\n if (data.type === 'SUCCESS') {\n pending.resolve(data.data);\n } else if (data.type === 'ERROR') {\n pending.reject(data.error || { code: 'UNKNOWN_ERROR', message: 'An unknown error occurred' });\n }\n };\n\n window.addEventListener('message', this.messageListener);\n this.isInitialized = true;\n }\n\n /**\n * Generate a unique request ID\n */\n private generateRequestId(action: string): string {\n return `${action}-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;\n }\n\n /**\n * Send a message to the parent window and return a Promise\n */\n private sendMessage<T = unknown>(action: string, payload?: unknown): Promise<T> {\n if (!this.isInitialized) {\n return Promise.reject({\n code: 'SDK_NOT_INITIALIZED',\n message: 'SDK is not initialized',\n } as LearnCardError);\n }\n\n return new Promise<T>((resolve, reject) => {\n const requestId = this.generateRequestId(action);\n\n // Set up timeout\n const timeoutId = setTimeout(() => {\n if (this.pendingRequests.has(requestId)) {\n this.pendingRequests.delete(requestId);\n reject({\n code: 'LC_TIMEOUT',\n message: `Request ${action} timed out after ${this.requestTimeout}ms`,\n } as LearnCardError);\n }\n }, this.requestTimeout);\n\n // Store the pending request\n this.pendingRequests.set(requestId, {\n resolve: resolve as (value: unknown) => void,\n reject,\n timeoutId,\n });\n\n // Construct the message\n const message: PostMessageRequest = {\n protocol: this.protocol,\n action,\n requestId,\n payload,\n };\n\n // Send to parent window with the active host origin (configured or overridden)\n window.parent.postMessage(message, this.activeHostOrigin);\n });\n }\n\n /**\n * Request user identity (Single Sign-On)\n * \n * @returns Promise resolving to user identity including DID and JWT token\n * @throws {LearnCardError} When user is not authenticated or request fails\n * \n * @example\n * ```typescript\n * const identity = await learnCard.requestIdentity();\n * console.log('User DID:', identity.user.did);\n * console.log('JWT Token:', identity.token);\n * ```\n */\n public requestIdentity(): Promise<IdentityResponse> {\n return this.sendMessage<IdentityResponse>('REQUEST_IDENTITY', { challenge: `${Date.now()}-${Math.random().toString(36).substring(2, 9)}` });\n }\n\n /**\n * Send a credential to the user's LearnCard wallet\n * \n * @param credential - The verifiable credential to send\n * @returns Promise resolving to credential ID\n * \n * @example\n * ```typescript\n * const response = await learnCard.sendCredential({\n * '@context': ['https://www.w3.org/2018/credentials/v1'],\n * type: ['VerifiableCredential'],\n * credentialSubject: { id: 'did:example:123' }\n * });\n * console.log('Credential ID:', response.credentialId);\n * ```\n */\n public sendCredential(credential: unknown): Promise<SendCredentialResponse> {\n return this.sendMessage<SendCredentialResponse>('SEND_CREDENTIAL', { credential });\n }\n\n /**\n * Launch a feature in the LearnCard host application\n * \n * @param featurePath - Path to the feature (e.g., '/ai/topics')\n * @param initialPrompt - Optional initial prompt or data\n * \n * @example\n * ```typescript\n * await learnCard.launchFeature(\n * '/ai/topics?shortCircuitStep=newTopic',\n * 'Help me understand cryptography'\n * );\n * ```\n */\n public launchFeature(featurePath: string, initialPrompt?: string): Promise<void> {\n return this.sendMessage<void>('LAUNCH_FEATURE', { featurePath, initialPrompt });\n }\n\n /**\n * Request credentials from the user's wallet using a query\n * \n * @param verifiablePresentationRequest - VPR with query criteria\n * @returns Promise resolving to verifiable presentation\n * \n * @example\n * ```typescript\n * const response = await learnCard.askCredentialSearch({\n * query: [{\n * type: 'QueryByTitle',\n * credentialQuery: {\n * reason: 'We need to verify your skills',\n * title: 'JavaScript Expert'\n * }\n * }],\n * challenge: 'challenge-123',\n * domain: window.location.hostname\n * });\n * \n * if (response.verifiablePresentation) {\n * console.log('Received credentials:', response.verifiablePresentation.verifiableCredential);\n * }\n * ```\n */\n public askCredentialSearch(\n verifiablePresentationRequest: VerifiablePresentationRequest\n ): Promise<CredentialSearchResponse> {\n return this.sendMessage<CredentialSearchResponse>('ASK_CREDENTIAL_SEARCH', {\n verifiablePresentationRequest,\n });\n }\n\n /**\n * Request a specific credential by ID\n * \n * @param credentialId - The ID of the credential to request\n * @returns Promise resolving to the credential\n * \n * @example\n * ```typescript\n * const response = await learnCard.askCredentialSpecific('credential-id-123');\n * if (response.credential) {\n * console.log('Received credential:', response.credential);\n * }\n * ```\n */\n public askCredentialSpecific(credentialId: string): Promise<CredentialSpecificResponse> {\n return this.sendMessage<CredentialSpecificResponse>('ASK_CREDENTIAL_SPECIFIC', {\n credentialId,\n });\n }\n\n /**\n * Request user consent for permissions\n * \n * @param contractUri - URI of the consent contract\n * @returns Promise resolving to consent response\n * \n * @example\n * ```typescript\n * const response = await learnCard.requestConsent('lc:network:network.learncard.com/trpc:contract:abc123');\n * if (response.granted) {\n * console.log('User granted consent');\n * }\n * ```\n */\n public requestConsent(contractUri: string): Promise<ConsentResponse> {\n return this.sendMessage<ConsentResponse>('REQUEST_CONSENT', { contractUri });\n }\n\n /**\n * Initiate a template-based credential issuance flow\n * \n * @param templateId - ID of the template/boost to issue\n * @param draftRecipients - Optional array of recipient DIDs\n * @returns Promise resolving to template issue response\n * \n * @example\n * ```typescript\n * const response = await learnCard.initiateTemplateIssue(\n * 'lc:network:network.learncard.com/trpc:boost:xyz789',\n * ['did:key:abc', 'did:key:def']\n * );\n * \n * if (response.issued) {\n * console.log('Template issued successfully');\n * }\n * ```\n */\n public initiateTemplateIssue(\n templateId: string,\n draftRecipients?: string[]\n ): Promise<TemplateIssueResponse> {\n return this.sendMessage<TemplateIssueResponse>('INITIATE_TEMPLATE_ISSUE', {\n templateId,\n draftRecipients: draftRecipients || [],\n });\n }\n\n /**\n * Clean up the SDK and remove event listeners\n */\n public destroy(): void {\n if (this.messageListener) {\n window.removeEventListener('message', this.messageListener);\n this.messageListener = null;\n }\n\n // Reject all pending requests\n for (const [requestId, pending] of this.pendingRequests.entries()) {\n clearTimeout(pending.timeoutId);\n pending.reject({\n code: 'SDK_DESTROYED',\n message: 'SDK was destroyed before request completed',\n });\n }\n\n this.pendingRequests.clear();\n this.isInitialized = false;\n }\n}\n\n/**\n * Factory function to create a PartnerConnect instance\n * \n * @param options - Configuration options\n * @returns PartnerConnect instance\n * \n * @example\n * ```typescript\n * const learnCard = createPartnerConnect({\n * hostOrigin: 'https://learncard.app',\n * protocol: 'LEARNCARD_V1',\n * requestTimeout: 30000\n * });\n * ```\n */\nexport function createPartnerConnect(options: PartnerConnectOptions): PartnerConnect {\n return new PartnerConnect(options);\n}\n\n// Default export for convenience\nexport default createPartnerConnect;\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"partner-connect.esm.js","sources":["../src/index.ts"],"sourcesContent":["/**\n * LearnCard Partner Connect SDK\n *\n * A Promise-based JavaScript utility for managing cross-origin message communication\n * between partner apps and the LearnCard host application.\n *\n * @example\n * ```typescript\n * import { createPartnerConnect } from '@learncard/partner-connect';\n *\n * const learnCard = createPartnerConnect({\n * hostOrigin: 'https://learncard.app'\n * });\n *\n * // Request user identity (SSO)\n * const identity = await learnCard.requestIdentity();\n * console.log('User DID:', identity.user.did);\n * ```\n */\n\nimport type {\n PartnerConnectOptions,\n IdentityResponse,\n SendCredentialResponse,\n TemplateCredentialInput,\n TemplateCredentialResponse,\n VerifiablePresentationRequest,\n CredentialSearchResponse,\n CredentialSpecificResponse,\n ConsentResponse,\n RequestConsentOptions,\n TemplateIssueResponse,\n AppEvent,\n AppEventResponse,\n LearnCardError,\n PostMessageRequest,\n PostMessageResponse,\n PendingRequest,\n} from './types';\n\nexport type * from './types';\n\n/**\n * LearnCard Partner Connect SDK class\n */\nexport class PartnerConnect {\n /** Default host origin (security anchor) */\n public static readonly DEFAULT_HOST_ORIGIN = 'https://learncard.app';\n\n private hostOrigins: string[] = ['https://learncard.app'];\n private activeHostOrigin: string = 'https://learncard.app';\n private allowNativeAppOrigins: boolean = true;\n private protocol: string = 'LEARNCARD_V1';\n private requestTimeout: number = 30000;\n private pendingRequests: Map<string, PendingRequest>;\n private messageListener: ((event: MessageEvent) => void) | null = null;\n private isInitialized = false;\n\n constructor(options?: PartnerConnectOptions) {\n // Normalize hostOrigin to an array for whitelist validation\n const hostOrigin = options?.hostOrigin || PartnerConnect.DEFAULT_HOST_ORIGIN;\n this.hostOrigins = Array.isArray(hostOrigin) ? hostOrigin : [hostOrigin];\n\n this.protocol = options?.protocol || 'LEARNCARD_V1';\n this.requestTimeout = options?.requestTimeout || 30000;\n this.allowNativeAppOrigins = options?.allowNativeAppOrigins ?? true;\n this.pendingRequests = new Map();\n this.configureActiveOrigin();\n this.setupMessageListener();\n }\n\n /**\n * Configure the active host origin using the following hierarchy:\n * 1. Check for `lc_host_override` query parameter (for staging/testing)\n * 2. Fall back to first configured origin\n * 3. Fall back to DEFAULT_HOST_ORIGIN\n *\n * This origin will be used for all outgoing messages and incoming message validation.\n */\n private configureActiveOrigin(): void {\n if (typeof window === 'undefined') {\n this.activeHostOrigin = this.hostOrigins[0] || PartnerConnect.DEFAULT_HOST_ORIGIN;\n return;\n }\n\n try {\n // Check for lc_host_override query parameter\n const urlParams = new URLSearchParams(window.location.search);\n const hostOverride = urlParams.get('lc_host_override');\n\n if (hostOverride) {\n // Validate override against whitelist (if provided)\n if (this.hostOrigins.length > 0 && !this.isOriginInWhitelist(hostOverride)) {\n console.warn(\n '[LearnCard SDK] lc_host_override value is not in the configured whitelist:',\n hostOverride,\n 'Allowed:',\n this.hostOrigins\n );\n this.activeHostOrigin =\n this.hostOrigins[0] || PartnerConnect.DEFAULT_HOST_ORIGIN;\n } else {\n this.activeHostOrigin = hostOverride;\n console.log('[LearnCard SDK] Using lc_host_override:', hostOverride);\n }\n } else {\n // Use first configured origin or default\n this.activeHostOrigin = this.hostOrigins[0] || PartnerConnect.DEFAULT_HOST_ORIGIN;\n console.log('[LearnCard SDK] Using configured origin:', this.activeHostOrigin);\n }\n } catch (error) {\n console.error('[LearnCard SDK] Error configuring active origin:', error);\n this.activeHostOrigin = this.hostOrigins[0] || PartnerConnect.DEFAULT_HOST_ORIGIN;\n }\n }\n\n private isOriginNativeApp(origin: string): boolean {\n return (\n origin.startsWith('capacitor://') ||\n origin.startsWith('ionic://') ||\n origin.startsWith('https://localhost') ||\n origin.startsWith('http://localhost') ||\n origin.startsWith('http://127.0.0.1')\n );\n }\n\n /**\n * Check if an origin is in the configured whitelist\n */\n private isOriginInWhitelist(origin: string): boolean {\n return (\n this.hostOrigins.includes(origin) ||\n (this.allowNativeAppOrigins && this.isOriginNativeApp(origin))\n );\n }\n\n /**\n * Check if an event origin is valid against the active host origin\n *\n * Security Rule: Incoming messages must exactly match the active host origin.\n * This prevents malicious actors from spoofing origins via query parameters.\n *\n * @param eventOrigin - The origin from the MessageEvent\n * @returns true if the origin is valid\n */\n private isValidOrigin(eventOrigin: string): boolean {\n // STRICT: Exact match with active host origin only\n return eventOrigin === this.activeHostOrigin;\n }\n\n /**\n * Set up the central message listener to handle responses from the LearnCard host\n */\n private setupMessageListener(): void {\n if (typeof window === 'undefined') {\n throw new Error('PartnerConnect SDK can only be used in a browser environment');\n }\n\n this.messageListener = (event: MessageEvent) => {\n // SECURITY CHECK 1: Strict origin validation - must exactly match active host origin\n if (!this.isValidOrigin(event.origin)) {\n // Silently ignore messages from unauthorized origins\n return;\n }\n\n const data = event.data as PostMessageResponse;\n\n // SECURITY CHECK 2: Validate protocol and requestId\n if (data.protocol !== this.protocol || !data.requestId) {\n return;\n }\n\n // Look up the pending request\n const pending = this.pendingRequests.get(data.requestId);\n if (!pending) {\n return; // Ignore stale or unrecognized responses\n }\n\n // Clean up\n clearTimeout(pending.timeoutId);\n this.pendingRequests.delete(data.requestId);\n\n // Resolve or reject the promise\n if (data.type === 'SUCCESS') {\n pending.resolve(data.data);\n } else if (data.type === 'ERROR') {\n pending.reject(\n data.error || { code: 'UNKNOWN_ERROR', message: 'An unknown error occurred' }\n );\n }\n };\n\n window.addEventListener('message', this.messageListener);\n this.isInitialized = true;\n }\n\n /**\n * Generate a unique request ID\n */\n private generateRequestId(action: string): string {\n return `${action}-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;\n }\n\n /**\n * Send a message to the parent window and return a Promise\n */\n private sendMessage<T = unknown>(action: string, payload?: unknown): Promise<T> {\n if (!this.isInitialized) {\n return Promise.reject({\n code: 'SDK_NOT_INITIALIZED',\n message: 'SDK is not initialized',\n } as LearnCardError);\n }\n\n return new Promise<T>((resolve, reject) => {\n const requestId = this.generateRequestId(action);\n\n // Set up timeout\n const timeoutId = setTimeout(() => {\n if (this.pendingRequests.has(requestId)) {\n this.pendingRequests.delete(requestId);\n reject({\n code: 'LC_TIMEOUT',\n message: `Request ${action} timed out after ${this.requestTimeout}ms`,\n } as LearnCardError);\n }\n }, this.requestTimeout);\n\n // Store the pending request\n this.pendingRequests.set(requestId, {\n resolve: resolve as (value: unknown) => void,\n reject,\n timeoutId,\n });\n\n // Construct the message\n const message: PostMessageRequest = {\n protocol: this.protocol,\n action,\n requestId,\n payload,\n };\n\n // Send to parent window with the active host origin (configured or overridden)\n window.parent.postMessage(message, this.activeHostOrigin);\n });\n }\n\n /**\n * Request user identity (Single Sign-On)\n *\n * @returns Promise resolving to user identity including DID and JWT token\n * @throws {LearnCardError} When user is not authenticated or request fails\n *\n * @example\n * ```typescript\n * const identity = await learnCard.requestIdentity();\n * console.log('User DID:', identity.user.did);\n * console.log('JWT Token:', identity.token);\n * ```\n */\n public requestIdentity(): Promise<IdentityResponse> {\n return this.sendMessage<IdentityResponse>('REQUEST_IDENTITY', {\n challenge: `${Date.now()}-${Math.random().toString(36).substring(2, 9)}`,\n });\n }\n\n /**\n * Send a credential to the user's LearnCard wallet\n *\n * Supports two modes:\n * 1. **Raw credential**: Pass a full verifiable credential object\n * 2. **Template-based**: Pass `{ templateAlias, templateData }` to issue from a pre-configured boost template\n *\n * @param input - Either a verifiable credential or a template-based input\n * @returns Promise resolving to credential response\n *\n * @example Raw credential\n * ```typescript\n * const response = await learnCard.sendCredential({\n * '@context': ['https://www.w3.org/2018/credentials/v1'],\n * type: ['VerifiableCredential'],\n * credentialSubject: { id: 'did:example:123' }\n * });\n * console.log('Credential ID:', response.credentialId);\n * ```\n *\n * @example Template-based (for App Store apps)\n * ```typescript\n * const response = await learnCard.sendCredential({\n * templateAlias: 'achievement-badge',\n * templateData: { score: '95', courseName: 'Web Dev 101' }\n * });\n * console.log('Credential URI:', response.credentialUri);\n * ```\n */\n public sendCredential(\n input: unknown | TemplateCredentialInput\n ): Promise<SendCredentialResponse | TemplateCredentialResponse> {\n if (\n input &&\n typeof input === 'object' &&\n 'templateAlias' in input &&\n typeof (input as TemplateCredentialInput).templateAlias === 'string'\n ) {\n const templateInput = input as TemplateCredentialInput;\n\n return this.sendAppEvent({\n type: 'send-credential',\n templateAlias: templateInput.templateAlias,\n templateData: templateInput.templateData,\n }) as Promise<TemplateCredentialResponse>;\n }\n\n return this.sendMessage<SendCredentialResponse>('SEND_CREDENTIAL', { credential: input });\n }\n\n /**\n * Launch a feature in the LearnCard host application\n *\n * @param featurePath - Path to the feature (e.g., '/ai/topics')\n * @param initialPrompt - Optional initial prompt or data\n *\n * @example\n * ```typescript\n * await learnCard.launchFeature(\n * '/ai/topics?shortCircuitStep=newTopic',\n * 'Help me understand cryptography'\n * );\n * ```\n */\n public launchFeature(featurePath: string, initialPrompt?: string): Promise<void> {\n return this.sendMessage<void>('LAUNCH_FEATURE', { featurePath, initialPrompt });\n }\n\n /**\n * Request credentials from the user's wallet using a query\n *\n * @param verifiablePresentationRequest - VPR with query criteria\n * @returns Promise resolving to verifiable presentation\n *\n * @example\n * ```typescript\n * const response = await learnCard.askCredentialSearch({\n * query: [{\n * type: 'QueryByTitle',\n * credentialQuery: {\n * reason: 'We need to verify your skills',\n * title: 'JavaScript Expert'\n * }\n * }],\n * challenge: 'challenge-123',\n * domain: window.location.hostname\n * });\n *\n * if (response.verifiablePresentation) {\n * console.log('Received credentials:', response.verifiablePresentation.verifiableCredential);\n * }\n * ```\n */\n public askCredentialSearch(\n verifiablePresentationRequest: VerifiablePresentationRequest\n ): Promise<CredentialSearchResponse> {\n return this.sendMessage<CredentialSearchResponse>('ASK_CREDENTIAL_SEARCH', {\n verifiablePresentationRequest,\n });\n }\n\n /**\n * Request a specific credential by ID\n *\n * @param credentialId - The ID of the credential to request\n * @returns Promise resolving to the credential\n *\n * @example\n * ```typescript\n * const response = await learnCard.askCredentialSpecific('credential-id-123');\n * if (response.credential) {\n * console.log('Received credential:', response.credential);\n * }\n * ```\n */\n public askCredentialSpecific(credentialId: string): Promise<CredentialSpecificResponse> {\n return this.sendMessage<CredentialSpecificResponse>('ASK_CREDENTIAL_SPECIFIC', {\n credentialId,\n });\n }\n\n /**\n * Request user consent for permissions\n *\n * @param contractUri - URI of the consent contract\n * @returns Promise resolving to consent response\n *\n * @example\n * ```typescript\n * // Without redirect (default) - returns VP in response if app owns the contract\n * const response = await learnCard.requestConsent('lc:network:network.learncard.com/trpc:contract:abc123');\n * if (response.granted) {\n * console.log('User granted consent');\n * if (response.vp) {\n * console.log('VP:', response.vp);\n * }\n * }\n *\n * // With redirect - redirects to contract's redirectUrl with VP in URL params\n * const response = await learnCard.requestConsent('lc:network:network.learncard.com/trpc:contract:abc123', { redirect: true });\n * ```\n */\n public requestConsent(\n contractUri: string,\n options: RequestConsentOptions = {}\n ): Promise<ConsentResponse> {\n const { redirect = false } = options;\n\n return this.sendMessage<ConsentResponse>('REQUEST_CONSENT', {\n contractUri,\n redirect,\n });\n }\n\n /**\n * Initiate a template-based credential issuance flow\n *\n * @param templateId - ID of the template/boost to issue\n * @param draftRecipients - Optional array of recipient DIDs\n * @returns Promise resolving to template issue response\n *\n * @example\n * ```typescript\n * const response = await learnCard.initiateTemplateIssue(\n * 'lc:network:network.learncard.com/trpc:boost:xyz789',\n * ['did:key:abc', 'did:key:def']\n * );\n *\n * if (response.issued) {\n * console.log('Template issued successfully');\n * }\n * ```\n */\n public initiateTemplateIssue(\n templateId: string,\n draftRecipients?: string[]\n ): Promise<TemplateIssueResponse> {\n return this.sendMessage<TemplateIssueResponse>('INITIATE_TEMPLATE_ISSUE', {\n templateId,\n draftRecipients: draftRecipients || [],\n });\n }\n\n /**\n * Send a generic event to be processed by the brain service on behalf of this app.\n * This is used for backend-like operations such as issuing credentials.\n *\n * @param event - The event payload to send\n * @returns Promise resolving to the event response\n *\n * @example\n * ```typescript\n * // Issue a credential to the current user\n * const response = await learnCard.sendAppEvent({\n * type: 'send-credential',\n * templateAlias: 'achievement-badge',\n * templateData: { score: '95' }\n * });\n *\n * if (response.credentialUri) {\n * console.log('Credential issued:', response.credentialUri);\n * }\n * ```\n */\n public sendAppEvent(event: AppEvent): Promise<AppEventResponse> {\n return this.sendMessage<AppEventResponse>('APP_EVENT', event);\n }\n\n /**\n * Clean up the SDK and remove event listeners\n */\n public destroy(): void {\n if (this.messageListener) {\n window.removeEventListener('message', this.messageListener);\n this.messageListener = null;\n }\n\n // Reject all pending requests\n for (const [requestId, pending] of this.pendingRequests.entries()) {\n clearTimeout(pending.timeoutId);\n pending.reject({\n code: 'SDK_DESTROYED',\n message: 'SDK was destroyed before request completed',\n });\n }\n\n this.pendingRequests.clear();\n this.isInitialized = false;\n }\n}\n\n/**\n * Factory function to create a PartnerConnect instance\n *\n * @param options - Configuration options\n * @returns PartnerConnect instance\n *\n * @example\n * ```typescript\n * const learnCard = createPartnerConnect({\n * hostOrigin: 'https://learncard.app',\n * protocol: 'LEARNCARD_V1',\n * requestTimeout: 30000\n * });\n * ```\n */\nexport function createPartnerConnect(options: PartnerConnectOptions): PartnerConnect {\n return new PartnerConnect(options);\n}\n\n// Default export for convenience\nexport default createPartnerConnect;\n"],"names":[],"mappings":"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"}
|
package/dist/partner-connect.js
CHANGED
|
@@ -30,7 +30,7 @@ const _PartnerConnect = class _PartnerConnect {
|
|
|
30
30
|
* 1. Check for `lc_host_override` query parameter (for staging/testing)
|
|
31
31
|
* 2. Fall back to first configured origin
|
|
32
32
|
* 3. Fall back to DEFAULT_HOST_ORIGIN
|
|
33
|
-
*
|
|
33
|
+
*
|
|
34
34
|
* This origin will be used for all outgoing messages and incoming message validation.
|
|
35
35
|
*/
|
|
36
36
|
configureActiveOrigin() {
|
|
@@ -64,7 +64,7 @@ const _PartnerConnect = class _PartnerConnect {
|
|
|
64
64
|
}
|
|
65
65
|
}
|
|
66
66
|
isOriginNativeApp(origin) {
|
|
67
|
-
return origin.startsWith("capacitor://") || origin.startsWith("ionic://") || origin.startsWith("https://localhost") || origin.startsWith("http://localhost") || origin.startsWith("http://127.0.0.1")
|
|
67
|
+
return origin.startsWith("capacitor://") || origin.startsWith("ionic://") || origin.startsWith("https://localhost") || origin.startsWith("http://localhost") || origin.startsWith("http://127.0.0.1");
|
|
68
68
|
}
|
|
69
69
|
/**
|
|
70
70
|
* Check if an origin is in the configured whitelist
|
|
@@ -74,10 +74,10 @@ const _PartnerConnect = class _PartnerConnect {
|
|
|
74
74
|
}
|
|
75
75
|
/**
|
|
76
76
|
* Check if an event origin is valid against the active host origin
|
|
77
|
-
*
|
|
77
|
+
*
|
|
78
78
|
* Security Rule: Incoming messages must exactly match the active host origin.
|
|
79
79
|
* This prevents malicious actors from spoofing origins via query parameters.
|
|
80
|
-
*
|
|
80
|
+
*
|
|
81
81
|
* @param eventOrigin - The origin from the MessageEvent
|
|
82
82
|
* @returns true if the origin is valid
|
|
83
83
|
*/
|
|
@@ -108,7 +108,9 @@ const _PartnerConnect = class _PartnerConnect {
|
|
|
108
108
|
if (data.type === "SUCCESS") {
|
|
109
109
|
pending.resolve(data.data);
|
|
110
110
|
} else if (data.type === "ERROR") {
|
|
111
|
-
pending.reject(
|
|
111
|
+
pending.reject(
|
|
112
|
+
data.error || { code: "UNKNOWN_ERROR", message: "An unknown error occurred" }
|
|
113
|
+
);
|
|
112
114
|
}
|
|
113
115
|
};
|
|
114
116
|
window.addEventListener("message", this.messageListener);
|
|
@@ -157,10 +159,10 @@ const _PartnerConnect = class _PartnerConnect {
|
|
|
157
159
|
}
|
|
158
160
|
/**
|
|
159
161
|
* Request user identity (Single Sign-On)
|
|
160
|
-
*
|
|
162
|
+
*
|
|
161
163
|
* @returns Promise resolving to user identity including DID and JWT token
|
|
162
164
|
* @throws {LearnCardError} When user is not authenticated or request fails
|
|
163
|
-
*
|
|
165
|
+
*
|
|
164
166
|
* @example
|
|
165
167
|
* ```typescript
|
|
166
168
|
* const identity = await learnCard.requestIdentity();
|
|
@@ -169,15 +171,21 @@ const _PartnerConnect = class _PartnerConnect {
|
|
|
169
171
|
* ```
|
|
170
172
|
*/
|
|
171
173
|
requestIdentity() {
|
|
172
|
-
return this.sendMessage("REQUEST_IDENTITY", {
|
|
174
|
+
return this.sendMessage("REQUEST_IDENTITY", {
|
|
175
|
+
challenge: `${Date.now()}-${Math.random().toString(36).substring(2, 9)}`
|
|
176
|
+
});
|
|
173
177
|
}
|
|
174
178
|
/**
|
|
175
179
|
* Send a credential to the user's LearnCard wallet
|
|
176
|
-
*
|
|
177
|
-
*
|
|
178
|
-
*
|
|
179
|
-
*
|
|
180
|
-
*
|
|
180
|
+
*
|
|
181
|
+
* Supports two modes:
|
|
182
|
+
* 1. **Raw credential**: Pass a full verifiable credential object
|
|
183
|
+
* 2. **Template-based**: Pass `{ templateAlias, templateData }` to issue from a pre-configured boost template
|
|
184
|
+
*
|
|
185
|
+
* @param input - Either a verifiable credential or a template-based input
|
|
186
|
+
* @returns Promise resolving to credential response
|
|
187
|
+
*
|
|
188
|
+
* @example Raw credential
|
|
181
189
|
* ```typescript
|
|
182
190
|
* const response = await learnCard.sendCredential({
|
|
183
191
|
* '@context': ['https://www.w3.org/2018/credentials/v1'],
|
|
@@ -186,16 +194,33 @@ const _PartnerConnect = class _PartnerConnect {
|
|
|
186
194
|
* });
|
|
187
195
|
* console.log('Credential ID:', response.credentialId);
|
|
188
196
|
* ```
|
|
197
|
+
*
|
|
198
|
+
* @example Template-based (for App Store apps)
|
|
199
|
+
* ```typescript
|
|
200
|
+
* const response = await learnCard.sendCredential({
|
|
201
|
+
* templateAlias: 'achievement-badge',
|
|
202
|
+
* templateData: { score: '95', courseName: 'Web Dev 101' }
|
|
203
|
+
* });
|
|
204
|
+
* console.log('Credential URI:', response.credentialUri);
|
|
205
|
+
* ```
|
|
189
206
|
*/
|
|
190
|
-
sendCredential(
|
|
191
|
-
|
|
207
|
+
sendCredential(input) {
|
|
208
|
+
if (input && typeof input === "object" && "templateAlias" in input && typeof input.templateAlias === "string") {
|
|
209
|
+
const templateInput = input;
|
|
210
|
+
return this.sendAppEvent({
|
|
211
|
+
type: "send-credential",
|
|
212
|
+
templateAlias: templateInput.templateAlias,
|
|
213
|
+
templateData: templateInput.templateData
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
return this.sendMessage("SEND_CREDENTIAL", { credential: input });
|
|
192
217
|
}
|
|
193
218
|
/**
|
|
194
219
|
* Launch a feature in the LearnCard host application
|
|
195
|
-
*
|
|
220
|
+
*
|
|
196
221
|
* @param featurePath - Path to the feature (e.g., '/ai/topics')
|
|
197
222
|
* @param initialPrompt - Optional initial prompt or data
|
|
198
|
-
*
|
|
223
|
+
*
|
|
199
224
|
* @example
|
|
200
225
|
* ```typescript
|
|
201
226
|
* await learnCard.launchFeature(
|
|
@@ -209,10 +234,10 @@ const _PartnerConnect = class _PartnerConnect {
|
|
|
209
234
|
}
|
|
210
235
|
/**
|
|
211
236
|
* Request credentials from the user's wallet using a query
|
|
212
|
-
*
|
|
237
|
+
*
|
|
213
238
|
* @param verifiablePresentationRequest - VPR with query criteria
|
|
214
239
|
* @returns Promise resolving to verifiable presentation
|
|
215
|
-
*
|
|
240
|
+
*
|
|
216
241
|
* @example
|
|
217
242
|
* ```typescript
|
|
218
243
|
* const response = await learnCard.askCredentialSearch({
|
|
@@ -226,7 +251,7 @@ const _PartnerConnect = class _PartnerConnect {
|
|
|
226
251
|
* challenge: 'challenge-123',
|
|
227
252
|
* domain: window.location.hostname
|
|
228
253
|
* });
|
|
229
|
-
*
|
|
254
|
+
*
|
|
230
255
|
* if (response.verifiablePresentation) {
|
|
231
256
|
* console.log('Received credentials:', response.verifiablePresentation.verifiableCredential);
|
|
232
257
|
* }
|
|
@@ -239,10 +264,10 @@ const _PartnerConnect = class _PartnerConnect {
|
|
|
239
264
|
}
|
|
240
265
|
/**
|
|
241
266
|
* Request a specific credential by ID
|
|
242
|
-
*
|
|
267
|
+
*
|
|
243
268
|
* @param credentialId - The ID of the credential to request
|
|
244
269
|
* @returns Promise resolving to the credential
|
|
245
|
-
*
|
|
270
|
+
*
|
|
246
271
|
* @example
|
|
247
272
|
* ```typescript
|
|
248
273
|
* const response = await learnCard.askCredentialSpecific('credential-id-123');
|
|
@@ -258,35 +283,46 @@ const _PartnerConnect = class _PartnerConnect {
|
|
|
258
283
|
}
|
|
259
284
|
/**
|
|
260
285
|
* Request user consent for permissions
|
|
261
|
-
*
|
|
286
|
+
*
|
|
262
287
|
* @param contractUri - URI of the consent contract
|
|
263
288
|
* @returns Promise resolving to consent response
|
|
264
|
-
*
|
|
289
|
+
*
|
|
265
290
|
* @example
|
|
266
291
|
* ```typescript
|
|
292
|
+
* // Without redirect (default) - returns VP in response if app owns the contract
|
|
267
293
|
* const response = await learnCard.requestConsent('lc:network:network.learncard.com/trpc:contract:abc123');
|
|
268
294
|
* if (response.granted) {
|
|
269
295
|
* console.log('User granted consent');
|
|
296
|
+
* if (response.vp) {
|
|
297
|
+
* console.log('VP:', response.vp);
|
|
298
|
+
* }
|
|
270
299
|
* }
|
|
300
|
+
*
|
|
301
|
+
* // With redirect - redirects to contract's redirectUrl with VP in URL params
|
|
302
|
+
* const response = await learnCard.requestConsent('lc:network:network.learncard.com/trpc:contract:abc123', { redirect: true });
|
|
271
303
|
* ```
|
|
272
304
|
*/
|
|
273
|
-
requestConsent(contractUri) {
|
|
274
|
-
|
|
305
|
+
requestConsent(contractUri, options = {}) {
|
|
306
|
+
const { redirect = false } = options;
|
|
307
|
+
return this.sendMessage("REQUEST_CONSENT", {
|
|
308
|
+
contractUri,
|
|
309
|
+
redirect
|
|
310
|
+
});
|
|
275
311
|
}
|
|
276
312
|
/**
|
|
277
313
|
* Initiate a template-based credential issuance flow
|
|
278
|
-
*
|
|
314
|
+
*
|
|
279
315
|
* @param templateId - ID of the template/boost to issue
|
|
280
316
|
* @param draftRecipients - Optional array of recipient DIDs
|
|
281
317
|
* @returns Promise resolving to template issue response
|
|
282
|
-
*
|
|
318
|
+
*
|
|
283
319
|
* @example
|
|
284
320
|
* ```typescript
|
|
285
321
|
* const response = await learnCard.initiateTemplateIssue(
|
|
286
322
|
* 'lc:network:network.learncard.com/trpc:boost:xyz789',
|
|
287
323
|
* ['did:key:abc', 'did:key:def']
|
|
288
324
|
* );
|
|
289
|
-
*
|
|
325
|
+
*
|
|
290
326
|
* if (response.issued) {
|
|
291
327
|
* console.log('Template issued successfully');
|
|
292
328
|
* }
|
|
@@ -298,6 +334,30 @@ const _PartnerConnect = class _PartnerConnect {
|
|
|
298
334
|
draftRecipients: draftRecipients || []
|
|
299
335
|
});
|
|
300
336
|
}
|
|
337
|
+
/**
|
|
338
|
+
* Send a generic event to be processed by the brain service on behalf of this app.
|
|
339
|
+
* This is used for backend-like operations such as issuing credentials.
|
|
340
|
+
*
|
|
341
|
+
* @param event - The event payload to send
|
|
342
|
+
* @returns Promise resolving to the event response
|
|
343
|
+
*
|
|
344
|
+
* @example
|
|
345
|
+
* ```typescript
|
|
346
|
+
* // Issue a credential to the current user
|
|
347
|
+
* const response = await learnCard.sendAppEvent({
|
|
348
|
+
* type: 'send-credential',
|
|
349
|
+
* templateAlias: 'achievement-badge',
|
|
350
|
+
* templateData: { score: '95' }
|
|
351
|
+
* });
|
|
352
|
+
*
|
|
353
|
+
* if (response.credentialUri) {
|
|
354
|
+
* console.log('Credential issued:', response.credentialUri);
|
|
355
|
+
* }
|
|
356
|
+
* ```
|
|
357
|
+
*/
|
|
358
|
+
sendAppEvent(event) {
|
|
359
|
+
return this.sendMessage("APP_EVENT", event);
|
|
360
|
+
}
|
|
301
361
|
/**
|
|
302
362
|
* Clean up the SDK and remove event listeners
|
|
303
363
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"partner-connect.js","sources":["../src/index.ts"],"sourcesContent":["/**\n * LearnCard Partner Connect SDK\n * \n * A Promise-based JavaScript utility for managing cross-origin message communication\n * between partner apps and the LearnCard host application.\n * \n * @example\n * ```typescript\n * import { createPartnerConnect } from '@learncard/partner-connect';\n * \n * const learnCard = createPartnerConnect({\n * hostOrigin: 'https://learncard.app'\n * });\n * \n * // Request user identity (SSO)\n * const identity = await learnCard.requestIdentity();\n * console.log('User DID:', identity.user.did);\n * ```\n */\n\nimport type {\n PartnerConnectOptions,\n IdentityResponse,\n SendCredentialResponse,\n VerifiablePresentationRequest,\n CredentialSearchResponse,\n CredentialSpecificResponse,\n ConsentResponse,\n TemplateIssueResponse,\n LearnCardError,\n PostMessageRequest,\n PostMessageResponse,\n PendingRequest,\n} from './types';\n\nexport type * from './types';\n\n/**\n * LearnCard Partner Connect SDK class\n */\nexport class PartnerConnect {\n /** Default host origin (security anchor) */\n public static readonly DEFAULT_HOST_ORIGIN = 'https://learncard.app';\n \n private hostOrigins: string[] = ['https://learncard.app'];\n private activeHostOrigin: string = 'https://learncard.app';\n private allowNativeAppOrigins: boolean = true;\n private protocol: string = 'LEARNCARD_V1';\n private requestTimeout: number = 30000;\n private pendingRequests: Map<string, PendingRequest>;\n private messageListener: ((event: MessageEvent) => void) | null = null;\n private isInitialized = false;\n\n constructor(options?: PartnerConnectOptions) {\n // Normalize hostOrigin to an array for whitelist validation\n const hostOrigin = options?.hostOrigin || PartnerConnect.DEFAULT_HOST_ORIGIN;\n this.hostOrigins = Array.isArray(hostOrigin) ? hostOrigin : [hostOrigin];\n \n this.protocol = options?.protocol || 'LEARNCARD_V1';\n this.requestTimeout = options?.requestTimeout || 30000;\n this.allowNativeAppOrigins = options?.allowNativeAppOrigins ?? true;\n this.pendingRequests = new Map();\n this.configureActiveOrigin();\n this.setupMessageListener();\n }\n\n /**\n * Configure the active host origin using the following hierarchy:\n * 1. Check for `lc_host_override` query parameter (for staging/testing)\n * 2. Fall back to first configured origin\n * 3. Fall back to DEFAULT_HOST_ORIGIN\n * \n * This origin will be used for all outgoing messages and incoming message validation.\n */\n private configureActiveOrigin(): void {\n if (typeof window === 'undefined') {\n this.activeHostOrigin = this.hostOrigins[0] || PartnerConnect.DEFAULT_HOST_ORIGIN;\n return;\n }\n\n try {\n // Check for lc_host_override query parameter\n const urlParams = new URLSearchParams(window.location.search);\n const hostOverride = urlParams.get('lc_host_override');\n\n if (hostOverride) {\n // Validate override against whitelist (if provided)\n if (this.hostOrigins.length > 0 && !this.isOriginInWhitelist(hostOverride)) {\n console.warn(\n '[LearnCard SDK] lc_host_override value is not in the configured whitelist:',\n hostOverride,\n 'Allowed:',\n this.hostOrigins\n );\n this.activeHostOrigin = this.hostOrigins[0] || PartnerConnect.DEFAULT_HOST_ORIGIN;\n } else {\n this.activeHostOrigin = hostOverride;\n console.log('[LearnCard SDK] Using lc_host_override:', hostOverride);\n }\n } else {\n // Use first configured origin or default\n this.activeHostOrigin = this.hostOrigins[0] || PartnerConnect.DEFAULT_HOST_ORIGIN;\n console.log('[LearnCard SDK] Using configured origin:', this.activeHostOrigin);\n }\n } catch (error) {\n console.error('[LearnCard SDK] Error configuring active origin:', error);\n this.activeHostOrigin = this.hostOrigins[0] || PartnerConnect.DEFAULT_HOST_ORIGIN;\n }\n }\n\n private isOriginNativeApp(origin: string): boolean {\n return origin.startsWith('capacitor://') || origin.startsWith('ionic://') || origin.startsWith('https://localhost') || origin.startsWith('http://localhost') || origin.startsWith('http://127.0.0.1') || origin.startsWith('http://192.168.1.164')\n }\n\n /**\n * Check if an origin is in the configured whitelist\n */\n private isOriginInWhitelist(origin: string): boolean {\n return this.hostOrigins.includes(origin) || (this.allowNativeAppOrigins && this.isOriginNativeApp(origin));\n }\n\n /**\n * Check if an event origin is valid against the active host origin\n * \n * Security Rule: Incoming messages must exactly match the active host origin.\n * This prevents malicious actors from spoofing origins via query parameters.\n * \n * @param eventOrigin - The origin from the MessageEvent\n * @returns true if the origin is valid\n */\n private isValidOrigin(eventOrigin: string): boolean {\n // STRICT: Exact match with active host origin only\n return eventOrigin === this.activeHostOrigin;\n }\n\n /**\n * Set up the central message listener to handle responses from the LearnCard host\n */\n private setupMessageListener(): void {\n if (typeof window === 'undefined') {\n throw new Error('PartnerConnect SDK can only be used in a browser environment');\n }\n\n this.messageListener = (event: MessageEvent) => {\n // SECURITY CHECK 1: Strict origin validation - must exactly match active host origin\n if (!this.isValidOrigin(event.origin)) {\n // Silently ignore messages from unauthorized origins\n return;\n }\n\n const data = event.data as PostMessageResponse;\n\n // SECURITY CHECK 2: Validate protocol and requestId\n if (data.protocol !== this.protocol || !data.requestId) {\n return;\n }\n\n // Look up the pending request\n const pending = this.pendingRequests.get(data.requestId);\n if (!pending) {\n return; // Ignore stale or unrecognized responses\n }\n\n // Clean up\n clearTimeout(pending.timeoutId);\n this.pendingRequests.delete(data.requestId);\n\n // Resolve or reject the promise\n if (data.type === 'SUCCESS') {\n pending.resolve(data.data);\n } else if (data.type === 'ERROR') {\n pending.reject(data.error || { code: 'UNKNOWN_ERROR', message: 'An unknown error occurred' });\n }\n };\n\n window.addEventListener('message', this.messageListener);\n this.isInitialized = true;\n }\n\n /**\n * Generate a unique request ID\n */\n private generateRequestId(action: string): string {\n return `${action}-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;\n }\n\n /**\n * Send a message to the parent window and return a Promise\n */\n private sendMessage<T = unknown>(action: string, payload?: unknown): Promise<T> {\n if (!this.isInitialized) {\n return Promise.reject({\n code: 'SDK_NOT_INITIALIZED',\n message: 'SDK is not initialized',\n } as LearnCardError);\n }\n\n return new Promise<T>((resolve, reject) => {\n const requestId = this.generateRequestId(action);\n\n // Set up timeout\n const timeoutId = setTimeout(() => {\n if (this.pendingRequests.has(requestId)) {\n this.pendingRequests.delete(requestId);\n reject({\n code: 'LC_TIMEOUT',\n message: `Request ${action} timed out after ${this.requestTimeout}ms`,\n } as LearnCardError);\n }\n }, this.requestTimeout);\n\n // Store the pending request\n this.pendingRequests.set(requestId, {\n resolve: resolve as (value: unknown) => void,\n reject,\n timeoutId,\n });\n\n // Construct the message\n const message: PostMessageRequest = {\n protocol: this.protocol,\n action,\n requestId,\n payload,\n };\n\n // Send to parent window with the active host origin (configured or overridden)\n window.parent.postMessage(message, this.activeHostOrigin);\n });\n }\n\n /**\n * Request user identity (Single Sign-On)\n * \n * @returns Promise resolving to user identity including DID and JWT token\n * @throws {LearnCardError} When user is not authenticated or request fails\n * \n * @example\n * ```typescript\n * const identity = await learnCard.requestIdentity();\n * console.log('User DID:', identity.user.did);\n * console.log('JWT Token:', identity.token);\n * ```\n */\n public requestIdentity(): Promise<IdentityResponse> {\n return this.sendMessage<IdentityResponse>('REQUEST_IDENTITY', { challenge: `${Date.now()}-${Math.random().toString(36).substring(2, 9)}` });\n }\n\n /**\n * Send a credential to the user's LearnCard wallet\n * \n * @param credential - The verifiable credential to send\n * @returns Promise resolving to credential ID\n * \n * @example\n * ```typescript\n * const response = await learnCard.sendCredential({\n * '@context': ['https://www.w3.org/2018/credentials/v1'],\n * type: ['VerifiableCredential'],\n * credentialSubject: { id: 'did:example:123' }\n * });\n * console.log('Credential ID:', response.credentialId);\n * ```\n */\n public sendCredential(credential: unknown): Promise<SendCredentialResponse> {\n return this.sendMessage<SendCredentialResponse>('SEND_CREDENTIAL', { credential });\n }\n\n /**\n * Launch a feature in the LearnCard host application\n * \n * @param featurePath - Path to the feature (e.g., '/ai/topics')\n * @param initialPrompt - Optional initial prompt or data\n * \n * @example\n * ```typescript\n * await learnCard.launchFeature(\n * '/ai/topics?shortCircuitStep=newTopic',\n * 'Help me understand cryptography'\n * );\n * ```\n */\n public launchFeature(featurePath: string, initialPrompt?: string): Promise<void> {\n return this.sendMessage<void>('LAUNCH_FEATURE', { featurePath, initialPrompt });\n }\n\n /**\n * Request credentials from the user's wallet using a query\n * \n * @param verifiablePresentationRequest - VPR with query criteria\n * @returns Promise resolving to verifiable presentation\n * \n * @example\n * ```typescript\n * const response = await learnCard.askCredentialSearch({\n * query: [{\n * type: 'QueryByTitle',\n * credentialQuery: {\n * reason: 'We need to verify your skills',\n * title: 'JavaScript Expert'\n * }\n * }],\n * challenge: 'challenge-123',\n * domain: window.location.hostname\n * });\n * \n * if (response.verifiablePresentation) {\n * console.log('Received credentials:', response.verifiablePresentation.verifiableCredential);\n * }\n * ```\n */\n public askCredentialSearch(\n verifiablePresentationRequest: VerifiablePresentationRequest\n ): Promise<CredentialSearchResponse> {\n return this.sendMessage<CredentialSearchResponse>('ASK_CREDENTIAL_SEARCH', {\n verifiablePresentationRequest,\n });\n }\n\n /**\n * Request a specific credential by ID\n * \n * @param credentialId - The ID of the credential to request\n * @returns Promise resolving to the credential\n * \n * @example\n * ```typescript\n * const response = await learnCard.askCredentialSpecific('credential-id-123');\n * if (response.credential) {\n * console.log('Received credential:', response.credential);\n * }\n * ```\n */\n public askCredentialSpecific(credentialId: string): Promise<CredentialSpecificResponse> {\n return this.sendMessage<CredentialSpecificResponse>('ASK_CREDENTIAL_SPECIFIC', {\n credentialId,\n });\n }\n\n /**\n * Request user consent for permissions\n * \n * @param contractUri - URI of the consent contract\n * @returns Promise resolving to consent response\n * \n * @example\n * ```typescript\n * const response = await learnCard.requestConsent('lc:network:network.learncard.com/trpc:contract:abc123');\n * if (response.granted) {\n * console.log('User granted consent');\n * }\n * ```\n */\n public requestConsent(contractUri: string): Promise<ConsentResponse> {\n return this.sendMessage<ConsentResponse>('REQUEST_CONSENT', { contractUri });\n }\n\n /**\n * Initiate a template-based credential issuance flow\n * \n * @param templateId - ID of the template/boost to issue\n * @param draftRecipients - Optional array of recipient DIDs\n * @returns Promise resolving to template issue response\n * \n * @example\n * ```typescript\n * const response = await learnCard.initiateTemplateIssue(\n * 'lc:network:network.learncard.com/trpc:boost:xyz789',\n * ['did:key:abc', 'did:key:def']\n * );\n * \n * if (response.issued) {\n * console.log('Template issued successfully');\n * }\n * ```\n */\n public initiateTemplateIssue(\n templateId: string,\n draftRecipients?: string[]\n ): Promise<TemplateIssueResponse> {\n return this.sendMessage<TemplateIssueResponse>('INITIATE_TEMPLATE_ISSUE', {\n templateId,\n draftRecipients: draftRecipients || [],\n });\n }\n\n /**\n * Clean up the SDK and remove event listeners\n */\n public destroy(): void {\n if (this.messageListener) {\n window.removeEventListener('message', this.messageListener);\n this.messageListener = null;\n }\n\n // Reject all pending requests\n for (const [requestId, pending] of this.pendingRequests.entries()) {\n clearTimeout(pending.timeoutId);\n pending.reject({\n code: 'SDK_DESTROYED',\n message: 'SDK was destroyed before request completed',\n });\n }\n\n this.pendingRequests.clear();\n this.isInitialized = false;\n }\n}\n\n/**\n * Factory function to create a PartnerConnect instance\n * \n * @param options - Configuration options\n * @returns PartnerConnect instance\n * \n * @example\n * ```typescript\n * const learnCard = createPartnerConnect({\n * hostOrigin: 'https://learncard.app',\n * protocol: 'LEARNCARD_V1',\n * requestTimeout: 30000\n * });\n * ```\n */\nexport function createPartnerConnect(options: PartnerConnectOptions): PartnerConnect {\n return new PartnerConnect(options);\n}\n\n// Default export for convenience\nexport default createPartnerConnect;\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"partner-connect.js","sources":["../src/index.ts"],"sourcesContent":["/**\n * LearnCard Partner Connect SDK\n *\n * A Promise-based JavaScript utility for managing cross-origin message communication\n * between partner apps and the LearnCard host application.\n *\n * @example\n * ```typescript\n * import { createPartnerConnect } from '@learncard/partner-connect';\n *\n * const learnCard = createPartnerConnect({\n * hostOrigin: 'https://learncard.app'\n * });\n *\n * // Request user identity (SSO)\n * const identity = await learnCard.requestIdentity();\n * console.log('User DID:', identity.user.did);\n * ```\n */\n\nimport type {\n PartnerConnectOptions,\n IdentityResponse,\n SendCredentialResponse,\n TemplateCredentialInput,\n TemplateCredentialResponse,\n VerifiablePresentationRequest,\n CredentialSearchResponse,\n CredentialSpecificResponse,\n ConsentResponse,\n RequestConsentOptions,\n TemplateIssueResponse,\n AppEvent,\n AppEventResponse,\n LearnCardError,\n PostMessageRequest,\n PostMessageResponse,\n PendingRequest,\n} from './types';\n\nexport type * from './types';\n\n/**\n * LearnCard Partner Connect SDK class\n */\nexport class PartnerConnect {\n /** Default host origin (security anchor) */\n public static readonly DEFAULT_HOST_ORIGIN = 'https://learncard.app';\n\n private hostOrigins: string[] = ['https://learncard.app'];\n private activeHostOrigin: string = 'https://learncard.app';\n private allowNativeAppOrigins: boolean = true;\n private protocol: string = 'LEARNCARD_V1';\n private requestTimeout: number = 30000;\n private pendingRequests: Map<string, PendingRequest>;\n private messageListener: ((event: MessageEvent) => void) | null = null;\n private isInitialized = false;\n\n constructor(options?: PartnerConnectOptions) {\n // Normalize hostOrigin to an array for whitelist validation\n const hostOrigin = options?.hostOrigin || PartnerConnect.DEFAULT_HOST_ORIGIN;\n this.hostOrigins = Array.isArray(hostOrigin) ? hostOrigin : [hostOrigin];\n\n this.protocol = options?.protocol || 'LEARNCARD_V1';\n this.requestTimeout = options?.requestTimeout || 30000;\n this.allowNativeAppOrigins = options?.allowNativeAppOrigins ?? true;\n this.pendingRequests = new Map();\n this.configureActiveOrigin();\n this.setupMessageListener();\n }\n\n /**\n * Configure the active host origin using the following hierarchy:\n * 1. Check for `lc_host_override` query parameter (for staging/testing)\n * 2. Fall back to first configured origin\n * 3. Fall back to DEFAULT_HOST_ORIGIN\n *\n * This origin will be used for all outgoing messages and incoming message validation.\n */\n private configureActiveOrigin(): void {\n if (typeof window === 'undefined') {\n this.activeHostOrigin = this.hostOrigins[0] || PartnerConnect.DEFAULT_HOST_ORIGIN;\n return;\n }\n\n try {\n // Check for lc_host_override query parameter\n const urlParams = new URLSearchParams(window.location.search);\n const hostOverride = urlParams.get('lc_host_override');\n\n if (hostOverride) {\n // Validate override against whitelist (if provided)\n if (this.hostOrigins.length > 0 && !this.isOriginInWhitelist(hostOverride)) {\n console.warn(\n '[LearnCard SDK] lc_host_override value is not in the configured whitelist:',\n hostOverride,\n 'Allowed:',\n this.hostOrigins\n );\n this.activeHostOrigin =\n this.hostOrigins[0] || PartnerConnect.DEFAULT_HOST_ORIGIN;\n } else {\n this.activeHostOrigin = hostOverride;\n console.log('[LearnCard SDK] Using lc_host_override:', hostOverride);\n }\n } else {\n // Use first configured origin or default\n this.activeHostOrigin = this.hostOrigins[0] || PartnerConnect.DEFAULT_HOST_ORIGIN;\n console.log('[LearnCard SDK] Using configured origin:', this.activeHostOrigin);\n }\n } catch (error) {\n console.error('[LearnCard SDK] Error configuring active origin:', error);\n this.activeHostOrigin = this.hostOrigins[0] || PartnerConnect.DEFAULT_HOST_ORIGIN;\n }\n }\n\n private isOriginNativeApp(origin: string): boolean {\n return (\n origin.startsWith('capacitor://') ||\n origin.startsWith('ionic://') ||\n origin.startsWith('https://localhost') ||\n origin.startsWith('http://localhost') ||\n origin.startsWith('http://127.0.0.1')\n );\n }\n\n /**\n * Check if an origin is in the configured whitelist\n */\n private isOriginInWhitelist(origin: string): boolean {\n return (\n this.hostOrigins.includes(origin) ||\n (this.allowNativeAppOrigins && this.isOriginNativeApp(origin))\n );\n }\n\n /**\n * Check if an event origin is valid against the active host origin\n *\n * Security Rule: Incoming messages must exactly match the active host origin.\n * This prevents malicious actors from spoofing origins via query parameters.\n *\n * @param eventOrigin - The origin from the MessageEvent\n * @returns true if the origin is valid\n */\n private isValidOrigin(eventOrigin: string): boolean {\n // STRICT: Exact match with active host origin only\n return eventOrigin === this.activeHostOrigin;\n }\n\n /**\n * Set up the central message listener to handle responses from the LearnCard host\n */\n private setupMessageListener(): void {\n if (typeof window === 'undefined') {\n throw new Error('PartnerConnect SDK can only be used in a browser environment');\n }\n\n this.messageListener = (event: MessageEvent) => {\n // SECURITY CHECK 1: Strict origin validation - must exactly match active host origin\n if (!this.isValidOrigin(event.origin)) {\n // Silently ignore messages from unauthorized origins\n return;\n }\n\n const data = event.data as PostMessageResponse;\n\n // SECURITY CHECK 2: Validate protocol and requestId\n if (data.protocol !== this.protocol || !data.requestId) {\n return;\n }\n\n // Look up the pending request\n const pending = this.pendingRequests.get(data.requestId);\n if (!pending) {\n return; // Ignore stale or unrecognized responses\n }\n\n // Clean up\n clearTimeout(pending.timeoutId);\n this.pendingRequests.delete(data.requestId);\n\n // Resolve or reject the promise\n if (data.type === 'SUCCESS') {\n pending.resolve(data.data);\n } else if (data.type === 'ERROR') {\n pending.reject(\n data.error || { code: 'UNKNOWN_ERROR', message: 'An unknown error occurred' }\n );\n }\n };\n\n window.addEventListener('message', this.messageListener);\n this.isInitialized = true;\n }\n\n /**\n * Generate a unique request ID\n */\n private generateRequestId(action: string): string {\n return `${action}-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;\n }\n\n /**\n * Send a message to the parent window and return a Promise\n */\n private sendMessage<T = unknown>(action: string, payload?: unknown): Promise<T> {\n if (!this.isInitialized) {\n return Promise.reject({\n code: 'SDK_NOT_INITIALIZED',\n message: 'SDK is not initialized',\n } as LearnCardError);\n }\n\n return new Promise<T>((resolve, reject) => {\n const requestId = this.generateRequestId(action);\n\n // Set up timeout\n const timeoutId = setTimeout(() => {\n if (this.pendingRequests.has(requestId)) {\n this.pendingRequests.delete(requestId);\n reject({\n code: 'LC_TIMEOUT',\n message: `Request ${action} timed out after ${this.requestTimeout}ms`,\n } as LearnCardError);\n }\n }, this.requestTimeout);\n\n // Store the pending request\n this.pendingRequests.set(requestId, {\n resolve: resolve as (value: unknown) => void,\n reject,\n timeoutId,\n });\n\n // Construct the message\n const message: PostMessageRequest = {\n protocol: this.protocol,\n action,\n requestId,\n payload,\n };\n\n // Send to parent window with the active host origin (configured or overridden)\n window.parent.postMessage(message, this.activeHostOrigin);\n });\n }\n\n /**\n * Request user identity (Single Sign-On)\n *\n * @returns Promise resolving to user identity including DID and JWT token\n * @throws {LearnCardError} When user is not authenticated or request fails\n *\n * @example\n * ```typescript\n * const identity = await learnCard.requestIdentity();\n * console.log('User DID:', identity.user.did);\n * console.log('JWT Token:', identity.token);\n * ```\n */\n public requestIdentity(): Promise<IdentityResponse> {\n return this.sendMessage<IdentityResponse>('REQUEST_IDENTITY', {\n challenge: `${Date.now()}-${Math.random().toString(36).substring(2, 9)}`,\n });\n }\n\n /**\n * Send a credential to the user's LearnCard wallet\n *\n * Supports two modes:\n * 1. **Raw credential**: Pass a full verifiable credential object\n * 2. **Template-based**: Pass `{ templateAlias, templateData }` to issue from a pre-configured boost template\n *\n * @param input - Either a verifiable credential or a template-based input\n * @returns Promise resolving to credential response\n *\n * @example Raw credential\n * ```typescript\n * const response = await learnCard.sendCredential({\n * '@context': ['https://www.w3.org/2018/credentials/v1'],\n * type: ['VerifiableCredential'],\n * credentialSubject: { id: 'did:example:123' }\n * });\n * console.log('Credential ID:', response.credentialId);\n * ```\n *\n * @example Template-based (for App Store apps)\n * ```typescript\n * const response = await learnCard.sendCredential({\n * templateAlias: 'achievement-badge',\n * templateData: { score: '95', courseName: 'Web Dev 101' }\n * });\n * console.log('Credential URI:', response.credentialUri);\n * ```\n */\n public sendCredential(\n input: unknown | TemplateCredentialInput\n ): Promise<SendCredentialResponse | TemplateCredentialResponse> {\n if (\n input &&\n typeof input === 'object' &&\n 'templateAlias' in input &&\n typeof (input as TemplateCredentialInput).templateAlias === 'string'\n ) {\n const templateInput = input as TemplateCredentialInput;\n\n return this.sendAppEvent({\n type: 'send-credential',\n templateAlias: templateInput.templateAlias,\n templateData: templateInput.templateData,\n }) as Promise<TemplateCredentialResponse>;\n }\n\n return this.sendMessage<SendCredentialResponse>('SEND_CREDENTIAL', { credential: input });\n }\n\n /**\n * Launch a feature in the LearnCard host application\n *\n * @param featurePath - Path to the feature (e.g., '/ai/topics')\n * @param initialPrompt - Optional initial prompt or data\n *\n * @example\n * ```typescript\n * await learnCard.launchFeature(\n * '/ai/topics?shortCircuitStep=newTopic',\n * 'Help me understand cryptography'\n * );\n * ```\n */\n public launchFeature(featurePath: string, initialPrompt?: string): Promise<void> {\n return this.sendMessage<void>('LAUNCH_FEATURE', { featurePath, initialPrompt });\n }\n\n /**\n * Request credentials from the user's wallet using a query\n *\n * @param verifiablePresentationRequest - VPR with query criteria\n * @returns Promise resolving to verifiable presentation\n *\n * @example\n * ```typescript\n * const response = await learnCard.askCredentialSearch({\n * query: [{\n * type: 'QueryByTitle',\n * credentialQuery: {\n * reason: 'We need to verify your skills',\n * title: 'JavaScript Expert'\n * }\n * }],\n * challenge: 'challenge-123',\n * domain: window.location.hostname\n * });\n *\n * if (response.verifiablePresentation) {\n * console.log('Received credentials:', response.verifiablePresentation.verifiableCredential);\n * }\n * ```\n */\n public askCredentialSearch(\n verifiablePresentationRequest: VerifiablePresentationRequest\n ): Promise<CredentialSearchResponse> {\n return this.sendMessage<CredentialSearchResponse>('ASK_CREDENTIAL_SEARCH', {\n verifiablePresentationRequest,\n });\n }\n\n /**\n * Request a specific credential by ID\n *\n * @param credentialId - The ID of the credential to request\n * @returns Promise resolving to the credential\n *\n * @example\n * ```typescript\n * const response = await learnCard.askCredentialSpecific('credential-id-123');\n * if (response.credential) {\n * console.log('Received credential:', response.credential);\n * }\n * ```\n */\n public askCredentialSpecific(credentialId: string): Promise<CredentialSpecificResponse> {\n return this.sendMessage<CredentialSpecificResponse>('ASK_CREDENTIAL_SPECIFIC', {\n credentialId,\n });\n }\n\n /**\n * Request user consent for permissions\n *\n * @param contractUri - URI of the consent contract\n * @returns Promise resolving to consent response\n *\n * @example\n * ```typescript\n * // Without redirect (default) - returns VP in response if app owns the contract\n * const response = await learnCard.requestConsent('lc:network:network.learncard.com/trpc:contract:abc123');\n * if (response.granted) {\n * console.log('User granted consent');\n * if (response.vp) {\n * console.log('VP:', response.vp);\n * }\n * }\n *\n * // With redirect - redirects to contract's redirectUrl with VP in URL params\n * const response = await learnCard.requestConsent('lc:network:network.learncard.com/trpc:contract:abc123', { redirect: true });\n * ```\n */\n public requestConsent(\n contractUri: string,\n options: RequestConsentOptions = {}\n ): Promise<ConsentResponse> {\n const { redirect = false } = options;\n\n return this.sendMessage<ConsentResponse>('REQUEST_CONSENT', {\n contractUri,\n redirect,\n });\n }\n\n /**\n * Initiate a template-based credential issuance flow\n *\n * @param templateId - ID of the template/boost to issue\n * @param draftRecipients - Optional array of recipient DIDs\n * @returns Promise resolving to template issue response\n *\n * @example\n * ```typescript\n * const response = await learnCard.initiateTemplateIssue(\n * 'lc:network:network.learncard.com/trpc:boost:xyz789',\n * ['did:key:abc', 'did:key:def']\n * );\n *\n * if (response.issued) {\n * console.log('Template issued successfully');\n * }\n * ```\n */\n public initiateTemplateIssue(\n templateId: string,\n draftRecipients?: string[]\n ): Promise<TemplateIssueResponse> {\n return this.sendMessage<TemplateIssueResponse>('INITIATE_TEMPLATE_ISSUE', {\n templateId,\n draftRecipients: draftRecipients || [],\n });\n }\n\n /**\n * Send a generic event to be processed by the brain service on behalf of this app.\n * This is used for backend-like operations such as issuing credentials.\n *\n * @param event - The event payload to send\n * @returns Promise resolving to the event response\n *\n * @example\n * ```typescript\n * // Issue a credential to the current user\n * const response = await learnCard.sendAppEvent({\n * type: 'send-credential',\n * templateAlias: 'achievement-badge',\n * templateData: { score: '95' }\n * });\n *\n * if (response.credentialUri) {\n * console.log('Credential issued:', response.credentialUri);\n * }\n * ```\n */\n public sendAppEvent(event: AppEvent): Promise<AppEventResponse> {\n return this.sendMessage<AppEventResponse>('APP_EVENT', event);\n }\n\n /**\n * Clean up the SDK and remove event listeners\n */\n public destroy(): void {\n if (this.messageListener) {\n window.removeEventListener('message', this.messageListener);\n this.messageListener = null;\n }\n\n // Reject all pending requests\n for (const [requestId, pending] of this.pendingRequests.entries()) {\n clearTimeout(pending.timeoutId);\n pending.reject({\n code: 'SDK_DESTROYED',\n message: 'SDK was destroyed before request completed',\n });\n }\n\n this.pendingRequests.clear();\n this.isInitialized = false;\n }\n}\n\n/**\n * Factory function to create a PartnerConnect instance\n *\n * @param options - Configuration options\n * @returns PartnerConnect instance\n *\n * @example\n * ```typescript\n * const learnCard = createPartnerConnect({\n * hostOrigin: 'https://learncard.app',\n * protocol: 'LEARNCARD_V1',\n * requestTimeout: 30000\n * });\n * ```\n */\nexport function createPartnerConnect(options: PartnerConnectOptions): PartnerConnect {\n return new PartnerConnect(options);\n}\n\n// Default export for convenience\nexport default createPartnerConnect;\n"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@learncard/partner-connect",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.3",
|
|
4
|
+
"repository": {
|
|
5
|
+
"type": "git",
|
|
6
|
+
"url": "https://github.com/learningeconomy/LearnCard"
|
|
7
|
+
},
|
|
4
8
|
"description": "LearnCard Partner Connect SDK - Promise-based cross-origin messaging for partner apps",
|
|
5
9
|
"main": "dist/partner-connect.js",
|
|
6
10
|
"module": "dist/partner-connect.esm.js",
|
|
@@ -25,6 +29,9 @@
|
|
|
25
29
|
],
|
|
26
30
|
"license": "MIT",
|
|
27
31
|
"author": "Learning Economy Foundation (www.learningeconomy.io)",
|
|
32
|
+
"dependencies": {
|
|
33
|
+
"@learncard/types": "5.11.3"
|
|
34
|
+
},
|
|
28
35
|
"devDependencies": {
|
|
29
36
|
"@rollup/plugin-commonjs": "^22.0.2",
|
|
30
37
|
"@rollup/plugin-node-resolve": "^13.3.0",
|