@learncard/partner-connect 0.2.9 → 0.2.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +285 -169
- package/dist/index.d.ts +145 -5
- package/dist/partner-connect.esm.js +75 -1
- package/dist/partner-connect.esm.js.map +1 -1
- package/dist/partner-connect.js +75 -1
- package/dist/partner-connect.js.map +1 -1
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export { AppEvent, AppEventResponse, SendCredentialEvent } from '@learncard/types';
|
|
1
|
+
import { AppEventResponse, AppEvent } from '@learncard/types';
|
|
2
|
+
export { AppEvent, AppEventResponse, CheckCredentialEvent, SendCredentialEvent } from '@learncard/types';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* LearnCard Partner Connect SDK - Type Definitions
|
|
@@ -89,6 +89,10 @@ interface TemplateCredentialInput {
|
|
|
89
89
|
templateAlias: string;
|
|
90
90
|
/** Optional template data for Mustache-style variable substitution */
|
|
91
91
|
templateData?: Record<string, unknown>;
|
|
92
|
+
/**
|
|
93
|
+
* If true, the host will return an existing credential (if present) instead of issuing a duplicate.
|
|
94
|
+
*/
|
|
95
|
+
preventDuplicateClaim?: boolean;
|
|
92
96
|
}
|
|
93
97
|
/**
|
|
94
98
|
* Response from template-based credential issuance
|
|
@@ -98,6 +102,14 @@ interface TemplateCredentialResponse {
|
|
|
98
102
|
credentialUri: string;
|
|
99
103
|
/** URI of the boost template used */
|
|
100
104
|
boostUri: string;
|
|
105
|
+
/** Whether the credential was already claimed (when preventDuplicateClaim is true) */
|
|
106
|
+
alreadyClaimed?: boolean;
|
|
107
|
+
/** Whether the user has the credential (when preventDuplicateClaim is true) */
|
|
108
|
+
hasCredential?: boolean;
|
|
109
|
+
/** The status of the credential (when preventDuplicateClaim is true) */
|
|
110
|
+
status?: 'pending' | 'claimed' | 'revoked';
|
|
111
|
+
/** The date the credential was received (when preventDuplicateClaim is true) */
|
|
112
|
+
receivedDate?: string;
|
|
101
113
|
[key: string]: unknown;
|
|
102
114
|
}
|
|
103
115
|
/**
|
|
@@ -160,10 +172,80 @@ interface TemplateIssueResponse {
|
|
|
160
172
|
issued: boolean;
|
|
161
173
|
[key: string]: unknown;
|
|
162
174
|
}
|
|
175
|
+
/**
|
|
176
|
+
* Input used to check if the authenticated user already has a credential for a boost template
|
|
177
|
+
*/
|
|
178
|
+
type CheckCredentialInput = {
|
|
179
|
+
templateAlias: string;
|
|
180
|
+
} | {
|
|
181
|
+
boostUri: string;
|
|
182
|
+
};
|
|
183
|
+
/**
|
|
184
|
+
* Response from checkUserHasCredential
|
|
185
|
+
*/
|
|
186
|
+
interface CheckCredentialResponse {
|
|
187
|
+
hasCredential: boolean;
|
|
188
|
+
credentialUri?: string;
|
|
189
|
+
receivedDate?: string;
|
|
190
|
+
status?: 'pending' | 'claimed' | 'revoked';
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Input for checking template issuance status to a specific recipient.
|
|
194
|
+
* The recipient can be either a DID (did:web:...) or a profileId.
|
|
195
|
+
*/
|
|
196
|
+
type CheckIssuanceStatusInput = {
|
|
197
|
+
templateAlias: string;
|
|
198
|
+
recipient: string;
|
|
199
|
+
} | {
|
|
200
|
+
boostUri: string;
|
|
201
|
+
recipient: string;
|
|
202
|
+
};
|
|
203
|
+
/**
|
|
204
|
+
* Response from getTemplateIssuanceStatus
|
|
205
|
+
*/
|
|
206
|
+
interface TemplateIssuanceStatusResponse {
|
|
207
|
+
sent: boolean;
|
|
208
|
+
credentialUri?: string;
|
|
209
|
+
sentDate?: string;
|
|
210
|
+
claimedDate?: string;
|
|
211
|
+
status?: 'pending' | 'claimed' | 'revoked';
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Input for getting template recipients list
|
|
215
|
+
*/
|
|
216
|
+
type GetTemplateRecipientsInput = {
|
|
217
|
+
templateAlias: string;
|
|
218
|
+
limit?: number;
|
|
219
|
+
cursor?: string;
|
|
220
|
+
} | {
|
|
221
|
+
boostUri: string;
|
|
222
|
+
limit?: number;
|
|
223
|
+
cursor?: string;
|
|
224
|
+
};
|
|
225
|
+
/**
|
|
226
|
+
* Individual recipient record in the template recipients response
|
|
227
|
+
*/
|
|
228
|
+
interface TemplateRecipientRecord {
|
|
229
|
+
recipientProfileId: string;
|
|
230
|
+
recipientDisplayName?: string;
|
|
231
|
+
sentDate: string;
|
|
232
|
+
claimedDate?: string;
|
|
233
|
+
credentialUri?: string;
|
|
234
|
+
status: 'pending' | 'claimed' | 'revoked';
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Response from getTemplateRecipients
|
|
238
|
+
*/
|
|
239
|
+
interface TemplateRecipientsResponse {
|
|
240
|
+
records: TemplateRecipientRecord[];
|
|
241
|
+
hasMore: boolean;
|
|
242
|
+
cursor?: string;
|
|
243
|
+
total?: number;
|
|
244
|
+
}
|
|
163
245
|
/**
|
|
164
246
|
* Error codes that can be returned by the LearnCard host
|
|
165
247
|
*/
|
|
166
|
-
type ErrorCode = 'LC_TIMEOUT' | 'LC_UNAUTHENTICATED' | 'CREDENTIAL_NOT_FOUND' | 'USER_REJECTED' | 'UNAUTHORIZED' | 'TEMPLATE_NOT_FOUND' | string;
|
|
248
|
+
type ErrorCode = 'LC_TIMEOUT' | 'LC_UNAUTHENTICATED' | 'CREDENTIAL_NOT_FOUND' | 'USER_REJECTED' | 'UNAUTHORIZED' | 'TEMPLATE_NOT_FOUND' | 'BOOST_NOT_FOUND' | 'INSUFFICIENT_PERMISSIONS' | string;
|
|
167
249
|
/**
|
|
168
250
|
* Error object returned when a request fails
|
|
169
251
|
*/
|
|
@@ -314,6 +396,64 @@ declare class PartnerConnect {
|
|
|
314
396
|
* ```
|
|
315
397
|
*/
|
|
316
398
|
sendCredential(input: unknown | TemplateCredentialInput): Promise<SendCredentialResponse | TemplateCredentialResponse>;
|
|
399
|
+
/**
|
|
400
|
+
* Check whether the current user already has a credential from a given boost template.
|
|
401
|
+
* This is a silent, non-interactive status check for installed app integrations.
|
|
402
|
+
*/
|
|
403
|
+
checkUserHasCredential(input: CheckCredentialInput): Promise<CheckCredentialResponse>;
|
|
404
|
+
/**
|
|
405
|
+
* Check if the current user has issued/sent a specific template to someone.
|
|
406
|
+
* Returns issuance status including sent date and claim status.
|
|
407
|
+
*
|
|
408
|
+
* @param input - Template identifier (templateAlias or boostUri) and recipient identifier (recipientDid or recipientProfileId)
|
|
409
|
+
* @returns Promise resolving to issuance status response
|
|
410
|
+
*
|
|
411
|
+
* @example
|
|
412
|
+
* ```typescript
|
|
413
|
+
* // Check if user already issued 'achievement-badge' to a specific person
|
|
414
|
+
* const status = await learnCard.getTemplateIssuanceStatus({
|
|
415
|
+
* templateAlias: 'achievement-badge',
|
|
416
|
+
* recipientProfileId: 'user123'
|
|
417
|
+
* });
|
|
418
|
+
*
|
|
419
|
+
* if (status.sent) {
|
|
420
|
+
* console.log('Already issued on:', status.sentDate);
|
|
421
|
+
* console.log('Status:', status.status); // 'pending', 'claimed', or 'revoked'
|
|
422
|
+
* }
|
|
423
|
+
* ```
|
|
424
|
+
*/
|
|
425
|
+
getTemplateIssuanceStatus(input: CheckIssuanceStatusInput): Promise<TemplateIssuanceStatusResponse>;
|
|
426
|
+
/**
|
|
427
|
+
* Get the list of all recipients for a specific template/boost.
|
|
428
|
+
* Useful for dashboards showing who has received a credential.
|
|
429
|
+
*
|
|
430
|
+
* @param input - Template identifier (templateAlias or boostUri) and optional pagination params
|
|
431
|
+
* @returns Promise resolving to paginated list of recipients
|
|
432
|
+
*
|
|
433
|
+
* @example
|
|
434
|
+
* ```typescript
|
|
435
|
+
* // Get first 10 recipients of 'achievement-badge'
|
|
436
|
+
* const recipients = await learnCard.getTemplateRecipients({
|
|
437
|
+
* templateAlias: 'achievement-badge',
|
|
438
|
+
* limit: 10
|
|
439
|
+
* });
|
|
440
|
+
*
|
|
441
|
+
* console.log(`Found ${recipients.records.length} recipients`);
|
|
442
|
+
* recipients.records.forEach(r => {
|
|
443
|
+
* console.log(`${r.recipientDisplayName}: ${r.status}`);
|
|
444
|
+
* });
|
|
445
|
+
*
|
|
446
|
+
* // Get next page if available
|
|
447
|
+
* if (recipients.hasMore) {
|
|
448
|
+
* const nextPage = await learnCard.getTemplateRecipients({
|
|
449
|
+
* templateAlias: 'achievement-badge',
|
|
450
|
+
* limit: 10,
|
|
451
|
+
* cursor: recipients.cursor
|
|
452
|
+
* });
|
|
453
|
+
* }
|
|
454
|
+
* ```
|
|
455
|
+
*/
|
|
456
|
+
getTemplateRecipients(input: GetTemplateRecipientsInput): Promise<TemplateRecipientsResponse>;
|
|
317
457
|
/**
|
|
318
458
|
* Launch a feature in the LearnCard host application
|
|
319
459
|
*
|
|
@@ -433,7 +573,7 @@ declare class PartnerConnect {
|
|
|
433
573
|
* }
|
|
434
574
|
* ```
|
|
435
575
|
*/
|
|
436
|
-
sendAppEvent(event: AppEvent): Promise<
|
|
576
|
+
sendAppEvent<T = AppEventResponse>(event: AppEvent): Promise<T>;
|
|
437
577
|
/**
|
|
438
578
|
* Clean up the SDK and remove event listeners
|
|
439
579
|
*/
|
|
@@ -456,4 +596,4 @@ declare class PartnerConnect {
|
|
|
456
596
|
*/
|
|
457
597
|
declare function createPartnerConnect(options?: PartnerConnectOptions): PartnerConnect;
|
|
458
598
|
|
|
459
|
-
export { ConsentResponse, CredentialSearchResponse, CredentialSpecificResponse, ErrorCode, IdentityResponse, LearnCardError, PartnerConnect, PartnerConnectOptions, PendingRequest, PostMessageRequest, PostMessageResponse, RequestConsentOptions, SendCredentialResponse, TemplateCredentialInput, TemplateCredentialResponse, TemplateIssueResponse, VPRQuery, VerifiablePresentationRequest, createPartnerConnect, createPartnerConnect as default };
|
|
599
|
+
export { CheckCredentialInput, CheckCredentialResponse, CheckIssuanceStatusInput, ConsentResponse, CredentialSearchResponse, CredentialSpecificResponse, ErrorCode, GetTemplateRecipientsInput, IdentityResponse, LearnCardError, PartnerConnect, PartnerConnectOptions, PendingRequest, PostMessageRequest, PostMessageResponse, RequestConsentOptions, SendCredentialResponse, TemplateCredentialInput, TemplateCredentialResponse, TemplateIssuanceStatusResponse, TemplateIssueResponse, TemplateRecipientRecord, TemplateRecipientsResponse, VPRQuery, VerifiablePresentationRequest, createPartnerConnect, createPartnerConnect as default };
|
|
@@ -206,11 +206,85 @@ const _PartnerConnect = class _PartnerConnect {
|
|
|
206
206
|
return this.sendAppEvent({
|
|
207
207
|
type: "send-credential",
|
|
208
208
|
templateAlias: templateInput.templateAlias,
|
|
209
|
-
templateData: templateInput.templateData
|
|
209
|
+
templateData: templateInput.templateData,
|
|
210
|
+
preventDuplicateClaim: templateInput.preventDuplicateClaim
|
|
210
211
|
});
|
|
211
212
|
}
|
|
212
213
|
return this.sendMessage("SEND_CREDENTIAL", { credential: input });
|
|
213
214
|
}
|
|
215
|
+
/**
|
|
216
|
+
* Check whether the current user already has a credential from a given boost template.
|
|
217
|
+
* This is a silent, non-interactive status check for installed app integrations.
|
|
218
|
+
*/
|
|
219
|
+
checkUserHasCredential(input) {
|
|
220
|
+
return this.sendAppEvent({
|
|
221
|
+
type: "check-credential",
|
|
222
|
+
...input
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Check if the current user has issued/sent a specific template to someone.
|
|
227
|
+
* Returns issuance status including sent date and claim status.
|
|
228
|
+
*
|
|
229
|
+
* @param input - Template identifier (templateAlias or boostUri) and recipient identifier (recipientDid or recipientProfileId)
|
|
230
|
+
* @returns Promise resolving to issuance status response
|
|
231
|
+
*
|
|
232
|
+
* @example
|
|
233
|
+
* ```typescript
|
|
234
|
+
* // Check if user already issued 'achievement-badge' to a specific person
|
|
235
|
+
* const status = await learnCard.getTemplateIssuanceStatus({
|
|
236
|
+
* templateAlias: 'achievement-badge',
|
|
237
|
+
* recipientProfileId: 'user123'
|
|
238
|
+
* });
|
|
239
|
+
*
|
|
240
|
+
* if (status.sent) {
|
|
241
|
+
* console.log('Already issued on:', status.sentDate);
|
|
242
|
+
* console.log('Status:', status.status); // 'pending', 'claimed', or 'revoked'
|
|
243
|
+
* }
|
|
244
|
+
* ```
|
|
245
|
+
*/
|
|
246
|
+
getTemplateIssuanceStatus(input) {
|
|
247
|
+
return this.sendAppEvent({
|
|
248
|
+
type: "check-issuance-status",
|
|
249
|
+
...input
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* Get the list of all recipients for a specific template/boost.
|
|
254
|
+
* Useful for dashboards showing who has received a credential.
|
|
255
|
+
*
|
|
256
|
+
* @param input - Template identifier (templateAlias or boostUri) and optional pagination params
|
|
257
|
+
* @returns Promise resolving to paginated list of recipients
|
|
258
|
+
*
|
|
259
|
+
* @example
|
|
260
|
+
* ```typescript
|
|
261
|
+
* // Get first 10 recipients of 'achievement-badge'
|
|
262
|
+
* const recipients = await learnCard.getTemplateRecipients({
|
|
263
|
+
* templateAlias: 'achievement-badge',
|
|
264
|
+
* limit: 10
|
|
265
|
+
* });
|
|
266
|
+
*
|
|
267
|
+
* console.log(`Found ${recipients.records.length} recipients`);
|
|
268
|
+
* recipients.records.forEach(r => {
|
|
269
|
+
* console.log(`${r.recipientDisplayName}: ${r.status}`);
|
|
270
|
+
* });
|
|
271
|
+
*
|
|
272
|
+
* // Get next page if available
|
|
273
|
+
* if (recipients.hasMore) {
|
|
274
|
+
* const nextPage = await learnCard.getTemplateRecipients({
|
|
275
|
+
* templateAlias: 'achievement-badge',
|
|
276
|
+
* limit: 10,
|
|
277
|
+
* cursor: recipients.cursor
|
|
278
|
+
* });
|
|
279
|
+
* }
|
|
280
|
+
* ```
|
|
281
|
+
*/
|
|
282
|
+
getTemplateRecipients(input) {
|
|
283
|
+
return this.sendAppEvent({
|
|
284
|
+
type: "get-template-recipients",
|
|
285
|
+
...input
|
|
286
|
+
});
|
|
287
|
+
}
|
|
214
288
|
/**
|
|
215
289
|
* Launch a feature in the LearnCard host application
|
|
216
290
|
*
|
|
@@ -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 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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"}
|
|
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 CheckCredentialInput,\n CheckCredentialResponse,\n CheckIssuanceStatusInput,\n TemplateIssuanceStatusResponse,\n GetTemplateRecipientsInput,\n TemplateRecipientsResponse,\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<TemplateCredentialResponse>({\n type: 'send-credential',\n templateAlias: templateInput.templateAlias,\n templateData: templateInput.templateData,\n preventDuplicateClaim: templateInput.preventDuplicateClaim,\n });\n }\n\n return this.sendMessage<SendCredentialResponse>('SEND_CREDENTIAL', { credential: input });\n }\n\n /**\n * Check whether the current user already has a credential from a given boost template.\n * This is a silent, non-interactive status check for installed app integrations.\n */\n public checkUserHasCredential(input: CheckCredentialInput): Promise<CheckCredentialResponse> {\n return this.sendAppEvent<CheckCredentialResponse>({\n type: 'check-credential',\n ...input,\n });\n }\n\n /**\n * Check if the current user has issued/sent a specific template to someone.\n * Returns issuance status including sent date and claim status.\n *\n * @param input - Template identifier (templateAlias or boostUri) and recipient identifier (recipientDid or recipientProfileId)\n * @returns Promise resolving to issuance status response\n *\n * @example\n * ```typescript\n * // Check if user already issued 'achievement-badge' to a specific person\n * const status = await learnCard.getTemplateIssuanceStatus({\n * templateAlias: 'achievement-badge',\n * recipientProfileId: 'user123'\n * });\n *\n * if (status.sent) {\n * console.log('Already issued on:', status.sentDate);\n * console.log('Status:', status.status); // 'pending', 'claimed', or 'revoked'\n * }\n * ```\n */\n public getTemplateIssuanceStatus(\n input: CheckIssuanceStatusInput\n ): Promise<TemplateIssuanceStatusResponse> {\n return this.sendAppEvent<TemplateIssuanceStatusResponse>({\n type: 'check-issuance-status',\n ...input,\n });\n }\n\n /**\n * Get the list of all recipients for a specific template/boost.\n * Useful for dashboards showing who has received a credential.\n *\n * @param input - Template identifier (templateAlias or boostUri) and optional pagination params\n * @returns Promise resolving to paginated list of recipients\n *\n * @example\n * ```typescript\n * // Get first 10 recipients of 'achievement-badge'\n * const recipients = await learnCard.getTemplateRecipients({\n * templateAlias: 'achievement-badge',\n * limit: 10\n * });\n *\n * console.log(`Found ${recipients.records.length} recipients`);\n * recipients.records.forEach(r => {\n * console.log(`${r.recipientDisplayName}: ${r.status}`);\n * });\n *\n * // Get next page if available\n * if (recipients.hasMore) {\n * const nextPage = await learnCard.getTemplateRecipients({\n * templateAlias: 'achievement-badge',\n * limit: 10,\n * cursor: recipients.cursor\n * });\n * }\n * ```\n */\n public getTemplateRecipients(\n input: GetTemplateRecipientsInput\n ): Promise<TemplateRecipientsResponse> {\n return this.sendAppEvent<TemplateRecipientsResponse>({\n type: 'get-template-recipients',\n ...input,\n });\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<T = AppEventResponse>(event: AppEvent): Promise<T> {\n return this.sendMessage<T>('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
|
@@ -210,11 +210,85 @@ const _PartnerConnect = class _PartnerConnect {
|
|
|
210
210
|
return this.sendAppEvent({
|
|
211
211
|
type: "send-credential",
|
|
212
212
|
templateAlias: templateInput.templateAlias,
|
|
213
|
-
templateData: templateInput.templateData
|
|
213
|
+
templateData: templateInput.templateData,
|
|
214
|
+
preventDuplicateClaim: templateInput.preventDuplicateClaim
|
|
214
215
|
});
|
|
215
216
|
}
|
|
216
217
|
return this.sendMessage("SEND_CREDENTIAL", { credential: input });
|
|
217
218
|
}
|
|
219
|
+
/**
|
|
220
|
+
* Check whether the current user already has a credential from a given boost template.
|
|
221
|
+
* This is a silent, non-interactive status check for installed app integrations.
|
|
222
|
+
*/
|
|
223
|
+
checkUserHasCredential(input) {
|
|
224
|
+
return this.sendAppEvent({
|
|
225
|
+
type: "check-credential",
|
|
226
|
+
...input
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* Check if the current user has issued/sent a specific template to someone.
|
|
231
|
+
* Returns issuance status including sent date and claim status.
|
|
232
|
+
*
|
|
233
|
+
* @param input - Template identifier (templateAlias or boostUri) and recipient identifier (recipientDid or recipientProfileId)
|
|
234
|
+
* @returns Promise resolving to issuance status response
|
|
235
|
+
*
|
|
236
|
+
* @example
|
|
237
|
+
* ```typescript
|
|
238
|
+
* // Check if user already issued 'achievement-badge' to a specific person
|
|
239
|
+
* const status = await learnCard.getTemplateIssuanceStatus({
|
|
240
|
+
* templateAlias: 'achievement-badge',
|
|
241
|
+
* recipientProfileId: 'user123'
|
|
242
|
+
* });
|
|
243
|
+
*
|
|
244
|
+
* if (status.sent) {
|
|
245
|
+
* console.log('Already issued on:', status.sentDate);
|
|
246
|
+
* console.log('Status:', status.status); // 'pending', 'claimed', or 'revoked'
|
|
247
|
+
* }
|
|
248
|
+
* ```
|
|
249
|
+
*/
|
|
250
|
+
getTemplateIssuanceStatus(input) {
|
|
251
|
+
return this.sendAppEvent({
|
|
252
|
+
type: "check-issuance-status",
|
|
253
|
+
...input
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
/**
|
|
257
|
+
* Get the list of all recipients for a specific template/boost.
|
|
258
|
+
* Useful for dashboards showing who has received a credential.
|
|
259
|
+
*
|
|
260
|
+
* @param input - Template identifier (templateAlias or boostUri) and optional pagination params
|
|
261
|
+
* @returns Promise resolving to paginated list of recipients
|
|
262
|
+
*
|
|
263
|
+
* @example
|
|
264
|
+
* ```typescript
|
|
265
|
+
* // Get first 10 recipients of 'achievement-badge'
|
|
266
|
+
* const recipients = await learnCard.getTemplateRecipients({
|
|
267
|
+
* templateAlias: 'achievement-badge',
|
|
268
|
+
* limit: 10
|
|
269
|
+
* });
|
|
270
|
+
*
|
|
271
|
+
* console.log(`Found ${recipients.records.length} recipients`);
|
|
272
|
+
* recipients.records.forEach(r => {
|
|
273
|
+
* console.log(`${r.recipientDisplayName}: ${r.status}`);
|
|
274
|
+
* });
|
|
275
|
+
*
|
|
276
|
+
* // Get next page if available
|
|
277
|
+
* if (recipients.hasMore) {
|
|
278
|
+
* const nextPage = await learnCard.getTemplateRecipients({
|
|
279
|
+
* templateAlias: 'achievement-badge',
|
|
280
|
+
* limit: 10,
|
|
281
|
+
* cursor: recipients.cursor
|
|
282
|
+
* });
|
|
283
|
+
* }
|
|
284
|
+
* ```
|
|
285
|
+
*/
|
|
286
|
+
getTemplateRecipients(input) {
|
|
287
|
+
return this.sendAppEvent({
|
|
288
|
+
type: "get-template-recipients",
|
|
289
|
+
...input
|
|
290
|
+
});
|
|
291
|
+
}
|
|
218
292
|
/**
|
|
219
293
|
* Launch a feature in the LearnCard host application
|
|
220
294
|
*
|