@learncard/partner-connect 0.3.7 → 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
package/README.md
CHANGED
|
@@ -635,28 +635,28 @@ try {
|
|
|
635
635
|
---
|
|
636
636
|
// src/pages/index.astro
|
|
637
637
|
const config = {
|
|
638
|
-
|
|
638
|
+
learnCardHostOrigin: import.meta.env.PUBLIC_LEARNCARD_HOST || 'https://learncard.app',
|
|
639
639
|
};
|
|
640
640
|
---
|
|
641
641
|
|
|
642
642
|
<script>
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
643
|
+
import { createPartnerConnect } from '@learncard/partner-connect';
|
|
644
|
+
|
|
645
|
+
const config = window.__LC_CONFIG;
|
|
646
|
+
const learnCard = createPartnerConnect({
|
|
647
|
+
hostOrigin: config.learnCardHostOrigin,
|
|
648
|
+
});
|
|
649
|
+
|
|
650
|
+
async function init() {
|
|
651
|
+
try {
|
|
652
|
+
const identity = await learnCard.requestIdentity();
|
|
653
|
+
console.log('Logged in as:', identity.user.did);
|
|
654
|
+
} catch (error) {
|
|
655
|
+
console.error('Not authenticated:', error);
|
|
656
|
+
}
|
|
656
657
|
}
|
|
657
|
-
}
|
|
658
658
|
|
|
659
|
-
|
|
659
|
+
init();
|
|
660
660
|
</script>
|
|
661
661
|
```
|
|
662
662
|
|
|
@@ -736,6 +736,38 @@ import type {
|
|
|
736
736
|
} from '@learncard/partner-connect';
|
|
737
737
|
```
|
|
738
738
|
|
|
739
|
+
## Learner Context Sync Readiness
|
|
740
|
+
|
|
741
|
+
Apps that need a complete learner snapshot can ask LearnCard to wait for background data sync:
|
|
742
|
+
|
|
743
|
+
```typescript
|
|
744
|
+
const context = await learnCard.requestLearnerContext({
|
|
745
|
+
includeCredentials: true,
|
|
746
|
+
waitForSync: true,
|
|
747
|
+
format: 'structured',
|
|
748
|
+
});
|
|
749
|
+
|
|
750
|
+
if (context.status === 'syncing') {
|
|
751
|
+
const unsubscribe = learnCard.onSyncComplete(async () => {
|
|
752
|
+
const readyContext = await learnCard.requestLearnerContext({
|
|
753
|
+
includeCredentials: true,
|
|
754
|
+
waitForSync: true,
|
|
755
|
+
format: 'structured',
|
|
756
|
+
});
|
|
757
|
+
|
|
758
|
+
unsubscribe();
|
|
759
|
+
console.log(readyContext.raw?.credentials);
|
|
760
|
+
});
|
|
761
|
+
}
|
|
762
|
+
```
|
|
763
|
+
|
|
764
|
+
Use `learnCard.getSyncStatus()` to render your own progress UI:
|
|
765
|
+
|
|
766
|
+
```typescript
|
|
767
|
+
const syncStatus = await learnCard.getSyncStatus();
|
|
768
|
+
console.log(syncStatus.status, syncStatus.progress);
|
|
769
|
+
```
|
|
770
|
+
|
|
739
771
|
## License
|
|
740
772
|
|
|
741
773
|
MIT
|
package/dist/index.d.ts
CHANGED
|
@@ -312,6 +312,12 @@ interface RequestLearnerContextOptions {
|
|
|
312
312
|
* @default 'compact'
|
|
313
313
|
*/
|
|
314
314
|
detailLevel?: 'compact' | 'expanded';
|
|
315
|
+
/**
|
|
316
|
+
* Wait for LearnCard to finish background ConsentFlow data sync before returning context.
|
|
317
|
+
* Apps that need a complete learner snapshot should set this to true.
|
|
318
|
+
* @default false
|
|
319
|
+
*/
|
|
320
|
+
waitForSync?: boolean;
|
|
315
321
|
}
|
|
316
322
|
/**
|
|
317
323
|
* Raw data included in learner context response (when format is 'structured')
|
|
@@ -326,6 +332,10 @@ interface LearnerContextRawData {
|
|
|
326
332
|
* Response from REQUEST_LEARNER_CONTEXT action
|
|
327
333
|
*/
|
|
328
334
|
interface LearnerContextResponse {
|
|
335
|
+
/** Whether the response used immediately available data or waited for a complete sync */
|
|
336
|
+
status?: 'ready' | 'syncing';
|
|
337
|
+
/** Current sync progress when available */
|
|
338
|
+
progress?: SyncProgress;
|
|
329
339
|
/** LLM-ready formatted prompt text */
|
|
330
340
|
prompt: string;
|
|
331
341
|
/** Raw structured data (only included when format is 'structured') */
|
|
@@ -335,6 +345,18 @@ interface LearnerContextResponse {
|
|
|
335
345
|
/** User's display name if available */
|
|
336
346
|
displayName?: string;
|
|
337
347
|
}
|
|
348
|
+
interface SyncProgress {
|
|
349
|
+
totalCredentials: number;
|
|
350
|
+
completedCredentials: number;
|
|
351
|
+
failedCredentials: number;
|
|
352
|
+
retryCount: number;
|
|
353
|
+
}
|
|
354
|
+
interface SyncStatus {
|
|
355
|
+
status: 'ready' | 'syncing' | 'error';
|
|
356
|
+
progress: SyncProgress;
|
|
357
|
+
eta?: number;
|
|
358
|
+
lastError?: string;
|
|
359
|
+
}
|
|
338
360
|
/**
|
|
339
361
|
* Keywords for next steps in summary credential data
|
|
340
362
|
*/
|
|
@@ -587,6 +609,8 @@ declare class PartnerConnect {
|
|
|
587
609
|
private pendingRequests;
|
|
588
610
|
private messageListener;
|
|
589
611
|
private isInitialized;
|
|
612
|
+
private syncCompleteCallbacks;
|
|
613
|
+
private syncStatusPollId;
|
|
590
614
|
constructor(options?: PartnerConnectOptions);
|
|
591
615
|
/**
|
|
592
616
|
* Configure the active host origin using the following hierarchy:
|
|
@@ -889,11 +913,19 @@ declare class PartnerConnect {
|
|
|
889
913
|
* const context = await learnCard.requestLearnerContext({
|
|
890
914
|
* includeCredentials: true,
|
|
891
915
|
* includePersonalData: true,
|
|
916
|
+
* waitForSync: true,
|
|
892
917
|
* format: 'prompt',
|
|
893
918
|
* instructions: 'Focus on technical skills and certifications',
|
|
894
919
|
* detailLevel: 'expanded'
|
|
895
920
|
* });
|
|
896
921
|
*
|
|
922
|
+
* if (context.status === 'syncing') {
|
|
923
|
+
* const unsubscribe = learnCard.onSyncComplete(async () => {
|
|
924
|
+
* const readyContext = await learnCard.requestLearnerContext({ waitForSync: true });
|
|
925
|
+
* unsubscribe();
|
|
926
|
+
* });
|
|
927
|
+
* }
|
|
928
|
+
*
|
|
897
929
|
* // Use in AI system prompt
|
|
898
930
|
* const systemPrompt = `You are a helpful tutor. ${context.prompt}`;
|
|
899
931
|
*
|
|
@@ -903,6 +935,18 @@ declare class PartnerConnect {
|
|
|
903
935
|
* ```
|
|
904
936
|
*/
|
|
905
937
|
requestLearnerContext(options?: RequestLearnerContextOptions): Promise<LearnerContextResponse>;
|
|
938
|
+
/**
|
|
939
|
+
* Get the current LearnCard background data sync status.
|
|
940
|
+
*/
|
|
941
|
+
getSyncStatus(): Promise<SyncStatus>;
|
|
942
|
+
/**
|
|
943
|
+
* Register a callback that fires when LearnCard reports background sync has reached a
|
|
944
|
+
* terminal state ('ready' or 'error'). Check `status.status` to distinguish the two.
|
|
945
|
+
* Polling stops once a terminal state is reached, all callbacks unsubscribe, or the
|
|
946
|
+
* poll exceeds its maximum duration (reported to callbacks as an 'error' status).
|
|
947
|
+
* Returns an unsubscribe function.
|
|
948
|
+
*/
|
|
949
|
+
onSyncComplete(callback: (status: SyncStatus) => void): () => void;
|
|
906
950
|
/**
|
|
907
951
|
* Send a generic event to be processed by the brain service on behalf of this app.
|
|
908
952
|
* This is used for backend-like operations such as issuing credentials.
|
|
@@ -977,4 +1021,4 @@ declare class PartnerConnect {
|
|
|
977
1021
|
*/
|
|
978
1022
|
declare function createPartnerConnect(options?: PartnerConnectOptions): PartnerConnect;
|
|
979
1023
|
|
|
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 };
|
|
1024
|
+
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, SyncProgress, SyncStatus, TemplateCredentialInput, TemplateCredentialResponse, TemplateIssuanceStatusResponse, TemplateIssueResponse, TemplateRecipientRecord, TemplateRecipientsResponse, VPRQuery, VerifiablePresentationRequest, createPartnerConnect, createPartnerConnect as default };
|
|
@@ -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
|
};
|