@learncard/partner-connect 0.2.14 → 0.3.0
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 +96 -0
- package/dist/index.d.ts +421 -40
- package/dist/partner-connect.esm.js +326 -43
- package/dist/partner-connect.esm.js.map +1 -1
- package/dist/partner-connect.js +326 -42
- package/dist/partner-connect.js.map +1 -1
- package/package.json +6 -4
package/README.md
CHANGED
|
@@ -305,6 +305,102 @@ if (recipients.hasMore) {
|
|
|
305
305
|
|
|
306
306
|
**Returns:** `{ records: TemplateRecipientRecord[], hasMore: boolean, cursor?: string, total?: number }`
|
|
307
307
|
|
|
308
|
+
### `sendNotification(input)`
|
|
309
|
+
|
|
310
|
+
Send a notification to the current user from this app. The notification appears in the user's LearnCard notification inbox, even after they leave the app.
|
|
311
|
+
|
|
312
|
+
```typescript
|
|
313
|
+
await learnCard.sendNotification({
|
|
314
|
+
title: 'Sprint Bonus!',
|
|
315
|
+
body: '+10 coins from Sprint 42',
|
|
316
|
+
actionPath: '/',
|
|
317
|
+
category: 'reward',
|
|
318
|
+
});
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
**Parameters:**
|
|
322
|
+
|
|
323
|
+
- `title` _(optional)_: Notification title
|
|
324
|
+
- `body` _(optional)_: Notification body text
|
|
325
|
+
- `actionPath` _(optional)_: Deep link path within the app (e.g. `'/prizes'`). Must be an absolute pathname starting with `/`. This path is appended to the app's configured embed URL when the user taps the notification. For example, if your embed URL is `https://myapp.com` and `actionPath` is `'/challenges/42'`, the app will open at `https://myapp.com/challenges/42`. Hash routes (e.g. `'/#/page'`) are **not** supported — use pathname-based routing.
|
|
326
|
+
- `category` _(optional)_: Grouping category (e.g. `'reward'`, `'announcement'`, `'status'`)
|
|
327
|
+
- `priority` _(optional)_: `'normal'` (default) or `'high'`. Affects visual styling of the notification card and toast. Does not change delivery priority or ordering.
|
|
328
|
+
|
|
329
|
+
At least one of `title` or `body` is required.
|
|
330
|
+
|
|
331
|
+
**Returns:** `{ sent: boolean }`
|
|
332
|
+
|
|
333
|
+
> **Note:** This method sends a notification to the _current_ user (self-notification) via the `send-notification` app event. For server-to-server notifications to arbitrary users, use the `POST /app-store/listing/{listingId}/notify` brain-service route directly from your app backend.
|
|
334
|
+
|
|
335
|
+
---
|
|
336
|
+
|
|
337
|
+
### `incrementCounter(key, amount)`
|
|
338
|
+
|
|
339
|
+
Increment or decrement an app-scoped counter for the current user. Counters are scoped to (user, app, key). If the counter does not exist, it is created with the given amount as its initial value.
|
|
340
|
+
|
|
341
|
+
```typescript
|
|
342
|
+
// Add 10 coins
|
|
343
|
+
const result = await learnCard.incrementCounter('coins', 10);
|
|
344
|
+
console.log(result.newValue); // 10
|
|
345
|
+
|
|
346
|
+
// Spend 5 coins
|
|
347
|
+
const spent = await learnCard.incrementCounter('coins', -5);
|
|
348
|
+
console.log(spent.newValue); // 5
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
**Parameters:**
|
|
352
|
+
|
|
353
|
+
- `key` _(required)_: Counter name. Must match `[a-zA-Z0-9_-]+`, max 64 characters.
|
|
354
|
+
- `amount` _(required)_: Integer value to add. Use a negative integer to decrement.
|
|
355
|
+
|
|
356
|
+
**Returns:** `{ key: string, previousValue: number, newValue: number }`
|
|
357
|
+
|
|
358
|
+
**Limits:**
|
|
359
|
+
|
|
360
|
+
- Max 50 distinct counter keys per user per app
|
|
361
|
+
- Max 100 writes per user per app per minute
|
|
362
|
+
- Amount must be a finite integer
|
|
363
|
+
|
|
364
|
+
---
|
|
365
|
+
|
|
366
|
+
### `getCounter(key)`
|
|
367
|
+
|
|
368
|
+
Read the current value of an app-scoped counter. Returns `{ value: 0 }` if the counter does not exist.
|
|
369
|
+
|
|
370
|
+
```typescript
|
|
371
|
+
const { value } = await learnCard.getCounter('coins');
|
|
372
|
+
console.log('Balance:', value);
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
**Parameters:**
|
|
376
|
+
|
|
377
|
+
- `key` _(required)_: Counter name (same format as `incrementCounter`)
|
|
378
|
+
|
|
379
|
+
**Returns:** `{ key: string, value: number, updatedAt: string | null }`
|
|
380
|
+
|
|
381
|
+
---
|
|
382
|
+
|
|
383
|
+
### `getCounters(keys?)`
|
|
384
|
+
|
|
385
|
+
Read multiple app-scoped counters at once. If `keys` is omitted, returns all counters for this app.
|
|
386
|
+
|
|
387
|
+
```typescript
|
|
388
|
+
// Specific keys
|
|
389
|
+
const { counters } = await learnCard.getCounters(['coins', 'spins', 'streak']);
|
|
390
|
+
counters.forEach(c => console.log(c.key, c.value));
|
|
391
|
+
|
|
392
|
+
// All counters
|
|
393
|
+
const all = await learnCard.getCounters();
|
|
394
|
+
```
|
|
395
|
+
|
|
396
|
+
**Parameters:**
|
|
397
|
+
|
|
398
|
+
- `keys` _(optional)_: Array of counter names to fetch (max 50). Omit to return all.
|
|
399
|
+
|
|
400
|
+
**Returns:** `{ counters: Array<{ key: string, value: number, updatedAt: string | null }> }`
|
|
401
|
+
|
|
402
|
+
---
|
|
403
|
+
|
|
308
404
|
### `launchFeature(featurePath, initialPrompt?)`
|
|
309
405
|
|
|
310
406
|
Launch a feature in the LearnCard host application.
|
package/dist/index.d.ts
CHANGED
|
@@ -10,44 +10,73 @@ export { AppEvent, AppEventResponse, CheckCredentialEvent, SendCredentialEvent }
|
|
|
10
10
|
*/
|
|
11
11
|
interface PartnerConnectOptions {
|
|
12
12
|
/**
|
|
13
|
-
* The origin(s) of the LearnCard host
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
* **
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
13
|
+
* The origin(s) of the LearnCard host.
|
|
14
|
+
*
|
|
15
|
+
* Each entry may be either an **exact origin** (`https://learncard.app`) or a
|
|
16
|
+
* **wildcard pattern** where `*` stands in for a single DNS label portion in
|
|
17
|
+
* the host portion of the origin (e.g. `https://*.learncard.app`,
|
|
18
|
+
* `https://*.vetpass.app`). Wildcards are **only** allowed in the host and
|
|
19
|
+
* only as label(s); the protocol and port must always match exactly.
|
|
20
|
+
*
|
|
21
|
+
* Wildcard patterns match any non-empty chain of labels. So
|
|
22
|
+
* `https://*.learncard.app` matches both `https://staging.learncard.app` and
|
|
23
|
+
* `https://pr-123.preview.learncard.app`, but **not** `https://learncard.app`
|
|
24
|
+
* itself (include the bare origin explicitly if you need it) and **not**
|
|
25
|
+
* `https://evil.learncard.app.attacker.com` (the suffix must match).
|
|
26
|
+
*
|
|
27
|
+
* **Origin Configuration Hierarchy at runtime:**
|
|
28
|
+
* 1. `window.location.ancestorOrigins[0]` (when available) — the real parent
|
|
29
|
+
* origin as reported by the browser, validated against the effective
|
|
30
|
+
* whitelist. This source cannot be spoofed by a malicious query param.
|
|
31
|
+
* 2. `?lc_host_override=<origin>` query parameter — validated against the
|
|
32
|
+
* whitelist. Used by the LearnCard host to tell the SDK which origin it
|
|
33
|
+
* is loading from.
|
|
34
|
+
* 3. `sessionStorage['lc_host_override']` — a previously-validated override
|
|
35
|
+
* persisted across in-iframe navigation.
|
|
36
|
+
* 4. First value in the configured `hostOrigin` array / single string.
|
|
37
|
+
* 5. `PartnerConnect.DEFAULT_HOST_ORIGIN` (`https://learncard.app`).
|
|
38
|
+
*
|
|
39
|
+
* The partner app's configured whitelist is combined with a small built-in
|
|
40
|
+
* list of LearnCard tenant domains (see `disableDefaultTenants` to opt out).
|
|
41
|
+
* This lets a partner app work out-of-the-box inside any current or future
|
|
42
|
+
* `*.learncard.app`, `*.learncard.ai`, or `*.vetpass.app` tenant without a
|
|
43
|
+
* re-deploy.
|
|
30
44
|
*
|
|
31
45
|
* **Examples:**
|
|
32
46
|
*
|
|
33
|
-
* Single origin (production):
|
|
47
|
+
* Single origin (production only):
|
|
34
48
|
* ```typescript
|
|
35
49
|
* hostOrigin: 'https://learncard.app'
|
|
36
|
-
* // Uses: https://learncard.app
|
|
37
|
-
* // Override: ?lc_host_override=https://staging.learncard.app (not validated)
|
|
38
50
|
* ```
|
|
39
51
|
*
|
|
40
|
-
*
|
|
52
|
+
* Wildcard whitelist (covers staging + preview):
|
|
53
|
+
* ```typescript
|
|
54
|
+
* hostOrigin: ['https://learncard.app', 'https://*.learncard.app']
|
|
55
|
+
* ```
|
|
56
|
+
*
|
|
57
|
+
* Custom tenant alongside the built-in LearnCard defaults:
|
|
41
58
|
* ```typescript
|
|
42
|
-
* hostOrigin: ['https://
|
|
43
|
-
* //
|
|
44
|
-
* //
|
|
45
|
-
* // Invalid: ?lc_host_override=https://evil.com (rejected)
|
|
59
|
+
* hostOrigin: ['https://partner.example.com']
|
|
60
|
+
* // learncard.app / *.learncard.app / *.learncard.ai / vetpass.app / *.vetpass.app
|
|
61
|
+
* // are ALSO trusted because disableDefaultTenants is false.
|
|
46
62
|
* ```
|
|
47
63
|
*
|
|
48
64
|
* @default 'https://learncard.app'
|
|
49
65
|
*/
|
|
50
66
|
hostOrigin?: string | string[];
|
|
67
|
+
/**
|
|
68
|
+
* Opt out of the built-in LearnCard tenant whitelist.
|
|
69
|
+
*
|
|
70
|
+
* By default, the SDK merges `hostOrigin` with a curated list of LearnCard
|
|
71
|
+
* and tenant domains (see `PartnerConnect.DEFAULT_TRUSTED_TENANTS`) so that
|
|
72
|
+
* partner apps work on any LearnCard-managed tenant without reconfiguration.
|
|
73
|
+
*
|
|
74
|
+
* Set to `true` if you want the partner app to **only** trust the origins
|
|
75
|
+
* you pass in `hostOrigin`.
|
|
76
|
+
*
|
|
77
|
+
* @default false
|
|
78
|
+
*/
|
|
79
|
+
disableDefaultTenants?: boolean;
|
|
51
80
|
/**
|
|
52
81
|
* Whether to allow native app origins (default: true)
|
|
53
82
|
*
|
|
@@ -158,6 +187,17 @@ interface RequestConsentOptions {
|
|
|
158
187
|
*/
|
|
159
188
|
redirect?: boolean;
|
|
160
189
|
}
|
|
190
|
+
/**
|
|
191
|
+
* Payload for REQUEST_CONSENT action
|
|
192
|
+
*/
|
|
193
|
+
interface RequestConsentPayload {
|
|
194
|
+
/**
|
|
195
|
+
* URI of the consent contract. If not provided, the system will attempt
|
|
196
|
+
* to use the contract configured for the current app listing.
|
|
197
|
+
*/
|
|
198
|
+
contractUri?: string;
|
|
199
|
+
redirect?: boolean;
|
|
200
|
+
}
|
|
161
201
|
/**
|
|
162
202
|
* Response from REQUEST_CONSENT action
|
|
163
203
|
*/
|
|
@@ -242,17 +282,236 @@ interface TemplateRecipientsResponse {
|
|
|
242
282
|
cursor?: string;
|
|
243
283
|
total?: number;
|
|
244
284
|
}
|
|
285
|
+
/**
|
|
286
|
+
* Options for REQUEST_LEARNER_CONTEXT action
|
|
287
|
+
*/
|
|
288
|
+
interface RequestLearnerContextOptions {
|
|
289
|
+
/**
|
|
290
|
+
* Whether to include credentials in the context
|
|
291
|
+
* @default true
|
|
292
|
+
*/
|
|
293
|
+
includeCredentials?: boolean;
|
|
294
|
+
/**
|
|
295
|
+
* Whether to include personal data (name, bio, etc.) in the context
|
|
296
|
+
* @default false
|
|
297
|
+
*/
|
|
298
|
+
includePersonalData?: boolean;
|
|
299
|
+
/**
|
|
300
|
+
* Format of the response
|
|
301
|
+
* - 'prompt': Returns LLM-ready formatted text
|
|
302
|
+
* - 'structured': Returns structured data object
|
|
303
|
+
* @default 'prompt'
|
|
304
|
+
*/
|
|
305
|
+
format?: 'prompt' | 'structured';
|
|
306
|
+
/**
|
|
307
|
+
* Optional instructions to guide the LLM prompt generation
|
|
308
|
+
*/
|
|
309
|
+
instructions?: string;
|
|
310
|
+
/**
|
|
311
|
+
* Level of detail in the generated prompt
|
|
312
|
+
* @default 'compact'
|
|
313
|
+
*/
|
|
314
|
+
detailLevel?: 'compact' | 'expanded';
|
|
315
|
+
}
|
|
316
|
+
/**
|
|
317
|
+
* Raw data included in learner context response (when format is 'structured')
|
|
318
|
+
*/
|
|
319
|
+
interface LearnerContextRawData {
|
|
320
|
+
/** Array of Verifiable Credentials */
|
|
321
|
+
credentials: unknown[];
|
|
322
|
+
/** Personal data if requested and available (name, bio, etc.) */
|
|
323
|
+
personalData?: Record<string, unknown>;
|
|
324
|
+
}
|
|
325
|
+
/**
|
|
326
|
+
* Response from REQUEST_LEARNER_CONTEXT action
|
|
327
|
+
*/
|
|
328
|
+
interface LearnerContextResponse {
|
|
329
|
+
/** LLM-ready formatted prompt text */
|
|
330
|
+
prompt: string;
|
|
331
|
+
/** Raw structured data (only included when format is 'structured') */
|
|
332
|
+
raw?: LearnerContextRawData;
|
|
333
|
+
/** User's DID */
|
|
334
|
+
did: string;
|
|
335
|
+
/** User's display name if available */
|
|
336
|
+
displayName?: string;
|
|
337
|
+
}
|
|
338
|
+
/**
|
|
339
|
+
* Keywords for next steps in summary credential data
|
|
340
|
+
*/
|
|
341
|
+
interface SummaryCredentialKeyword {
|
|
342
|
+
occupations: string[] | null;
|
|
343
|
+
careers: string[] | null;
|
|
344
|
+
jobs: string[] | null;
|
|
345
|
+
skills: string[] | null;
|
|
346
|
+
fieldOfStudy: string | null;
|
|
347
|
+
}
|
|
348
|
+
/**
|
|
349
|
+
* Skill item in summary credential data
|
|
350
|
+
*/
|
|
351
|
+
interface SummaryCredentialSkill {
|
|
352
|
+
title: string;
|
|
353
|
+
description: string;
|
|
354
|
+
}
|
|
355
|
+
/**
|
|
356
|
+
* Next step item in summary credential data
|
|
357
|
+
*
|
|
358
|
+
* `keywords` is optional. Omit it entirely (or pass `undefined`) when you do not
|
|
359
|
+
* have taxonomy data — you no longer need to pass a struct of `null` fields.
|
|
360
|
+
*/
|
|
361
|
+
interface SummaryCredentialNextStep {
|
|
362
|
+
title: string;
|
|
363
|
+
description: string;
|
|
364
|
+
keywords?: SummaryCredentialKeyword;
|
|
365
|
+
}
|
|
366
|
+
/**
|
|
367
|
+
* Reflection item in summary credential data
|
|
368
|
+
*/
|
|
369
|
+
interface SummaryCredentialReflection {
|
|
370
|
+
title: string;
|
|
371
|
+
description: string;
|
|
372
|
+
}
|
|
373
|
+
/**
|
|
374
|
+
* Summary data for an AI Session credential
|
|
375
|
+
* Contains structured information about what was learned
|
|
376
|
+
*/
|
|
377
|
+
interface SummaryCredentialData {
|
|
378
|
+
/** Short, concise title for the learning session or credential */
|
|
379
|
+
title: string;
|
|
380
|
+
/** Comprehensive summary of what happened during the session */
|
|
381
|
+
summary: string;
|
|
382
|
+
/** Bullet points of key knowledge gained */
|
|
383
|
+
learned: string[];
|
|
384
|
+
/** Categorized skills learned during the session */
|
|
385
|
+
skills: SummaryCredentialSkill[];
|
|
386
|
+
/** Recommended follow-up activities or learning modules */
|
|
387
|
+
nextSteps: SummaryCredentialNextStep[];
|
|
388
|
+
/** Reflections on the learning experience */
|
|
389
|
+
reflections: SummaryCredentialReflection[];
|
|
390
|
+
}
|
|
391
|
+
/**
|
|
392
|
+
* Input for creating and sending an AI Session credential
|
|
393
|
+
*/
|
|
394
|
+
interface SendAiSessionCredentialInput {
|
|
395
|
+
/** Title of this specific AI session */
|
|
396
|
+
sessionTitle: string;
|
|
397
|
+
/** Structured summary data about what was learned */
|
|
398
|
+
summaryData: SummaryCredentialData;
|
|
399
|
+
/** Optional metadata for the session */
|
|
400
|
+
metadata?: Record<string, unknown>;
|
|
401
|
+
}
|
|
402
|
+
/**
|
|
403
|
+
* Response from sending an AI Session credential
|
|
404
|
+
*/
|
|
405
|
+
interface SendAiSessionCredentialResponse {
|
|
406
|
+
/** URI of the AI Topic (parent) boost */
|
|
407
|
+
topicUri: string;
|
|
408
|
+
/** URI of the topic credential, if a new topic was created */
|
|
409
|
+
topicCredentialUri?: string;
|
|
410
|
+
/** URI of the created AI Session credential */
|
|
411
|
+
sessionCredentialUri: string;
|
|
412
|
+
/** URI of the session boost (child of topic) */
|
|
413
|
+
sessionBoostUri: string;
|
|
414
|
+
/** Whether a new topic was created (true) or existing was used (false) */
|
|
415
|
+
isNewTopic: boolean;
|
|
416
|
+
}
|
|
417
|
+
/**
|
|
418
|
+
* Input for sending a notification to the current user from this app.
|
|
419
|
+
* The notification appears in the user's LearnCard notification inbox.
|
|
420
|
+
*/
|
|
421
|
+
interface AppNotificationInput {
|
|
422
|
+
/** Notification title */
|
|
423
|
+
title?: string;
|
|
424
|
+
/** Notification body text */
|
|
425
|
+
body?: string;
|
|
426
|
+
/** Deep link path within the app (e.g. '/prizes') */
|
|
427
|
+
actionPath?: string;
|
|
428
|
+
/** Grouping category (e.g. 'reward', 'announcement', 'status') */
|
|
429
|
+
category?: string;
|
|
430
|
+
/** Notification priority */
|
|
431
|
+
priority?: 'normal' | 'high';
|
|
432
|
+
}
|
|
433
|
+
/**
|
|
434
|
+
* Response from sendNotification
|
|
435
|
+
*/
|
|
436
|
+
interface AppNotificationResponse {
|
|
437
|
+
sent: boolean;
|
|
438
|
+
}
|
|
439
|
+
/**
|
|
440
|
+
* Response from incrementCounter
|
|
441
|
+
*/
|
|
442
|
+
interface IncrementCounterResponse {
|
|
443
|
+
key: string;
|
|
444
|
+
previousValue: number;
|
|
445
|
+
newValue: number;
|
|
446
|
+
}
|
|
447
|
+
/**
|
|
448
|
+
* Response from getCounter
|
|
449
|
+
*/
|
|
450
|
+
interface GetCounterResponse {
|
|
451
|
+
key: string;
|
|
452
|
+
value: number;
|
|
453
|
+
updatedAt: string | null;
|
|
454
|
+
}
|
|
455
|
+
/**
|
|
456
|
+
* Response from getCounters
|
|
457
|
+
*/
|
|
458
|
+
interface GetCountersResponse {
|
|
459
|
+
counters: GetCounterResponse[];
|
|
460
|
+
}
|
|
245
461
|
/**
|
|
246
462
|
* Error codes that can be returned by the LearnCard host
|
|
247
463
|
*/
|
|
248
464
|
type ErrorCode = 'LC_TIMEOUT' | 'LC_UNAUTHENTICATED' | 'CREDENTIAL_NOT_FOUND' | 'USER_REJECTED' | 'UNAUTHORIZED' | 'TEMPLATE_NOT_FOUND' | 'BOOST_NOT_FOUND' | 'INSUFFICIENT_PERMISSIONS' | string;
|
|
249
465
|
/**
|
|
250
|
-
* Error object returned when a request fails
|
|
466
|
+
* Error object returned when a request fails.
|
|
467
|
+
*
|
|
468
|
+
* Historically the SDK rejected with a plain `{ code, message }` object. As of
|
|
469
|
+
* v0.3.0 we reject with a {@link PartnerConnectError} instance instead, which
|
|
470
|
+
* still satisfies this interface (it has both `code` and `message` fields), so
|
|
471
|
+
* existing consumers that do `if (err.code === '...')` continue to work
|
|
472
|
+
* unchanged.
|
|
251
473
|
*/
|
|
252
474
|
interface LearnCardError {
|
|
253
475
|
code: ErrorCode;
|
|
254
476
|
message: string;
|
|
255
477
|
}
|
|
478
|
+
/**
|
|
479
|
+
* Typed error class for all Partner Connect SDK rejections.
|
|
480
|
+
*
|
|
481
|
+
* Use `instanceof PartnerConnectError` to narrow caught errors and unlock
|
|
482
|
+
* exhaustive `switch` checks on `code`. Both `code` and `message` are present
|
|
483
|
+
* (so the legacy `LearnCardError` object shape is preserved), and `name` is
|
|
484
|
+
* always `'PartnerConnectError'`.
|
|
485
|
+
*
|
|
486
|
+
* @example
|
|
487
|
+
* ```typescript
|
|
488
|
+
* try {
|
|
489
|
+
* await learnCard.requestLearnerContext();
|
|
490
|
+
* } catch (err) {
|
|
491
|
+
* if (err instanceof PartnerConnectError) {
|
|
492
|
+
* switch (err.code) {
|
|
493
|
+
* case 'LC_UNAUTHENTICATED': showLogin(); break;
|
|
494
|
+
* case 'USER_REJECTED': showPrivacyNotice(); break;
|
|
495
|
+
* case 'UNAUTHORIZED': showPermissionsError(); break;
|
|
496
|
+
* default: console.error(err);
|
|
497
|
+
* }
|
|
498
|
+
* }
|
|
499
|
+
* }
|
|
500
|
+
* ```
|
|
501
|
+
*/
|
|
502
|
+
declare class PartnerConnectError extends Error implements LearnCardError {
|
|
503
|
+
readonly code: ErrorCode;
|
|
504
|
+
constructor(code: ErrorCode, message: string);
|
|
505
|
+
/**
|
|
506
|
+
* Wrap any incoming `LearnCardError`-shaped value into a `PartnerConnectError`.
|
|
507
|
+
* Returns the value unchanged if it is already an instance.
|
|
508
|
+
*
|
|
509
|
+
* Used internally at every reject site so callers always receive a typed
|
|
510
|
+
* `PartnerConnectError`, regardless of whether the failure originated from
|
|
511
|
+
* the host (over postMessage), an SDK timeout, or `destroy()`.
|
|
512
|
+
*/
|
|
513
|
+
static from(input: LearnCardError | unknown): PartnerConnectError;
|
|
514
|
+
}
|
|
256
515
|
/**
|
|
257
516
|
* Internal message structure sent via postMessage
|
|
258
517
|
*/
|
|
@@ -307,6 +566,19 @@ interface PendingRequest {
|
|
|
307
566
|
declare class PartnerConnect {
|
|
308
567
|
/** Default host origin (security anchor) */
|
|
309
568
|
static readonly DEFAULT_HOST_ORIGIN = "https://learncard.app";
|
|
569
|
+
/**
|
|
570
|
+
* Built-in list of LearnCard-managed tenant origins.
|
|
571
|
+
*
|
|
572
|
+
* These are merged with the partner app's configured `hostOrigin` whitelist
|
|
573
|
+
* unless `disableDefaultTenants: true` is passed. This lets a partner app
|
|
574
|
+
* run inside any current or future LearnCard tenant (staging, preview,
|
|
575
|
+
* VetPass, etc.) without needing a re-deploy each time a new tenant is
|
|
576
|
+
* onboarded.
|
|
577
|
+
*
|
|
578
|
+
* Patterns follow the same rules as user-supplied `hostOrigin` entries:
|
|
579
|
+
* `*` is a wildcard for one or more DNS labels in the host portion.
|
|
580
|
+
*/
|
|
581
|
+
static readonly DEFAULT_TRUSTED_TENANTS: readonly string[];
|
|
310
582
|
private hostOrigins;
|
|
311
583
|
private activeHostOrigin;
|
|
312
584
|
private allowNativeAppOrigins;
|
|
@@ -318,16 +590,60 @@ declare class PartnerConnect {
|
|
|
318
590
|
constructor(options?: PartnerConnectOptions);
|
|
319
591
|
/**
|
|
320
592
|
* Configure the active host origin using the following hierarchy:
|
|
321
|
-
* 1.
|
|
322
|
-
*
|
|
323
|
-
*
|
|
324
|
-
*
|
|
325
|
-
*
|
|
593
|
+
* 1. `window.location.ancestorOrigins[0]` (when supported) — the browser's
|
|
594
|
+
* view of who our parent frame is. Cannot be forged by a malicious
|
|
595
|
+
* `lc_host_override` query param and therefore takes precedence.
|
|
596
|
+
* 2. `?lc_host_override=<origin>` query param (for staging / cross-tenant).
|
|
597
|
+
* 3. `sessionStorage` value saved from a previously-validated override.
|
|
598
|
+
* 4. First configured origin.
|
|
599
|
+
* 5. `DEFAULT_HOST_ORIGIN`.
|
|
600
|
+
*
|
|
601
|
+
* When a valid override is found in the query parameter, it is persisted
|
|
602
|
+
* to sessionStorage so subsequent in-iframe navigations in the same tab
|
|
603
|
+
* continue to use the same active origin.
|
|
604
|
+
*/
|
|
605
|
+
private static readonly SESSION_STORAGE_KEY;
|
|
606
|
+
/**
|
|
607
|
+
* Read `window.location.ancestorOrigins[0]` without throwing if the
|
|
608
|
+
* property is unavailable (Firefox) or the list is empty (top-level
|
|
609
|
+
* context, e.g. running outside of an iframe).
|
|
326
610
|
*/
|
|
611
|
+
private readAncestorOrigin;
|
|
327
612
|
private configureActiveOrigin;
|
|
613
|
+
private persistOverride;
|
|
328
614
|
private isOriginNativeApp;
|
|
329
615
|
/**
|
|
330
|
-
*
|
|
616
|
+
* Internal placeholder substituted in for `*` so that `new URL(...)` can
|
|
617
|
+
* parse a wildcard pattern. Chosen to be a syntactically-valid DNS label
|
|
618
|
+
* that cannot collide with a real hostname.
|
|
619
|
+
*/
|
|
620
|
+
private static readonly WILDCARD_PLACEHOLDER;
|
|
621
|
+
/** `*` (any number of occurrences) for replacement in the pattern. */
|
|
622
|
+
private static readonly WILDCARD_REGEX;
|
|
623
|
+
/** The required leading-label form a wildcard pattern must take. */
|
|
624
|
+
private static readonly WILDCARD_LEADING_PREFIX;
|
|
625
|
+
/**
|
|
626
|
+
* Check whether a candidate origin matches a configured whitelist entry.
|
|
627
|
+
*
|
|
628
|
+
* Supports exact matches and wildcard patterns. A wildcard entry has the
|
|
629
|
+
* form `<protocol>://*.<domain>` and matches any origin with the same
|
|
630
|
+
* protocol, same port, and a host ending in `.<domain>` with at least
|
|
631
|
+
* one non-empty DNS label in place of the `*`.
|
|
632
|
+
*
|
|
633
|
+
* Examples with pattern `https://*.learncard.app`:
|
|
634
|
+
* - `https://staging.learncard.app` → match
|
|
635
|
+
* - `https://pr-1.preview.learncard.app` → match
|
|
636
|
+
* - `https://learncard.app` → no match (no subdomain)
|
|
637
|
+
* - `http://staging.learncard.app` → no match (protocol mismatch)
|
|
638
|
+
* - `https://learncard.app.attacker.com` → no match (suffix mismatch)
|
|
639
|
+
*
|
|
640
|
+
* Exposed as a public static so it can be unit-tested directly without
|
|
641
|
+
* standing up a full SDK instance.
|
|
642
|
+
*/
|
|
643
|
+
static matchesOriginPattern(candidate: string, pattern: string): boolean;
|
|
644
|
+
/**
|
|
645
|
+
* Check if an origin is in the effective whitelist (exact origins +
|
|
646
|
+
* wildcard patterns + optional native-app origins).
|
|
331
647
|
*/
|
|
332
648
|
private isOriginInWhitelist;
|
|
333
649
|
/**
|
|
@@ -513,25 +829,30 @@ declare class PartnerConnect {
|
|
|
513
829
|
/**
|
|
514
830
|
* Request user consent for permissions
|
|
515
831
|
*
|
|
516
|
-
* @param contractUri - URI of the consent contract
|
|
832
|
+
* @param contractUri - URI of the consent contract (optional for App Store apps with configured contracts)
|
|
833
|
+
* @param options - Additional options including redirect behavior
|
|
517
834
|
* @returns Promise resolving to consent response
|
|
518
835
|
*
|
|
519
836
|
* @example
|
|
520
837
|
* ```typescript
|
|
521
|
-
* //
|
|
838
|
+
* // With explicit contract URI (for external/non-app store integrations)
|
|
522
839
|
* const response = await learnCard.requestConsent('lc:network:network.learncard.com/trpc:contract:abc123');
|
|
523
840
|
* if (response.granted) {
|
|
524
841
|
* console.log('User granted consent');
|
|
525
|
-
*
|
|
526
|
-
*
|
|
527
|
-
*
|
|
842
|
+
* }
|
|
843
|
+
*
|
|
844
|
+
* // Without contract URI (uses app's configured contract from integration)
|
|
845
|
+
* // This works for App Store apps that have configured a contract in their integration
|
|
846
|
+
* const response = await learnCard.requestConsent();
|
|
847
|
+
* if (response.granted) {
|
|
848
|
+
* console.log('User granted consent using listing contract');
|
|
528
849
|
* }
|
|
529
850
|
*
|
|
530
851
|
* // With redirect - redirects to contract's redirectUrl with VP in URL params
|
|
531
|
-
* const response = await learnCard.requestConsent(
|
|
852
|
+
* const response = await learnCard.requestConsent(undefined, { redirect: true });
|
|
532
853
|
* ```
|
|
533
854
|
*/
|
|
534
|
-
requestConsent(contractUri
|
|
855
|
+
requestConsent(contractUri?: string, options?: RequestConsentOptions): Promise<ConsentResponse>;
|
|
535
856
|
/**
|
|
536
857
|
* Initiate a template-based credential issuance flow
|
|
537
858
|
*
|
|
@@ -552,6 +873,36 @@ declare class PartnerConnect {
|
|
|
552
873
|
* ```
|
|
553
874
|
*/
|
|
554
875
|
initiateTemplateIssue(templateId: string, draftRecipients?: string[]): Promise<TemplateIssueResponse>;
|
|
876
|
+
/**
|
|
877
|
+
* Request comprehensive learner context for AI tutoring systems.
|
|
878
|
+
*
|
|
879
|
+
* This method retrieves the user's credentials and personal data,
|
|
880
|
+
* then formats them into an LLM-ready prompt that can be injected directly into
|
|
881
|
+
* an AI system prompt.
|
|
882
|
+
*
|
|
883
|
+
* @param options - Configuration options for what data to include and how to format it
|
|
884
|
+
* @returns Promise resolving to learner context with prompt and optional raw data
|
|
885
|
+
*
|
|
886
|
+
* @example
|
|
887
|
+
* ```typescript
|
|
888
|
+
* // Get LLM-ready prompt with credentials and personal data
|
|
889
|
+
* const context = await learnCard.requestLearnerContext({
|
|
890
|
+
* includeCredentials: true,
|
|
891
|
+
* includePersonalData: true,
|
|
892
|
+
* format: 'prompt',
|
|
893
|
+
* instructions: 'Focus on technical skills and certifications',
|
|
894
|
+
* detailLevel: 'expanded'
|
|
895
|
+
* });
|
|
896
|
+
*
|
|
897
|
+
* // Use in AI system prompt
|
|
898
|
+
* const systemPrompt = `You are a helpful tutor. ${context.prompt}`;
|
|
899
|
+
*
|
|
900
|
+
* // Access structured data if needed
|
|
901
|
+
* console.log('User DID:', context.did);
|
|
902
|
+
* console.log('Credentials count:', context.raw?.credentials.length);
|
|
903
|
+
* ```
|
|
904
|
+
*/
|
|
905
|
+
requestLearnerContext(options?: RequestLearnerContextOptions): Promise<LearnerContextResponse>;
|
|
555
906
|
/**
|
|
556
907
|
* Send a generic event to be processed by the brain service on behalf of this app.
|
|
557
908
|
* This is used for backend-like operations such as issuing credentials.
|
|
@@ -574,6 +925,36 @@ declare class PartnerConnect {
|
|
|
574
925
|
* ```
|
|
575
926
|
*/
|
|
576
927
|
sendAppEvent<T = AppEventResponse>(event: AppEvent): Promise<T>;
|
|
928
|
+
/**
|
|
929
|
+
* Create and send an AI Session credential to the user.
|
|
930
|
+
*
|
|
931
|
+
* This method manages the AI Topic → AI Session hierarchy:
|
|
932
|
+
* - Ensures an AI Topic exists for this app (creates one if needed)
|
|
933
|
+
* - Creates a new AI Session as a child of the topic
|
|
934
|
+
* - The topic appears in the user's AI Sessions page with the app's name
|
|
935
|
+
* - All sessions from this app are organized under that topic
|
|
936
|
+
*
|
|
937
|
+
* @param input - Session details including title and optional metadata
|
|
938
|
+
* @returns Promise resolving to topic and session URIs
|
|
939
|
+
*/
|
|
940
|
+
sendAiSessionCredential(input: SendAiSessionCredentialInput): Promise<SendAiSessionCredentialResponse>;
|
|
941
|
+
/**
|
|
942
|
+
* Send a notification to the current user from this app.
|
|
943
|
+
* The notification appears in the user's LearnCard notification inbox.
|
|
944
|
+
*/
|
|
945
|
+
sendNotification(input: AppNotificationInput): Promise<AppNotificationResponse>;
|
|
946
|
+
/**
|
|
947
|
+
* Increment or decrement an app-scoped counter for the current user.
|
|
948
|
+
*/
|
|
949
|
+
incrementCounter(key: string, amount: number): Promise<IncrementCounterResponse>;
|
|
950
|
+
/**
|
|
951
|
+
* Read the current value of an app-scoped counter for the current user.
|
|
952
|
+
*/
|
|
953
|
+
getCounter(key: string): Promise<GetCounterResponse>;
|
|
954
|
+
/**
|
|
955
|
+
* Read multiple app-scoped counters at once for the current user.
|
|
956
|
+
*/
|
|
957
|
+
getCounters(keys?: string[]): Promise<GetCountersResponse>;
|
|
577
958
|
/**
|
|
578
959
|
* Clean up the SDK and remove event listeners
|
|
579
960
|
*/
|
|
@@ -596,4 +977,4 @@ declare class PartnerConnect {
|
|
|
596
977
|
*/
|
|
597
978
|
declare function createPartnerConnect(options?: PartnerConnectOptions): PartnerConnect;
|
|
598
979
|
|
|
599
|
-
export { CheckCredentialInput, CheckCredentialResponse, CheckIssuanceStatusInput, ConsentResponse, CredentialSearchResponse, CredentialSpecificResponse, ErrorCode, GetTemplateRecipientsInput, IdentityResponse, LearnCardError, PartnerConnect, PartnerConnectOptions, PendingRequest, PostMessageRequest, PostMessageResponse, RequestConsentOptions, SendCredentialResponse, TemplateCredentialInput, TemplateCredentialResponse, TemplateIssuanceStatusResponse, TemplateIssueResponse, TemplateRecipientRecord, TemplateRecipientsResponse, VPRQuery, VerifiablePresentationRequest, createPartnerConnect, createPartnerConnect as default };
|
|
980
|
+
export { AppNotificationInput, AppNotificationResponse, CheckCredentialInput, CheckCredentialResponse, CheckIssuanceStatusInput, ConsentResponse, CredentialSearchResponse, CredentialSpecificResponse, ErrorCode, GetCounterResponse, GetCountersResponse, GetTemplateRecipientsInput, IdentityResponse, IncrementCounterResponse, LearnCardError, LearnerContextRawData, LearnerContextResponse, PartnerConnect, PartnerConnectError, PartnerConnectOptions, PendingRequest, PostMessageRequest, PostMessageResponse, RequestConsentOptions, RequestConsentPayload, RequestLearnerContextOptions, SendAiSessionCredentialInput, SendAiSessionCredentialResponse, SendCredentialResponse, SummaryCredentialData, SummaryCredentialKeyword, SummaryCredentialNextStep, SummaryCredentialReflection, SummaryCredentialSkill, TemplateCredentialInput, TemplateCredentialResponse, TemplateIssuanceStatusResponse, TemplateIssueResponse, TemplateRecipientRecord, TemplateRecipientsResponse, VPRQuery, VerifiablePresentationRequest, createPartnerConnect, createPartnerConnect as default };
|