@learncard/partner-connect 0.3.6 → 0.3.8
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 +48 -16
- package/dist/index.d.ts +45 -1
- package/dist/partner-connect.esm.js +74 -6
- package/dist/partner-connect.esm.js.map +1 -1
- package/dist/partner-connect.js +74 -6
- package/dist/partner-connect.js.map +1 -1
- package/dist/partner-connect.mjs +74 -6
- package/dist/partner-connect.mjs.map +1 -1
- package/package.json +59 -57
- package/src/index.ts +1013 -0
- package/src/origin-resolution.test.ts +374 -0
- package/src/pattern-matching.test.ts +134 -0
- package/src/types.ts +666 -0
- package/LICENSE +0 -21
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"partner-connect.js","sources":["../src/types.ts","../src/index.ts"],"sourcesContent":["/**\n * LearnCard Partner Connect SDK - Type Definitions\n */\n\n// Re-export AppEvent types from shared types package\nexport type {\n AppEvent,\n SendCredentialEvent,\n CheckCredentialEvent,\n AppEventResponse,\n} from '@learncard/types';\n\n/**\n * Configuration options for initializing the SDK\n */\nexport interface PartnerConnectOptions {\n /**\n * The origin(s) of the LearnCard host.\n *\n * Each entry may be either an **exact origin** (`https://learncard.app`) or a\n * **wildcard pattern** where `*` stands in for a single DNS label portion in\n * the host portion of the origin (e.g. `https://*.learncard.app`,\n * `https://*.vetpass.app`). Wildcards are **only** allowed in the host and\n * only as label(s); the protocol and port must always match exactly.\n *\n * Wildcard patterns match any non-empty chain of labels. So\n * `https://*.learncard.app` matches both `https://staging.learncard.app` and\n * `https://pr-123.preview.learncard.app`, but **not** `https://learncard.app`\n * itself (include the bare origin explicitly if you need it) and **not**\n * `https://evil.learncard.app.attacker.com` (the suffix must match).\n *\n * **Origin Configuration Hierarchy at runtime:**\n * 1. `window.location.ancestorOrigins[0]` (when available) — the real parent\n * origin as reported by the browser, validated against the effective\n * whitelist. This source cannot be spoofed by a malicious query param.\n * 2. `?lc_host_override=<origin>` query parameter — validated against the\n * whitelist. Used by the LearnCard host to tell the SDK which origin it\n * is loading from.\n * 3. `sessionStorage['lc_host_override']` — a previously-validated override\n * persisted across in-iframe navigation.\n * 4. First value in the configured `hostOrigin` array / single string.\n * 5. `PartnerConnect.DEFAULT_HOST_ORIGIN` (`https://learncard.app`).\n *\n * The partner app's configured whitelist is combined with a small built-in\n * list of LearnCard tenant domains (see `disableDefaultTenants` to opt out).\n * This lets a partner app work out-of-the-box inside any current or future\n * `*.learncard.app`, `*.learncard.ai`, or `*.vetpass.app` tenant without a\n * re-deploy.\n *\n * **Examples:**\n *\n * Single origin (production only):\n * ```typescript\n * hostOrigin: 'https://learncard.app'\n * ```\n *\n * Wildcard whitelist (covers staging + preview):\n * ```typescript\n * hostOrigin: ['https://learncard.app', 'https://*.learncard.app']\n * ```\n *\n * Custom tenant alongside the built-in LearnCard defaults:\n * ```typescript\n * hostOrigin: ['https://partner.example.com']\n * // learncard.app / *.learncard.app / *.learncard.ai / vetpass.app / *.vetpass.app\n * // are ALSO trusted because disableDefaultTenants is false.\n * ```\n *\n * @default 'https://learncard.app'\n */\n hostOrigin?: string | string[];\n\n /**\n * Opt out of the built-in LearnCard tenant whitelist.\n *\n * By default, the SDK merges `hostOrigin` with a curated list of LearnCard\n * and tenant domains (see `PartnerConnect.DEFAULT_TRUSTED_TENANTS`) so that\n * partner apps work on any LearnCard-managed tenant without reconfiguration.\n *\n * Set to `true` if you want the partner app to **only** trust the origins\n * you pass in `hostOrigin`.\n *\n * @default false\n */\n disableDefaultTenants?: boolean;\n\n /**\n * Whether to allow native app origins (default: true)\n *\n * @default true\n */\n allowNativeAppOrigins?: boolean;\n\n /**\n * Protocol identifier (default: 'LEARNCARD_V1')\n */\n protocol?: string;\n\n /**\n * Request timeout in milliseconds (default: 30000)\n */\n requestTimeout?: number;\n}\n\n/**\n * Identity information returned from REQUEST_IDENTITY\n */\nexport interface IdentityResponse {\n token: string;\n user: {\n did: string;\n [key: string]: unknown;\n };\n}\n\n/**\n * Response from SEND_CREDENTIAL action (raw credential)\n */\nexport interface SendCredentialResponse {\n credentialId: string;\n [key: string]: unknown;\n}\n\n/**\n * Template-based credential input for sendCredential\n * Uses a pre-configured boost template to issue a credential\n */\nexport interface TemplateCredentialInput {\n /** Alias of the boost template configured for this app */\n templateAlias: string;\n\n /** Optional template data for Mustache-style variable substitution */\n templateData?: Record<string, unknown>;\n\n /**\n * If true, the host will return an existing credential (if present) instead of issuing a duplicate.\n */\n preventDuplicateClaim?: boolean;\n}\n\n/**\n * Response from template-based credential issuance\n */\nexport interface TemplateCredentialResponse {\n /** URI of the issued credential */\n credentialUri: string;\n\n /** URI of the boost template used */\n boostUri: string;\n\n /** Whether the credential was already claimed (when preventDuplicateClaim is true) */\n alreadyClaimed?: boolean;\n\n /** Whether the user has the credential (when preventDuplicateClaim is true) */\n hasCredential?: boolean;\n\n /** The status of the credential (when preventDuplicateClaim is true) */\n status?: 'pending' | 'claimed' | 'revoked';\n\n /** The date the credential was received (when preventDuplicateClaim is true) */\n receivedDate?: string;\n\n [key: string]: unknown;\n}\n\n/**\n * Verifiable Presentation Request Query types\n */\nexport type VPRQuery =\n | {\n type: 'QueryByTitle';\n credentialQuery: {\n reason?: string;\n title: string;\n };\n }\n | {\n type: 'QueryByExample';\n credentialQuery: unknown;\n };\n\n/**\n * Verifiable Presentation Request structure\n */\nexport interface VerifiablePresentationRequest {\n query: VPRQuery[];\n challenge: string;\n domain: string;\n}\n\n/**\n * Response from ASK_CREDENTIAL_SEARCH action\n */\nexport interface CredentialSearchResponse {\n verifiablePresentation?: {\n verifiableCredential: unknown[];\n [key: string]: unknown;\n };\n}\n\n/**\n * Response from ASK_CREDENTIAL_SPECIFIC action\n */\nexport interface CredentialSpecificResponse {\n credential?: unknown;\n}\n\n/**\n * Options for REQUEST_CONSENT action\n */\nexport interface RequestConsentOptions {\n /**\n * If true, redirect to contract's redirectUrl with VP after consent.\n * Default: false\n */\n redirect?: boolean;\n}\n\n/**\n * Payload for REQUEST_CONSENT action\n */\nexport interface RequestConsentPayload {\n /**\n * URI of the consent contract. If not provided, the system will attempt\n * to use the contract configured for the current app listing.\n */\n contractUri?: string;\n redirect?: boolean;\n}\n\n/**\n * Response from REQUEST_CONSENT action\n */\nexport interface ConsentResponse {\n granted: boolean;\n [key: string]: unknown;\n}\n\n/**\n * Response from INITIATE_TEMPLATE_ISSUE action\n */\nexport interface TemplateIssueResponse {\n issued: boolean;\n [key: string]: unknown;\n}\n\n// AppEvent, SendCredentialEvent, CheckCredentialEvent, and AppEventResponse are re-exported from @learncard/types above\n\n/**\n * Input used to check if the authenticated user already has a credential for a boost template\n */\nexport type CheckCredentialInput = { templateAlias: string } | { boostUri: string };\n\n/**\n * Response from checkUserHasCredential\n */\nexport interface CheckCredentialResponse {\n hasCredential: boolean;\n credentialUri?: string;\n receivedDate?: string;\n status?: 'pending' | 'claimed' | 'revoked';\n}\n\n/**\n * Input for checking template issuance status to a specific recipient.\n * The recipient can be either a DID (did:web:...) or a profileId.\n */\nexport type CheckIssuanceStatusInput =\n | { templateAlias: string; recipient: string }\n | { boostUri: string; recipient: string };\n\n/**\n * Response from getTemplateIssuanceStatus\n */\nexport interface TemplateIssuanceStatusResponse {\n sent: boolean;\n credentialUri?: string;\n sentDate?: string;\n claimedDate?: string;\n status?: 'pending' | 'claimed' | 'revoked';\n}\n\n/**\n * Input for getting template recipients list\n */\nexport type GetTemplateRecipientsInput =\n | { templateAlias: string; limit?: number; cursor?: string }\n | { boostUri: string; limit?: number; cursor?: string };\n\n/**\n * Individual recipient record in the template recipients response\n */\nexport interface TemplateRecipientRecord {\n recipientProfileId: string;\n recipientDisplayName?: string;\n sentDate: string;\n claimedDate?: string;\n credentialUri?: string;\n status: 'pending' | 'claimed' | 'revoked';\n}\n\n/**\n * Response from getTemplateRecipients\n */\nexport interface TemplateRecipientsResponse {\n records: TemplateRecipientRecord[];\n hasMore: boolean;\n cursor?: string;\n total?: number;\n}\n\n/**\n * Options for REQUEST_LEARNER_CONTEXT action\n */\nexport interface RequestLearnerContextOptions {\n /**\n * Whether to include credentials in the context\n * @default true\n */\n includeCredentials?: boolean;\n\n /**\n * Whether to include personal data (name, bio, etc.) in the context\n * @default false\n */\n includePersonalData?: boolean;\n\n /**\n * Format of the response\n * - 'prompt': Returns LLM-ready formatted text\n * - 'structured': Returns structured data object\n * @default 'prompt'\n */\n format?: 'prompt' | 'structured';\n\n /**\n * Optional instructions to guide the LLM prompt generation\n */\n instructions?: string;\n\n /**\n * Level of detail in the generated prompt\n * @default 'compact'\n */\n detailLevel?: 'compact' | 'expanded';\n}\n\n/**\n * Raw data included in learner context response (when format is 'structured')\n */\nexport interface LearnerContextRawData {\n /** Array of Verifiable Credentials */\n credentials: unknown[];\n\n /** Personal data if requested and available (name, bio, etc.) */\n personalData?: Record<string, unknown>;\n}\n\n/**\n * Response from REQUEST_LEARNER_CONTEXT action\n */\nexport interface LearnerContextResponse {\n /** LLM-ready formatted prompt text */\n prompt: string;\n\n /** Raw structured data (only included when format is 'structured') */\n raw?: LearnerContextRawData;\n\n /** User's DID */\n did: string;\n\n /** User's display name if available */\n displayName?: string;\n}\n\n/**\n * Keywords for next steps in summary credential data\n */\nexport interface SummaryCredentialKeyword {\n occupations: string[] | null;\n careers: string[] | null;\n jobs: string[] | null;\n skills: string[] | null;\n fieldOfStudy: string | null;\n}\n\n/**\n * Skill item in summary credential data\n */\nexport interface SummaryCredentialSkill {\n title: string;\n description: string;\n}\n\n/**\n * Next step item in summary credential data\n *\n * `keywords` is optional. Omit it entirely (or pass `undefined`) when you do not\n * have taxonomy data — you no longer need to pass a struct of `null` fields.\n */\nexport interface SummaryCredentialNextStep {\n title: string;\n description: string;\n keywords?: SummaryCredentialKeyword;\n}\n\n/**\n * Reflection item in summary credential data\n */\nexport interface SummaryCredentialReflection {\n title: string;\n description: string;\n}\n\n/**\n * Summary data for an AI Session credential\n * Contains structured information about what was learned\n */\nexport interface SummaryCredentialData {\n /** Short, concise title for the learning session or credential */\n title: string;\n /** Comprehensive summary of what happened during the session */\n summary: string;\n /** Bullet points of key knowledge gained */\n learned: string[];\n /** Categorized skills learned during the session */\n skills: SummaryCredentialSkill[];\n /** Recommended follow-up activities or learning modules */\n nextSteps: SummaryCredentialNextStep[];\n /** Reflections on the learning experience */\n reflections: SummaryCredentialReflection[];\n}\n\n/**\n * Input for creating and sending an AI Session credential\n */\nexport interface SendAiSessionCredentialInput {\n /** Title of this specific AI session */\n sessionTitle: string;\n /** Structured summary data about what was learned */\n summaryData: SummaryCredentialData;\n /** Optional metadata for the session */\n metadata?: Record<string, unknown>;\n}\n\n/**\n * Response from sending an AI Session credential\n */\nexport interface SendAiSessionCredentialResponse {\n /** URI of the AI Topic (parent) boost */\n topicUri: string;\n /** URI of the topic credential, if a new topic was created */\n topicCredentialUri?: string;\n /** URI of the created AI Session credential */\n sessionCredentialUri: string;\n /** URI of the session boost (child of topic) */\n sessionBoostUri: string;\n /** Whether a new topic was created (true) or existing was used (false) */\n isNewTopic: boolean;\n}\n\n/**\n * Input for sending a notification to the current user from this app.\n * The notification appears in the user's LearnCard notification inbox.\n */\nexport interface AppNotificationInput {\n /** Notification title */\n title?: string;\n\n /** Notification body text */\n body?: string;\n\n /** Deep link path within the app (e.g. '/prizes') */\n actionPath?: string;\n\n /** Grouping category (e.g. 'reward', 'announcement', 'status') */\n category?: string;\n\n /** Notification priority */\n priority?: 'normal' | 'high';\n}\n\n/**\n * Response from sendNotification\n */\nexport interface AppNotificationResponse {\n sent: boolean;\n}\n\n/**\n * Response from incrementCounter\n */\nexport interface IncrementCounterResponse {\n key: string;\n previousValue: number;\n newValue: number;\n}\n\n/**\n * Response from getCounter\n */\nexport interface GetCounterResponse {\n key: string;\n value: number;\n updatedAt: string | null;\n}\n\n/**\n * Response from getCounters\n */\nexport interface GetCountersResponse {\n counters: GetCounterResponse[];\n}\n\n/**\n * Error codes that can be returned by the LearnCard host\n */\nexport type ErrorCode =\n | 'LC_TIMEOUT'\n | 'LC_UNAUTHENTICATED'\n | 'CREDENTIAL_NOT_FOUND'\n | 'USER_REJECTED'\n | 'UNAUTHORIZED'\n | 'TEMPLATE_NOT_FOUND'\n | 'BOOST_NOT_FOUND'\n | 'INSUFFICIENT_PERMISSIONS'\n | string;\n\n/**\n * Error object returned when a request fails.\n *\n * Historically the SDK rejected with a plain `{ code, message }` object. As of\n * v0.3.0 we reject with a {@link PartnerConnectError} instance instead, which\n * still satisfies this interface (it has both `code` and `message` fields), so\n * existing consumers that do `if (err.code === '...')` continue to work\n * unchanged.\n */\nexport interface LearnCardError {\n code: ErrorCode;\n message: string;\n}\n\n/**\n * Typed error class for all Partner Connect SDK rejections.\n *\n * Use `instanceof PartnerConnectError` to narrow caught errors and unlock\n * exhaustive `switch` checks on `code`. Both `code` and `message` are present\n * (so the legacy `LearnCardError` object shape is preserved), and `name` is\n * always `'PartnerConnectError'`.\n *\n * @example\n * ```typescript\n * try {\n * await learnCard.requestLearnerContext();\n * } catch (err) {\n * if (err instanceof PartnerConnectError) {\n * switch (err.code) {\n * case 'LC_UNAUTHENTICATED': showLogin(); break;\n * case 'USER_REJECTED': showPrivacyNotice(); break;\n * case 'UNAUTHORIZED': showPermissionsError(); break;\n * default: console.error(err);\n * }\n * }\n * }\n * ```\n */\nexport class PartnerConnectError extends Error implements LearnCardError {\n public readonly code: ErrorCode;\n\n constructor(code: ErrorCode, message: string) {\n super(message);\n this.name = 'PartnerConnectError';\n this.code = code;\n\n // Restore prototype chain when transpiled to ES5 targets.\n Object.setPrototypeOf(this, PartnerConnectError.prototype);\n }\n\n /**\n * Wrap any incoming `LearnCardError`-shaped value into a `PartnerConnectError`.\n * Returns the value unchanged if it is already an instance.\n *\n * Used internally at every reject site so callers always receive a typed\n * `PartnerConnectError`, regardless of whether the failure originated from\n * the host (over postMessage), an SDK timeout, or `destroy()`.\n */\n public static from(input: LearnCardError | unknown): PartnerConnectError {\n if (input instanceof PartnerConnectError) return input;\n\n if (\n input &&\n typeof input === 'object' &&\n 'code' in input &&\n typeof (input as { code: unknown }).code === 'string'\n ) {\n const candidate = input as { code: string; message?: unknown };\n const message =\n typeof candidate.message === 'string'\n ? candidate.message\n : 'Partner Connect request failed';\n return new PartnerConnectError(candidate.code as ErrorCode, message);\n }\n\n return new PartnerConnectError(\n 'UNKNOWN_ERROR',\n input instanceof Error ? input.message : 'An unknown error occurred'\n );\n }\n}\n\n/**\n * Internal message structure sent via postMessage\n */\nexport interface PostMessageRequest {\n protocol: string;\n action: string;\n requestId: string;\n payload?: unknown;\n}\n\n/**\n * Internal message structure received via postMessage\n */\nexport interface PostMessageResponse {\n protocol: string;\n requestId: string;\n type: 'SUCCESS' | 'ERROR';\n data?: unknown;\n error?: LearnCardError;\n}\n\n/**\n * Pending request tracking structure\n */\nexport interface PendingRequest {\n resolve: (value: unknown) => void;\n reject: (error: LearnCardError) => void;\n timeoutId: ReturnType<typeof setTimeout>;\n}\n","/**\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 { PartnerConnectError } from './types';\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 RequestLearnerContextOptions,\n LearnerContextResponse,\n SendAiSessionCredentialInput,\n SendAiSessionCredentialResponse,\n AppNotificationInput,\n AppNotificationResponse,\n IncrementCounterResponse,\n GetCounterResponse,\n GetCountersResponse,\n AppEvent,\n AppEventResponse,\n LearnCardError,\n PostMessageRequest,\n PostMessageResponse,\n PendingRequest,\n} from './types';\n\n// Re-export the class as a value plus all type exports.\nexport { PartnerConnectError } from './types';\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 /**\n * Built-in list of LearnCard-managed tenant origins.\n *\n * These are merged with the partner app's configured `hostOrigin` whitelist\n * unless `disableDefaultTenants: true` is passed. This lets a partner app\n * run inside any current or future LearnCard tenant (staging, preview,\n * VetPass, etc.) without needing a re-deploy each time a new tenant is\n * onboarded.\n *\n * Patterns follow the same rules as user-supplied `hostOrigin` entries:\n * `*` is a wildcard for one or more DNS labels in the host portion.\n */\n public static readonly DEFAULT_TRUSTED_TENANTS: readonly string[] = [\n 'https://learncard.app',\n 'https://*.learncard.app',\n 'https://*.learncard.ai',\n 'https://vetpass.app',\n 'https://*.vetpass.app',\n ];\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 const configured = Array.isArray(hostOrigin) ? hostOrigin : [hostOrigin];\n\n // Merge with the built-in tenant list unless the caller explicitly\n // opted out. De-duplicate while preserving order so the caller's\n // first entry remains the default active origin.\n const disableDefaults = options?.disableDefaultTenants === true;\n const merged = disableDefaults\n ? [...configured]\n : [...configured, ...PartnerConnect.DEFAULT_TRUSTED_TENANTS];\n this.hostOrigins = Array.from(new Set(merged));\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. `window.location.ancestorOrigins[0]` (when supported) — the browser's\n * view of who our parent frame is. Cannot be forged by a malicious\n * `lc_host_override` query param and therefore takes precedence.\n * 2. `?lc_host_override=<origin>` query param (for staging / cross-tenant).\n * 3. `sessionStorage` value saved from a previously-validated override.\n * 4. First configured origin.\n * 5. `DEFAULT_HOST_ORIGIN`.\n *\n * When a valid override is found in the query parameter, it is persisted\n * to sessionStorage so subsequent in-iframe navigations in the same tab\n * continue to use the same active origin.\n */\n private static readonly SESSION_STORAGE_KEY = 'lc_host_override';\n\n /**\n * Read `window.location.ancestorOrigins[0]` without throwing if the\n * property is unavailable (Firefox) or the list is empty (top-level\n * context, e.g. running outside of an iframe).\n */\n private readAncestorOrigin(): string | null {\n if (typeof window === 'undefined') return null;\n\n try {\n const ancestors = window.location.ancestorOrigins;\n\n if (ancestors && ancestors.length > 0) {\n const parent = ancestors[0];\n\n if (typeof parent === 'string' && parent.length > 0) {\n return parent;\n }\n }\n } catch {\n // `ancestorOrigins` is a WebKit/Blink extension; accessing it\n // under unusual conditions can throw. Treat as unavailable.\n }\n\n return null;\n }\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 const ancestorOrigin = this.readAncestorOrigin();\n const urlParams = new URLSearchParams(window.location.search);\n const hostOverride = urlParams.get('lc_host_override');\n\n // Priority 1: the real parent origin as reported by the browser.\n // This is unspoofable by query-param manipulation, so if it is\n // trusted we use it and ignore any override. If both are present\n // and disagree, we log and prefer the ancestor.\n if (ancestorOrigin && this.isOriginInWhitelist(ancestorOrigin)) {\n if (hostOverride && hostOverride !== ancestorOrigin) {\n console.warn(\n '[LearnCard SDK] lc_host_override does not match the real parent origin; preferring parent.',\n { override: hostOverride, parent: ancestorOrigin }\n );\n }\n\n this.activeHostOrigin = ancestorOrigin;\n this.persistOverride(ancestorOrigin);\n console.log('[LearnCard SDK] Using parent origin:', ancestorOrigin);\n return;\n }\n\n // Priority 2: lc_host_override query parameter.\n if (hostOverride) {\n if (this.isOriginInWhitelist(hostOverride)) {\n this.activeHostOrigin = hostOverride;\n this.persistOverride(hostOverride);\n console.log('[LearnCard SDK] Using lc_host_override:', hostOverride);\n return;\n }\n\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 }\n\n // Priority 3: a previously-validated override from this tab session.\n let storedOverride: string | null = null;\n\n try {\n storedOverride = sessionStorage.getItem(PartnerConnect.SESSION_STORAGE_KEY);\n } catch {\n // sessionStorage may be unavailable (e.g. sandboxed iframes)\n }\n\n if (storedOverride && this.isOriginInWhitelist(storedOverride)) {\n this.activeHostOrigin = storedOverride;\n console.log('[LearnCard SDK] Using stored lc_host_override:', storedOverride);\n return;\n }\n\n // Priority 4/5: fall back to the 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 } 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 persistOverride(origin: string): void {\n try {\n sessionStorage.setItem(PartnerConnect.SESSION_STORAGE_KEY, origin);\n } catch {\n // sessionStorage may be unavailable (e.g. sandboxed iframes)\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 * Internal placeholder substituted in for `*` so that `new URL(...)` can\n * parse a wildcard pattern. Chosen to be a syntactically-valid DNS label\n * that cannot collide with a real hostname.\n */\n private static readonly WILDCARD_PLACEHOLDER = '__lc_wildcard__';\n\n /** `*` (any number of occurrences) for replacement in the pattern. */\n private static readonly WILDCARD_REGEX = /\\*/g;\n\n /** The required leading-label form a wildcard pattern must take. */\n private static readonly WILDCARD_LEADING_PREFIX = `${PartnerConnect.WILDCARD_PLACEHOLDER}.`;\n\n /**\n * Check whether a candidate origin matches a configured whitelist entry.\n *\n * Supports exact matches and wildcard patterns. A wildcard entry has the\n * form `<protocol>://*.<domain>` and matches any origin with the same\n * protocol, same port, and a host ending in `.<domain>` with at least\n * one non-empty DNS label in place of the `*`.\n *\n * Examples with pattern `https://*.learncard.app`:\n * - `https://staging.learncard.app` → match\n * - `https://pr-1.preview.learncard.app` → match\n * - `https://learncard.app` → no match (no subdomain)\n * - `http://staging.learncard.app` → no match (protocol mismatch)\n * - `https://learncard.app.attacker.com` → no match (suffix mismatch)\n *\n * Exposed as a public static so it can be unit-tested directly without\n * standing up a full SDK instance.\n */\n public static matchesOriginPattern(candidate: string, pattern: string): boolean {\n if (candidate === pattern) return true;\n if (!pattern.includes('*')) return false;\n\n let patternUrl: URL;\n let candidateUrl: URL;\n\n try {\n // Replace the wildcard labels with a syntactically-valid host so\n // URL() can parse it; we validate the real shape ourselves below.\n patternUrl = new URL(\n pattern.replace(\n PartnerConnect.WILDCARD_REGEX,\n PartnerConnect.WILDCARD_PLACEHOLDER\n )\n );\n candidateUrl = new URL(candidate);\n } catch {\n return false;\n }\n\n // Protocol, port, and (empty) path-origin must match exactly.\n if (patternUrl.protocol !== candidateUrl.protocol) return false;\n if (patternUrl.port !== candidateUrl.port) return false;\n\n const patternHost = patternUrl.hostname;\n const candidateHost = candidateUrl.hostname;\n\n // Only allow wildcards as leading label(s): `*.foo.bar`, not `a*.b` or\n // `foo.*.bar`. This keeps the matching rule predictable and safe.\n if (!patternHost.startsWith(PartnerConnect.WILDCARD_LEADING_PREFIX)) return false;\n\n const patternSuffix = patternHost.slice(PartnerConnect.WILDCARD_LEADING_PREFIX.length);\n\n if (patternSuffix.length === 0) return false;\n // No further wildcards anywhere else in the pattern.\n if (patternSuffix.includes(PartnerConnect.WILDCARD_PLACEHOLDER)) return false;\n\n // Candidate must end with `.<suffix>` and have at least one label\n // before the suffix (the portion that the `*` stands in for).\n const required = '.' + patternSuffix;\n\n if (!candidateHost.endsWith(required)) return false;\n\n const prefix = candidateHost.slice(0, candidateHost.length - required.length);\n\n if (prefix.length === 0) return false;\n // Labels in the prefix must themselves be non-empty (no `..`).\n if (prefix.startsWith('.') || prefix.endsWith('.')) return false;\n if (prefix.split('.').some(label => label.length === 0)) return false;\n\n return true;\n }\n\n /**\n * Check if an origin is in the effective whitelist (exact origins +\n * wildcard patterns + optional native-app origins).\n */\n private isOriginInWhitelist(origin: string): boolean {\n if (!origin) return false;\n\n for (const entry of this.hostOrigins) {\n if (PartnerConnect.matchesOriginPattern(origin, entry)) return true;\n }\n\n if (this.allowNativeAppOrigins && this.isOriginNativeApp(origin)) return true;\n\n return false;\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 PartnerConnectError.from(\n data.error || {\n code: 'UNKNOWN_ERROR',\n message: 'An unknown error occurred',\n }\n )\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 new PartnerConnectError('SDK_NOT_INITIALIZED', 'SDK is not initialized')\n );\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 new PartnerConnectError(\n 'LC_TIMEOUT',\n `Request ${action} timed out after ${this.requestTimeout}ms`\n )\n );\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 (optional for App Store apps with configured contracts)\n * @param options - Additional options including redirect behavior\n * @returns Promise resolving to consent response\n *\n * @example\n * ```typescript\n * // With explicit contract URI (for external/non-app store integrations)\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 * // Without contract URI (uses app's configured contract from integration)\n * // This works for App Store apps that have configured a contract in their integration\n * const response = await learnCard.requestConsent();\n * if (response.granted) {\n * console.log('User granted consent using listing contract');\n * }\n *\n * // With redirect - redirects to contract's redirectUrl with VP in URL params\n * const response = await learnCard.requestConsent(undefined, { 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 * Request comprehensive learner context for AI tutoring systems.\n *\n * This method retrieves the user's credentials and personal data,\n * then formats them into an LLM-ready prompt that can be injected directly into\n * an AI system prompt.\n *\n * @param options - Configuration options for what data to include and how to format it\n * @returns Promise resolving to learner context with prompt and optional raw data\n *\n * @example\n * ```typescript\n * // Get LLM-ready prompt with credentials and personal data\n * const context = await learnCard.requestLearnerContext({\n * includeCredentials: true,\n * includePersonalData: true,\n * format: 'prompt',\n * instructions: 'Focus on technical skills and certifications',\n * detailLevel: 'expanded'\n * });\n *\n * // Use in AI system prompt\n * const systemPrompt = `You are a helpful tutor. ${context.prompt}`;\n *\n * // Access structured data if needed\n * console.log('User DID:', context.did);\n * console.log('Credentials count:', context.raw?.credentials.length);\n * ```\n */\n public requestLearnerContext(\n options?: RequestLearnerContextOptions\n ): Promise<LearnerContextResponse> {\n return this.sendMessage<LearnerContextResponse>('REQUEST_LEARNER_CONTEXT', {\n includeCredentials: options?.includeCredentials ?? true,\n includePersonalData: options?.includePersonalData ?? false,\n format: options?.format ?? 'prompt',\n instructions: options?.instructions,\n detailLevel: options?.detailLevel ?? 'compact',\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 * Create and send an AI Session credential to the user.\n *\n * This method manages the AI Topic → AI Session hierarchy:\n * - Ensures an AI Topic exists for this app (creates one if needed)\n * - Creates a new AI Session as a child of the topic\n * - The topic appears in the user's AI Sessions page with the app's name\n * - All sessions from this app are organized under that topic\n *\n * @param input - Session details including title and optional metadata\n * @returns Promise resolving to topic and session URIs\n */\n public sendAiSessionCredential(\n input: SendAiSessionCredentialInput\n ): Promise<SendAiSessionCredentialResponse> {\n return this.sendAppEvent<SendAiSessionCredentialResponse>({\n type: 'send-ai-session-credential',\n ...input,\n });\n }\n\n /**\n * Send a notification to the current user from this app.\n * The notification appears in the user's LearnCard notification inbox.\n */\n public sendNotification(input: AppNotificationInput): Promise<AppNotificationResponse> {\n return this.sendAppEvent<AppNotificationResponse>({\n type: 'send-notification',\n ...input,\n });\n }\n\n /**\n * Increment or decrement an app-scoped counter for the current user.\n */\n public incrementCounter(key: string, amount: number): Promise<IncrementCounterResponse> {\n return this.sendAppEvent<IncrementCounterResponse>({\n type: 'increment-counter',\n key,\n amount,\n });\n }\n\n /**\n * Read the current value of an app-scoped counter for the current user.\n */\n public getCounter(key: string): Promise<GetCounterResponse> {\n return this.sendAppEvent<GetCounterResponse>({\n type: 'get-counter',\n key,\n });\n }\n\n /**\n * Read multiple app-scoped counters at once for the current user.\n */\n public getCounters(keys?: string[]): Promise<GetCountersResponse> {\n return this.sendAppEvent<GetCountersResponse>({\n type: 'get-counters',\n ...(keys ? { keys } : {}),\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 new PartnerConnectError(\n 'SDK_DESTROYED',\n 'SDK was destroyed before request completed'\n )\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":["__publicField"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"partner-connect.js","sources":["../src/types.ts","../src/index.ts"],"sourcesContent":["/**\n * LearnCard Partner Connect SDK - Type Definitions\n */\n\n// Re-export AppEvent types from shared types package\nexport type {\n AppEvent,\n SendCredentialEvent,\n CheckCredentialEvent,\n AppEventResponse,\n} from '@learncard/types';\n\n/**\n * Configuration options for initializing the SDK\n */\nexport interface PartnerConnectOptions {\n /**\n * The origin(s) of the LearnCard host.\n *\n * Each entry may be either an **exact origin** (`https://learncard.app`) or a\n * **wildcard pattern** where `*` stands in for a single DNS label portion in\n * the host portion of the origin (e.g. `https://*.learncard.app`,\n * `https://*.vetpass.app`). Wildcards are **only** allowed in the host and\n * only as label(s); the protocol and port must always match exactly.\n *\n * Wildcard patterns match any non-empty chain of labels. So\n * `https://*.learncard.app` matches both `https://staging.learncard.app` and\n * `https://pr-123.preview.learncard.app`, but **not** `https://learncard.app`\n * itself (include the bare origin explicitly if you need it) and **not**\n * `https://evil.learncard.app.attacker.com` (the suffix must match).\n *\n * **Origin Configuration Hierarchy at runtime:**\n * 1. `window.location.ancestorOrigins[0]` (when available) — the real parent\n * origin as reported by the browser, validated against the effective\n * whitelist. This source cannot be spoofed by a malicious query param.\n * 2. `?lc_host_override=<origin>` query parameter — validated against the\n * whitelist. Used by the LearnCard host to tell the SDK which origin it\n * is loading from.\n * 3. `sessionStorage['lc_host_override']` — a previously-validated override\n * persisted across in-iframe navigation.\n * 4. First value in the configured `hostOrigin` array / single string.\n * 5. `PartnerConnect.DEFAULT_HOST_ORIGIN` (`https://learncard.app`).\n *\n * The partner app's configured whitelist is combined with a small built-in\n * list of LearnCard tenant domains (see `disableDefaultTenants` to opt out).\n * This lets a partner app work out-of-the-box inside any current or future\n * `*.learncard.app`, `*.learncard.ai`, or `*.vetpass.app` tenant without a\n * re-deploy.\n *\n * **Examples:**\n *\n * Single origin (production only):\n * ```typescript\n * hostOrigin: 'https://learncard.app'\n * ```\n *\n * Wildcard whitelist (covers staging + preview):\n * ```typescript\n * hostOrigin: ['https://learncard.app', 'https://*.learncard.app']\n * ```\n *\n * Custom tenant alongside the built-in LearnCard defaults:\n * ```typescript\n * hostOrigin: ['https://partner.example.com']\n * // learncard.app / *.learncard.app / *.learncard.ai / vetpass.app / *.vetpass.app\n * // are ALSO trusted because disableDefaultTenants is false.\n * ```\n *\n * @default 'https://learncard.app'\n */\n hostOrigin?: string | string[];\n\n /**\n * Opt out of the built-in LearnCard tenant whitelist.\n *\n * By default, the SDK merges `hostOrigin` with a curated list of LearnCard\n * and tenant domains (see `PartnerConnect.DEFAULT_TRUSTED_TENANTS`) so that\n * partner apps work on any LearnCard-managed tenant without reconfiguration.\n *\n * Set to `true` if you want the partner app to **only** trust the origins\n * you pass in `hostOrigin`.\n *\n * @default false\n */\n disableDefaultTenants?: boolean;\n\n /**\n * Whether to allow native app origins (default: true)\n *\n * @default true\n */\n allowNativeAppOrigins?: boolean;\n\n /**\n * Protocol identifier (default: 'LEARNCARD_V1')\n */\n protocol?: string;\n\n /**\n * Request timeout in milliseconds (default: 30000)\n */\n requestTimeout?: number;\n}\n\n/**\n * Identity information returned from REQUEST_IDENTITY\n */\nexport interface IdentityResponse {\n token: string;\n user: {\n did: string;\n [key: string]: unknown;\n };\n}\n\n/**\n * Response from SEND_CREDENTIAL action (raw credential)\n */\nexport interface SendCredentialResponse {\n credentialId: string;\n [key: string]: unknown;\n}\n\n/**\n * Template-based credential input for sendCredential\n * Uses a pre-configured boost template to issue a credential\n */\nexport interface TemplateCredentialInput {\n /** Alias of the boost template configured for this app */\n templateAlias: string;\n\n /** Optional template data for Mustache-style variable substitution */\n templateData?: Record<string, unknown>;\n\n /**\n * If true, the host will return an existing credential (if present) instead of issuing a duplicate.\n */\n preventDuplicateClaim?: boolean;\n}\n\n/**\n * Response from template-based credential issuance\n */\nexport interface TemplateCredentialResponse {\n /** URI of the issued credential */\n credentialUri: string;\n\n /** URI of the boost template used */\n boostUri: string;\n\n /** Whether the credential was already claimed (when preventDuplicateClaim is true) */\n alreadyClaimed?: boolean;\n\n /** Whether the user has the credential (when preventDuplicateClaim is true) */\n hasCredential?: boolean;\n\n /** The status of the credential (when preventDuplicateClaim is true) */\n status?: 'pending' | 'claimed' | 'revoked';\n\n /** The date the credential was received (when preventDuplicateClaim is true) */\n receivedDate?: string;\n\n [key: string]: unknown;\n}\n\n/**\n * Verifiable Presentation Request Query types\n */\nexport type VPRQuery =\n | {\n type: 'QueryByTitle';\n credentialQuery: {\n reason?: string;\n title: string;\n };\n }\n | {\n type: 'QueryByExample';\n credentialQuery: unknown;\n };\n\n/**\n * Verifiable Presentation Request structure\n */\nexport interface VerifiablePresentationRequest {\n query: VPRQuery[];\n challenge: string;\n domain: string;\n}\n\n/**\n * Response from ASK_CREDENTIAL_SEARCH action\n */\nexport interface CredentialSearchResponse {\n verifiablePresentation?: {\n verifiableCredential: unknown[];\n [key: string]: unknown;\n };\n}\n\n/**\n * Response from ASK_CREDENTIAL_SPECIFIC action\n */\nexport interface CredentialSpecificResponse {\n credential?: unknown;\n}\n\n/**\n * Options for REQUEST_CONSENT action\n */\nexport interface RequestConsentOptions {\n /**\n * If true, redirect to contract's redirectUrl with VP after consent.\n * Default: false\n */\n redirect?: boolean;\n}\n\n/**\n * Payload for REQUEST_CONSENT action\n */\nexport interface RequestConsentPayload {\n /**\n * URI of the consent contract. If not provided, the system will attempt\n * to use the contract configured for the current app listing.\n */\n contractUri?: string;\n redirect?: boolean;\n}\n\n/**\n * Response from REQUEST_CONSENT action\n */\nexport interface ConsentResponse {\n granted: boolean;\n [key: string]: unknown;\n}\n\n/**\n * Response from INITIATE_TEMPLATE_ISSUE action\n */\nexport interface TemplateIssueResponse {\n issued: boolean;\n [key: string]: unknown;\n}\n\n// AppEvent, SendCredentialEvent, CheckCredentialEvent, and AppEventResponse are re-exported from @learncard/types above\n\n/**\n * Input used to check if the authenticated user already has a credential for a boost template\n */\nexport type CheckCredentialInput = { templateAlias: string } | { boostUri: string };\n\n/**\n * Response from checkUserHasCredential\n */\nexport interface CheckCredentialResponse {\n hasCredential: boolean;\n credentialUri?: string;\n receivedDate?: string;\n status?: 'pending' | 'claimed' | 'revoked';\n}\n\n/**\n * Input for checking template issuance status to a specific recipient.\n * The recipient can be either a DID (did:web:...) or a profileId.\n */\nexport type CheckIssuanceStatusInput =\n | { templateAlias: string; recipient: string }\n | { boostUri: string; recipient: string };\n\n/**\n * Response from getTemplateIssuanceStatus\n */\nexport interface TemplateIssuanceStatusResponse {\n sent: boolean;\n credentialUri?: string;\n sentDate?: string;\n claimedDate?: string;\n status?: 'pending' | 'claimed' | 'revoked';\n}\n\n/**\n * Input for getting template recipients list\n */\nexport type GetTemplateRecipientsInput =\n | { templateAlias: string; limit?: number; cursor?: string }\n | { boostUri: string; limit?: number; cursor?: string };\n\n/**\n * Individual recipient record in the template recipients response\n */\nexport interface TemplateRecipientRecord {\n recipientProfileId: string;\n recipientDisplayName?: string;\n sentDate: string;\n claimedDate?: string;\n credentialUri?: string;\n status: 'pending' | 'claimed' | 'revoked';\n}\n\n/**\n * Response from getTemplateRecipients\n */\nexport interface TemplateRecipientsResponse {\n records: TemplateRecipientRecord[];\n hasMore: boolean;\n cursor?: string;\n total?: number;\n}\n\n/**\n * Options for REQUEST_LEARNER_CONTEXT action\n */\nexport interface RequestLearnerContextOptions {\n /**\n * Whether to include credentials in the context\n * @default true\n */\n includeCredentials?: boolean;\n\n /**\n * Whether to include personal data (name, bio, etc.) in the context\n * @default false\n */\n includePersonalData?: boolean;\n\n /**\n * Format of the response\n * - 'prompt': Returns LLM-ready formatted text\n * - 'structured': Returns structured data object\n * @default 'prompt'\n */\n format?: 'prompt' | 'structured';\n\n /**\n * Optional instructions to guide the LLM prompt generation\n */\n instructions?: string;\n\n /**\n * Level of detail in the generated prompt\n * @default 'compact'\n */\n detailLevel?: 'compact' | 'expanded';\n\n /**\n * Wait for LearnCard to finish background ConsentFlow data sync before returning context.\n * Apps that need a complete learner snapshot should set this to true.\n * @default false\n */\n waitForSync?: boolean;\n}\n\n/**\n * Raw data included in learner context response (when format is 'structured')\n */\nexport interface LearnerContextRawData {\n /** Array of Verifiable Credentials */\n credentials: unknown[];\n\n /** Personal data if requested and available (name, bio, etc.) */\n personalData?: Record<string, unknown>;\n}\n\n/**\n * Response from REQUEST_LEARNER_CONTEXT action\n */\nexport interface LearnerContextResponse {\n /** Whether the response used immediately available data or waited for a complete sync */\n status?: 'ready' | 'syncing';\n\n /** Current sync progress when available */\n progress?: SyncProgress;\n\n /** LLM-ready formatted prompt text */\n prompt: string;\n\n /** Raw structured data (only included when format is 'structured') */\n raw?: LearnerContextRawData;\n\n /** User's DID */\n did: string;\n\n /** User's display name if available */\n displayName?: string;\n}\n\nexport interface SyncProgress {\n totalCredentials: number;\n completedCredentials: number;\n failedCredentials: number;\n retryCount: number;\n}\n\nexport interface SyncStatus {\n status: 'ready' | 'syncing' | 'error';\n progress: SyncProgress;\n eta?: number;\n lastError?: string;\n}\n\n/**\n * Keywords for next steps in summary credential data\n */\nexport interface SummaryCredentialKeyword {\n occupations: string[] | null;\n careers: string[] | null;\n jobs: string[] | null;\n skills: string[] | null;\n fieldOfStudy: string | null;\n}\n\n/**\n * Skill item in summary credential data\n */\nexport interface SummaryCredentialSkill {\n title: string;\n description: string;\n}\n\n/**\n * Next step item in summary credential data\n *\n * `keywords` is optional. Omit it entirely (or pass `undefined`) when you do not\n * have taxonomy data — you no longer need to pass a struct of `null` fields.\n */\nexport interface SummaryCredentialNextStep {\n title: string;\n description: string;\n keywords?: SummaryCredentialKeyword;\n}\n\n/**\n * Reflection item in summary credential data\n */\nexport interface SummaryCredentialReflection {\n title: string;\n description: string;\n}\n\n/**\n * Summary data for an AI Session credential\n * Contains structured information about what was learned\n */\nexport interface SummaryCredentialData {\n /** Short, concise title for the learning session or credential */\n title: string;\n /** Comprehensive summary of what happened during the session */\n summary: string;\n /** Bullet points of key knowledge gained */\n learned: string[];\n /** Categorized skills learned during the session */\n skills: SummaryCredentialSkill[];\n /** Recommended follow-up activities or learning modules */\n nextSteps: SummaryCredentialNextStep[];\n /** Reflections on the learning experience */\n reflections: SummaryCredentialReflection[];\n}\n\n/**\n * Input for creating and sending an AI Session credential\n */\nexport interface SendAiSessionCredentialInput {\n /** Title of this specific AI session */\n sessionTitle: string;\n /** Structured summary data about what was learned */\n summaryData: SummaryCredentialData;\n /** Optional metadata for the session */\n metadata?: Record<string, unknown>;\n}\n\n/**\n * Response from sending an AI Session credential\n */\nexport interface SendAiSessionCredentialResponse {\n /** URI of the AI Topic (parent) boost */\n topicUri: string;\n /** URI of the topic credential, if a new topic was created */\n topicCredentialUri?: string;\n /** URI of the created AI Session credential */\n sessionCredentialUri: string;\n /** URI of the session boost (child of topic) */\n sessionBoostUri: string;\n /** Whether a new topic was created (true) or existing was used (false) */\n isNewTopic: boolean;\n}\n\n/**\n * Input for sending a notification to the current user from this app.\n * The notification appears in the user's LearnCard notification inbox.\n */\nexport interface AppNotificationInput {\n /** Notification title */\n title?: string;\n\n /** Notification body text */\n body?: string;\n\n /** Deep link path within the app (e.g. '/prizes') */\n actionPath?: string;\n\n /** Grouping category (e.g. 'reward', 'announcement', 'status') */\n category?: string;\n\n /** Notification priority */\n priority?: 'normal' | 'high';\n}\n\n/**\n * Response from sendNotification\n */\nexport interface AppNotificationResponse {\n sent: boolean;\n}\n\n/**\n * Response from incrementCounter\n */\nexport interface IncrementCounterResponse {\n key: string;\n previousValue: number;\n newValue: number;\n}\n\n/**\n * Response from getCounter\n */\nexport interface GetCounterResponse {\n key: string;\n value: number;\n updatedAt: string | null;\n}\n\n/**\n * Response from getCounters\n */\nexport interface GetCountersResponse {\n counters: GetCounterResponse[];\n}\n\n/**\n * Error codes that can be returned by the LearnCard host\n */\nexport type ErrorCode =\n | 'LC_TIMEOUT'\n | 'LC_UNAUTHENTICATED'\n | 'CREDENTIAL_NOT_FOUND'\n | 'USER_REJECTED'\n | 'UNAUTHORIZED'\n | 'TEMPLATE_NOT_FOUND'\n | 'BOOST_NOT_FOUND'\n | 'INSUFFICIENT_PERMISSIONS'\n | string;\n\n/**\n * Error object returned when a request fails.\n *\n * Historically the SDK rejected with a plain `{ code, message }` object. As of\n * v0.3.0 we reject with a {@link PartnerConnectError} instance instead, which\n * still satisfies this interface (it has both `code` and `message` fields), so\n * existing consumers that do `if (err.code === '...')` continue to work\n * unchanged.\n */\nexport interface LearnCardError {\n code: ErrorCode;\n message: string;\n}\n\n/**\n * Typed error class for all Partner Connect SDK rejections.\n *\n * Use `instanceof PartnerConnectError` to narrow caught errors and unlock\n * exhaustive `switch` checks on `code`. Both `code` and `message` are present\n * (so the legacy `LearnCardError` object shape is preserved), and `name` is\n * always `'PartnerConnectError'`.\n *\n * @example\n * ```typescript\n * try {\n * await learnCard.requestLearnerContext();\n * } catch (err) {\n * if (err instanceof PartnerConnectError) {\n * switch (err.code) {\n * case 'LC_UNAUTHENTICATED': showLogin(); break;\n * case 'USER_REJECTED': showPrivacyNotice(); break;\n * case 'UNAUTHORIZED': showPermissionsError(); break;\n * default: console.error(err);\n * }\n * }\n * }\n * ```\n */\nexport class PartnerConnectError extends Error implements LearnCardError {\n public readonly code: ErrorCode;\n\n constructor(code: ErrorCode, message: string) {\n super(message);\n this.name = 'PartnerConnectError';\n this.code = code;\n\n // Restore prototype chain when transpiled to ES5 targets.\n Object.setPrototypeOf(this, PartnerConnectError.prototype);\n }\n\n /**\n * Wrap any incoming `LearnCardError`-shaped value into a `PartnerConnectError`.\n * Returns the value unchanged if it is already an instance.\n *\n * Used internally at every reject site so callers always receive a typed\n * `PartnerConnectError`, regardless of whether the failure originated from\n * the host (over postMessage), an SDK timeout, or `destroy()`.\n */\n public static from(input: LearnCardError | unknown): PartnerConnectError {\n if (input instanceof PartnerConnectError) return input;\n\n if (\n input &&\n typeof input === 'object' &&\n 'code' in input &&\n typeof (input as { code: unknown }).code === 'string'\n ) {\n const candidate = input as { code: string; message?: unknown };\n const message =\n typeof candidate.message === 'string'\n ? candidate.message\n : 'Partner Connect request failed';\n return new PartnerConnectError(candidate.code as ErrorCode, message);\n }\n\n return new PartnerConnectError(\n 'UNKNOWN_ERROR',\n input instanceof Error ? input.message : 'An unknown error occurred'\n );\n }\n}\n\n/**\n * Internal message structure sent via postMessage\n */\nexport interface PostMessageRequest {\n protocol: string;\n action: string;\n requestId: string;\n payload?: unknown;\n}\n\n/**\n * Internal message structure received via postMessage\n */\nexport interface PostMessageResponse {\n protocol: string;\n requestId: string;\n type: 'SUCCESS' | 'ERROR';\n data?: unknown;\n error?: LearnCardError;\n}\n\n/**\n * Pending request tracking structure\n */\nexport interface PendingRequest {\n resolve: (value: unknown) => void;\n reject: (error: LearnCardError) => void;\n timeoutId: ReturnType<typeof setTimeout>;\n}\n","/**\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 { PartnerConnectError } from './types';\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 RequestLearnerContextOptions,\n LearnerContextResponse,\n SyncStatus,\n SendAiSessionCredentialInput,\n SendAiSessionCredentialResponse,\n AppNotificationInput,\n AppNotificationResponse,\n IncrementCounterResponse,\n GetCounterResponse,\n GetCountersResponse,\n AppEvent,\n AppEventResponse,\n LearnCardError,\n PostMessageRequest,\n PostMessageResponse,\n PendingRequest,\n} from './types';\n\n// Re-export the class as a value plus all type exports.\nexport { PartnerConnectError } from './types';\nexport type * from './types';\n\n/** Maximum time to poll for sync completion before giving up (10 minutes) */\nconst SYNC_STATUS_POLL_MAX_DURATION_MS = 10 * 60 * 1000;\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 /**\n * Built-in list of LearnCard-managed tenant origins.\n *\n * These are merged with the partner app's configured `hostOrigin` whitelist\n * unless `disableDefaultTenants: true` is passed. This lets a partner app\n * run inside any current or future LearnCard tenant (staging, preview,\n * VetPass, etc.) without needing a re-deploy each time a new tenant is\n * onboarded.\n *\n * Patterns follow the same rules as user-supplied `hostOrigin` entries:\n * `*` is a wildcard for one or more DNS labels in the host portion.\n */\n public static readonly DEFAULT_TRUSTED_TENANTS: readonly string[] = [\n 'https://learncard.app',\n 'https://*.learncard.app',\n 'https://*.learncard.ai',\n 'https://vetpass.app',\n 'https://*.vetpass.app',\n ];\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 private syncCompleteCallbacks: Set<(status: SyncStatus) => void> = new Set();\n private syncStatusPollId: ReturnType<typeof setInterval> | null = null;\n\n constructor(options?: PartnerConnectOptions) {\n // Normalize hostOrigin to an array for whitelist validation\n const hostOrigin = options?.hostOrigin ?? PartnerConnect.DEFAULT_HOST_ORIGIN;\n const configured = Array.isArray(hostOrigin) ? hostOrigin : [hostOrigin];\n\n // Merge with the built-in tenant list unless the caller explicitly\n // opted out. De-duplicate while preserving order so the caller's\n // first entry remains the default active origin.\n const disableDefaults = options?.disableDefaultTenants === true;\n const merged = disableDefaults\n ? [...configured]\n : [...configured, ...PartnerConnect.DEFAULT_TRUSTED_TENANTS];\n this.hostOrigins = Array.from(new Set(merged));\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. `window.location.ancestorOrigins[0]` (when supported) — the browser's\n * view of who our parent frame is. Cannot be forged by a malicious\n * `lc_host_override` query param and therefore takes precedence.\n * 2. `?lc_host_override=<origin>` query param (for staging / cross-tenant).\n * 3. `sessionStorage` value saved from a previously-validated override.\n * 4. First configured origin.\n * 5. `DEFAULT_HOST_ORIGIN`.\n *\n * When a valid override is found in the query parameter, it is persisted\n * to sessionStorage so subsequent in-iframe navigations in the same tab\n * continue to use the same active origin.\n */\n private static readonly SESSION_STORAGE_KEY = 'lc_host_override';\n\n /**\n * Read `window.location.ancestorOrigins[0]` without throwing if the\n * property is unavailable (Firefox) or the list is empty (top-level\n * context, e.g. running outside of an iframe).\n */\n private readAncestorOrigin(): string | null {\n if (typeof window === 'undefined') return null;\n\n try {\n const ancestors = window.location.ancestorOrigins;\n\n if (ancestors && ancestors.length > 0) {\n const parent = ancestors[0];\n\n if (typeof parent === 'string' && parent.length > 0) {\n return parent;\n }\n }\n } catch {\n // `ancestorOrigins` is a WebKit/Blink extension; accessing it\n // under unusual conditions can throw. Treat as unavailable.\n }\n\n return null;\n }\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 const ancestorOrigin = this.readAncestorOrigin();\n const urlParams = new URLSearchParams(window.location.search);\n const hostOverride = urlParams.get('lc_host_override');\n\n // Priority 1: the real parent origin as reported by the browser.\n // This is unspoofable by query-param manipulation, so if it is\n // trusted we use it and ignore any override. If both are present\n // and disagree, we log and prefer the ancestor.\n if (ancestorOrigin && this.isOriginInWhitelist(ancestorOrigin)) {\n if (hostOverride && hostOverride !== ancestorOrigin) {\n console.warn(\n '[LearnCard SDK] lc_host_override does not match the real parent origin; preferring parent.',\n { override: hostOverride, parent: ancestorOrigin }\n );\n }\n\n this.activeHostOrigin = ancestorOrigin;\n this.persistOverride(ancestorOrigin);\n console.log('[LearnCard SDK] Using parent origin:', ancestorOrigin);\n return;\n }\n\n // Priority 2: lc_host_override query parameter.\n if (hostOverride) {\n if (this.isOriginInWhitelist(hostOverride)) {\n this.activeHostOrigin = hostOverride;\n this.persistOverride(hostOverride);\n console.log('[LearnCard SDK] Using lc_host_override:', hostOverride);\n return;\n }\n\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 }\n\n // Priority 3: a previously-validated override from this tab session.\n let storedOverride: string | null = null;\n\n try {\n storedOverride = sessionStorage.getItem(PartnerConnect.SESSION_STORAGE_KEY);\n } catch {\n // sessionStorage may be unavailable (e.g. sandboxed iframes)\n }\n\n if (storedOverride && this.isOriginInWhitelist(storedOverride)) {\n this.activeHostOrigin = storedOverride;\n console.log('[LearnCard SDK] Using stored lc_host_override:', storedOverride);\n return;\n }\n\n // Priority 4/5: fall back to the 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 } 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 persistOverride(origin: string): void {\n try {\n sessionStorage.setItem(PartnerConnect.SESSION_STORAGE_KEY, origin);\n } catch {\n // sessionStorage may be unavailable (e.g. sandboxed iframes)\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 * Internal placeholder substituted in for `*` so that `new URL(...)` can\n * parse a wildcard pattern. Chosen to be a syntactically-valid DNS label\n * that cannot collide with a real hostname.\n */\n private static readonly WILDCARD_PLACEHOLDER = '__lc_wildcard__';\n\n /** `*` (any number of occurrences) for replacement in the pattern. */\n private static readonly WILDCARD_REGEX = /\\*/g;\n\n /** The required leading-label form a wildcard pattern must take. */\n private static readonly WILDCARD_LEADING_PREFIX = `${PartnerConnect.WILDCARD_PLACEHOLDER}.`;\n\n /**\n * Check whether a candidate origin matches a configured whitelist entry.\n *\n * Supports exact matches and wildcard patterns. A wildcard entry has the\n * form `<protocol>://*.<domain>` and matches any origin with the same\n * protocol, same port, and a host ending in `.<domain>` with at least\n * one non-empty DNS label in place of the `*`.\n *\n * Examples with pattern `https://*.learncard.app`:\n * - `https://staging.learncard.app` → match\n * - `https://pr-1.preview.learncard.app` → match\n * - `https://learncard.app` → no match (no subdomain)\n * - `http://staging.learncard.app` → no match (protocol mismatch)\n * - `https://learncard.app.attacker.com` → no match (suffix mismatch)\n *\n * Exposed as a public static so it can be unit-tested directly without\n * standing up a full SDK instance.\n */\n public static matchesOriginPattern(candidate: string, pattern: string): boolean {\n if (candidate === pattern) return true;\n if (!pattern.includes('*')) return false;\n\n let patternUrl: URL;\n let candidateUrl: URL;\n\n try {\n // Replace the wildcard labels with a syntactically-valid host so\n // URL() can parse it; we validate the real shape ourselves below.\n patternUrl = new URL(\n pattern.replace(PartnerConnect.WILDCARD_REGEX, PartnerConnect.WILDCARD_PLACEHOLDER)\n );\n candidateUrl = new URL(candidate);\n } catch {\n return false;\n }\n\n // Protocol, port, and (empty) path-origin must match exactly.\n if (patternUrl.protocol !== candidateUrl.protocol) return false;\n if (patternUrl.port !== candidateUrl.port) return false;\n\n const patternHost = patternUrl.hostname;\n const candidateHost = candidateUrl.hostname;\n\n // Only allow wildcards as leading label(s): `*.foo.bar`, not `a*.b` or\n // `foo.*.bar`. This keeps the matching rule predictable and safe.\n if (!patternHost.startsWith(PartnerConnect.WILDCARD_LEADING_PREFIX)) return false;\n\n const patternSuffix = patternHost.slice(PartnerConnect.WILDCARD_LEADING_PREFIX.length);\n\n if (patternSuffix.length === 0) return false;\n // No further wildcards anywhere else in the pattern.\n if (patternSuffix.includes(PartnerConnect.WILDCARD_PLACEHOLDER)) return false;\n\n // Candidate must end with `.<suffix>` and have at least one label\n // before the suffix (the portion that the `*` stands in for).\n const required = '.' + patternSuffix;\n\n if (!candidateHost.endsWith(required)) return false;\n\n const prefix = candidateHost.slice(0, candidateHost.length - required.length);\n\n if (prefix.length === 0) return false;\n // Labels in the prefix must themselves be non-empty (no `..`).\n if (prefix.startsWith('.') || prefix.endsWith('.')) return false;\n if (prefix.split('.').some(label => label.length === 0)) return false;\n\n return true;\n }\n\n /**\n * Check if an origin is in the effective whitelist (exact origins +\n * wildcard patterns + optional native-app origins).\n */\n private isOriginInWhitelist(origin: string): boolean {\n if (!origin) return false;\n\n for (const entry of this.hostOrigins) {\n if (PartnerConnect.matchesOriginPattern(origin, entry)) return true;\n }\n\n if (this.allowNativeAppOrigins && this.isOriginNativeApp(origin)) return true;\n\n return false;\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 PartnerConnectError.from(\n data.error || {\n code: 'UNKNOWN_ERROR',\n message: 'An unknown error occurred',\n }\n )\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 new PartnerConnectError('SDK_NOT_INITIALIZED', 'SDK is not initialized')\n );\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 new PartnerConnectError(\n 'LC_TIMEOUT',\n `Request ${action} timed out after ${this.requestTimeout}ms`\n )\n );\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 (optional for App Store apps with configured contracts)\n * @param options - Additional options including redirect behavior\n * @returns Promise resolving to consent response\n *\n * @example\n * ```typescript\n * // With explicit contract URI (for external/non-app store integrations)\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 * // Without contract URI (uses app's configured contract from integration)\n * // This works for App Store apps that have configured a contract in their integration\n * const response = await learnCard.requestConsent();\n * if (response.granted) {\n * console.log('User granted consent using listing contract');\n * }\n *\n * // With redirect - redirects to contract's redirectUrl with VP in URL params\n * const response = await learnCard.requestConsent(undefined, { 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 * Request comprehensive learner context for AI tutoring systems.\n *\n * This method retrieves the user's credentials and personal data,\n * then formats them into an LLM-ready prompt that can be injected directly into\n * an AI system prompt.\n *\n * @param options - Configuration options for what data to include and how to format it\n * @returns Promise resolving to learner context with prompt and optional raw data\n *\n * @example\n * ```typescript\n * // Get LLM-ready prompt with credentials and personal data\n * const context = await learnCard.requestLearnerContext({\n * includeCredentials: true,\n * includePersonalData: true,\n * waitForSync: true,\n * format: 'prompt',\n * instructions: 'Focus on technical skills and certifications',\n * detailLevel: 'expanded'\n * });\n *\n * if (context.status === 'syncing') {\n * const unsubscribe = learnCard.onSyncComplete(async () => {\n * const readyContext = await learnCard.requestLearnerContext({ waitForSync: true });\n * unsubscribe();\n * });\n * }\n *\n * // Use in AI system prompt\n * const systemPrompt = `You are a helpful tutor. ${context.prompt}`;\n *\n * // Access structured data if needed\n * console.log('User DID:', context.did);\n * console.log('Credentials count:', context.raw?.credentials.length);\n * ```\n */\n public requestLearnerContext(\n options?: RequestLearnerContextOptions\n ): Promise<LearnerContextResponse> {\n return this.sendMessage<LearnerContextResponse>('REQUEST_LEARNER_CONTEXT', {\n includeCredentials: options?.includeCredentials ?? true,\n includePersonalData: options?.includePersonalData ?? false,\n format: options?.format ?? 'prompt',\n instructions: options?.instructions,\n detailLevel: options?.detailLevel ?? 'compact',\n waitForSync: options?.waitForSync ?? false,\n });\n }\n\n /**\n * Get the current LearnCard background data sync status.\n */\n public getSyncStatus(): Promise<SyncStatus> {\n return this.sendMessage<SyncStatus>('GET_SYNC_STATUS');\n }\n\n /**\n * Register a callback that fires when LearnCard reports background sync has reached a\n * terminal state ('ready' or 'error'). Check `status.status` to distinguish the two.\n * Polling stops once a terminal state is reached, all callbacks unsubscribe, or the\n * poll exceeds its maximum duration (reported to callbacks as an 'error' status).\n * Returns an unsubscribe function.\n */\n public onSyncComplete(callback: (status: SyncStatus) => void): () => void {\n this.syncCompleteCallbacks.add(callback);\n\n if (!this.syncStatusPollId) {\n const pollStartedAt = Date.now();\n\n const stopPolling = () => {\n if (this.syncStatusPollId) {\n clearInterval(this.syncStatusPollId);\n this.syncStatusPollId = null;\n }\n };\n\n this.syncStatusPollId = setInterval(() => {\n if (Date.now() - pollStartedAt > SYNC_STATUS_POLL_MAX_DURATION_MS) {\n stopPolling();\n const timeoutStatus: SyncStatus = {\n status: 'error',\n progress: {\n totalCredentials: 0,\n completedCredentials: 0,\n failedCredentials: 0,\n retryCount: 0,\n },\n lastError: 'Timed out waiting for sync to complete',\n };\n this.syncCompleteCallbacks.forEach(cb => cb(timeoutStatus));\n return;\n }\n\n this.getSyncStatus()\n .then(status => {\n if (status.status !== 'ready' && status.status !== 'error') return;\n\n stopPolling();\n this.syncCompleteCallbacks.forEach(cb => cb(status));\n })\n .catch(() => undefined);\n }, 1000);\n }\n\n return () => {\n this.syncCompleteCallbacks.delete(callback);\n if (this.syncCompleteCallbacks.size === 0 && this.syncStatusPollId) {\n clearInterval(this.syncStatusPollId);\n this.syncStatusPollId = null;\n }\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 * Create and send an AI Session credential to the user.\n *\n * This method manages the AI Topic → AI Session hierarchy:\n * - Ensures an AI Topic exists for this app (creates one if needed)\n * - Creates a new AI Session as a child of the topic\n * - The topic appears in the user's AI Sessions page with the app's name\n * - All sessions from this app are organized under that topic\n *\n * @param input - Session details including title and optional metadata\n * @returns Promise resolving to topic and session URIs\n */\n public sendAiSessionCredential(\n input: SendAiSessionCredentialInput\n ): Promise<SendAiSessionCredentialResponse> {\n return this.sendAppEvent<SendAiSessionCredentialResponse>({\n type: 'send-ai-session-credential',\n ...input,\n });\n }\n\n /**\n * Send a notification to the current user from this app.\n * The notification appears in the user's LearnCard notification inbox.\n */\n public sendNotification(input: AppNotificationInput): Promise<AppNotificationResponse> {\n return this.sendAppEvent<AppNotificationResponse>({\n type: 'send-notification',\n ...input,\n });\n }\n\n /**\n * Increment or decrement an app-scoped counter for the current user.\n */\n public incrementCounter(key: string, amount: number): Promise<IncrementCounterResponse> {\n return this.sendAppEvent<IncrementCounterResponse>({\n type: 'increment-counter',\n key,\n amount,\n });\n }\n\n /**\n * Read the current value of an app-scoped counter for the current user.\n */\n public getCounter(key: string): Promise<GetCounterResponse> {\n return this.sendAppEvent<GetCounterResponse>({\n type: 'get-counter',\n key,\n });\n }\n\n /**\n * Read multiple app-scoped counters at once for the current user.\n */\n public getCounters(keys?: string[]): Promise<GetCountersResponse> {\n return this.sendAppEvent<GetCountersResponse>({\n type: 'get-counters',\n ...(keys ? { keys } : {}),\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 new PartnerConnectError(\n 'SDK_DESTROYED',\n 'SDK was destroyed before request completed'\n )\n );\n }\n\n this.pendingRequests.clear();\n\n if (this.syncStatusPollId) {\n clearInterval(this.syncStatusPollId);\n this.syncStatusPollId = null;\n }\n this.syncCompleteCallbacks.clear();\n\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":["__publicField"],"mappings":"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"}
|
package/dist/partner-connect.mjs
CHANGED
|
@@ -34,6 +34,7 @@ class PartnerConnectError extends Error {
|
|
|
34
34
|
var __defProp = Object.defineProperty;
|
|
35
35
|
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
36
36
|
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
37
|
+
const SYNC_STATUS_POLL_MAX_DURATION_MS = 10 * 60 * 1e3;
|
|
37
38
|
const _PartnerConnect = class _PartnerConnect {
|
|
38
39
|
constructor(options) {
|
|
39
40
|
__publicField(this, "hostOrigins", ["https://learncard.app"]);
|
|
@@ -44,6 +45,8 @@ const _PartnerConnect = class _PartnerConnect {
|
|
|
44
45
|
__publicField(this, "pendingRequests");
|
|
45
46
|
__publicField(this, "messageListener", null);
|
|
46
47
|
__publicField(this, "isInitialized", false);
|
|
48
|
+
__publicField(this, "syncCompleteCallbacks", /* @__PURE__ */ new Set());
|
|
49
|
+
__publicField(this, "syncStatusPollId", null);
|
|
47
50
|
var _a, _b;
|
|
48
51
|
const hostOrigin = (_a = options == null ? void 0 : options.hostOrigin) != null ? _a : _PartnerConnect.DEFAULT_HOST_ORIGIN;
|
|
49
52
|
const configured = Array.isArray(hostOrigin) ? hostOrigin : [hostOrigin];
|
|
@@ -162,10 +165,7 @@ const _PartnerConnect = class _PartnerConnect {
|
|
|
162
165
|
let candidateUrl;
|
|
163
166
|
try {
|
|
164
167
|
patternUrl = new URL(
|
|
165
|
-
pattern.replace(
|
|
166
|
-
_PartnerConnect.WILDCARD_REGEX,
|
|
167
|
-
_PartnerConnect.WILDCARD_PLACEHOLDER
|
|
168
|
-
)
|
|
168
|
+
pattern.replace(_PartnerConnect.WILDCARD_REGEX, _PartnerConnect.WILDCARD_PLACEHOLDER)
|
|
169
169
|
);
|
|
170
170
|
candidateUrl = new URL(candidate);
|
|
171
171
|
} catch {
|
|
@@ -562,11 +562,19 @@ const _PartnerConnect = class _PartnerConnect {
|
|
|
562
562
|
* const context = await learnCard.requestLearnerContext({
|
|
563
563
|
* includeCredentials: true,
|
|
564
564
|
* includePersonalData: true,
|
|
565
|
+
* waitForSync: true,
|
|
565
566
|
* format: 'prompt',
|
|
566
567
|
* instructions: 'Focus on technical skills and certifications',
|
|
567
568
|
* detailLevel: 'expanded'
|
|
568
569
|
* });
|
|
569
570
|
*
|
|
571
|
+
* if (context.status === 'syncing') {
|
|
572
|
+
* const unsubscribe = learnCard.onSyncComplete(async () => {
|
|
573
|
+
* const readyContext = await learnCard.requestLearnerContext({ waitForSync: true });
|
|
574
|
+
* unsubscribe();
|
|
575
|
+
* });
|
|
576
|
+
* }
|
|
577
|
+
*
|
|
570
578
|
* // Use in AI system prompt
|
|
571
579
|
* const systemPrompt = `You are a helpful tutor. ${context.prompt}`;
|
|
572
580
|
*
|
|
@@ -576,15 +584,70 @@ const _PartnerConnect = class _PartnerConnect {
|
|
|
576
584
|
* ```
|
|
577
585
|
*/
|
|
578
586
|
requestLearnerContext(options) {
|
|
579
|
-
var _a, _b, _c, _d;
|
|
587
|
+
var _a, _b, _c, _d, _e;
|
|
580
588
|
return this.sendMessage("REQUEST_LEARNER_CONTEXT", {
|
|
581
589
|
includeCredentials: (_a = options == null ? void 0 : options.includeCredentials) != null ? _a : true,
|
|
582
590
|
includePersonalData: (_b = options == null ? void 0 : options.includePersonalData) != null ? _b : false,
|
|
583
591
|
format: (_c = options == null ? void 0 : options.format) != null ? _c : "prompt",
|
|
584
592
|
instructions: options == null ? void 0 : options.instructions,
|
|
585
|
-
detailLevel: (_d = options == null ? void 0 : options.detailLevel) != null ? _d : "compact"
|
|
593
|
+
detailLevel: (_d = options == null ? void 0 : options.detailLevel) != null ? _d : "compact",
|
|
594
|
+
waitForSync: (_e = options == null ? void 0 : options.waitForSync) != null ? _e : false
|
|
586
595
|
});
|
|
587
596
|
}
|
|
597
|
+
/**
|
|
598
|
+
* Get the current LearnCard background data sync status.
|
|
599
|
+
*/
|
|
600
|
+
getSyncStatus() {
|
|
601
|
+
return this.sendMessage("GET_SYNC_STATUS");
|
|
602
|
+
}
|
|
603
|
+
/**
|
|
604
|
+
* Register a callback that fires when LearnCard reports background sync has reached a
|
|
605
|
+
* terminal state ('ready' or 'error'). Check `status.status` to distinguish the two.
|
|
606
|
+
* Polling stops once a terminal state is reached, all callbacks unsubscribe, or the
|
|
607
|
+
* poll exceeds its maximum duration (reported to callbacks as an 'error' status).
|
|
608
|
+
* Returns an unsubscribe function.
|
|
609
|
+
*/
|
|
610
|
+
onSyncComplete(callback) {
|
|
611
|
+
this.syncCompleteCallbacks.add(callback);
|
|
612
|
+
if (!this.syncStatusPollId) {
|
|
613
|
+
const pollStartedAt = Date.now();
|
|
614
|
+
const stopPolling = () => {
|
|
615
|
+
if (this.syncStatusPollId) {
|
|
616
|
+
clearInterval(this.syncStatusPollId);
|
|
617
|
+
this.syncStatusPollId = null;
|
|
618
|
+
}
|
|
619
|
+
};
|
|
620
|
+
this.syncStatusPollId = setInterval(() => {
|
|
621
|
+
if (Date.now() - pollStartedAt > SYNC_STATUS_POLL_MAX_DURATION_MS) {
|
|
622
|
+
stopPolling();
|
|
623
|
+
const timeoutStatus = {
|
|
624
|
+
status: "error",
|
|
625
|
+
progress: {
|
|
626
|
+
totalCredentials: 0,
|
|
627
|
+
completedCredentials: 0,
|
|
628
|
+
failedCredentials: 0,
|
|
629
|
+
retryCount: 0
|
|
630
|
+
},
|
|
631
|
+
lastError: "Timed out waiting for sync to complete"
|
|
632
|
+
};
|
|
633
|
+
this.syncCompleteCallbacks.forEach((cb) => cb(timeoutStatus));
|
|
634
|
+
return;
|
|
635
|
+
}
|
|
636
|
+
this.getSyncStatus().then((status) => {
|
|
637
|
+
if (status.status !== "ready" && status.status !== "error") return;
|
|
638
|
+
stopPolling();
|
|
639
|
+
this.syncCompleteCallbacks.forEach((cb) => cb(status));
|
|
640
|
+
}).catch(() => void 0);
|
|
641
|
+
}, 1e3);
|
|
642
|
+
}
|
|
643
|
+
return () => {
|
|
644
|
+
this.syncCompleteCallbacks.delete(callback);
|
|
645
|
+
if (this.syncCompleteCallbacks.size === 0 && this.syncStatusPollId) {
|
|
646
|
+
clearInterval(this.syncStatusPollId);
|
|
647
|
+
this.syncStatusPollId = null;
|
|
648
|
+
}
|
|
649
|
+
};
|
|
650
|
+
}
|
|
588
651
|
/**
|
|
589
652
|
* Send a generic event to be processed by the brain service on behalf of this app.
|
|
590
653
|
* This is used for backend-like operations such as issuing credentials.
|
|
@@ -683,6 +746,11 @@ const _PartnerConnect = class _PartnerConnect {
|
|
|
683
746
|
);
|
|
684
747
|
}
|
|
685
748
|
this.pendingRequests.clear();
|
|
749
|
+
if (this.syncStatusPollId) {
|
|
750
|
+
clearInterval(this.syncStatusPollId);
|
|
751
|
+
this.syncStatusPollId = null;
|
|
752
|
+
}
|
|
753
|
+
this.syncCompleteCallbacks.clear();
|
|
686
754
|
this.isInitialized = false;
|
|
687
755
|
}
|
|
688
756
|
};
|