@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
|
@@ -1,3 +1,36 @@
|
|
|
1
|
+
var __defProp$1 = Object.defineProperty;
|
|
2
|
+
var __defNormalProp$1 = (obj, key, value) => key in obj ? __defProp$1(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
3
|
+
var __publicField$1 = (obj, key, value) => __defNormalProp$1(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
4
|
+
class PartnerConnectError extends Error {
|
|
5
|
+
constructor(code, message) {
|
|
6
|
+
super(message);
|
|
7
|
+
__publicField$1(this, "code");
|
|
8
|
+
this.name = "PartnerConnectError";
|
|
9
|
+
this.code = code;
|
|
10
|
+
Object.setPrototypeOf(this, PartnerConnectError.prototype);
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Wrap any incoming `LearnCardError`-shaped value into a `PartnerConnectError`.
|
|
14
|
+
* Returns the value unchanged if it is already an instance.
|
|
15
|
+
*
|
|
16
|
+
* Used internally at every reject site so callers always receive a typed
|
|
17
|
+
* `PartnerConnectError`, regardless of whether the failure originated from
|
|
18
|
+
* the host (over postMessage), an SDK timeout, or `destroy()`.
|
|
19
|
+
*/
|
|
20
|
+
static from(input) {
|
|
21
|
+
if (input instanceof PartnerConnectError) return input;
|
|
22
|
+
if (input && typeof input === "object" && "code" in input && typeof input.code === "string") {
|
|
23
|
+
const candidate = input;
|
|
24
|
+
const message = typeof candidate.message === "string" ? candidate.message : "Partner Connect request failed";
|
|
25
|
+
return new PartnerConnectError(candidate.code, message);
|
|
26
|
+
}
|
|
27
|
+
return new PartnerConnectError(
|
|
28
|
+
"UNKNOWN_ERROR",
|
|
29
|
+
input instanceof Error ? input.message : "An unknown error occurred"
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
1
34
|
var __defProp = Object.defineProperty;
|
|
2
35
|
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
3
36
|
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
@@ -11,62 +44,160 @@ const _PartnerConnect = class _PartnerConnect {
|
|
|
11
44
|
__publicField(this, "pendingRequests");
|
|
12
45
|
__publicField(this, "messageListener", null);
|
|
13
46
|
__publicField(this, "isInitialized", false);
|
|
14
|
-
var _a;
|
|
15
|
-
const hostOrigin = (options == null ? void 0 : options.hostOrigin)
|
|
16
|
-
|
|
47
|
+
var _a, _b;
|
|
48
|
+
const hostOrigin = (_a = options == null ? void 0 : options.hostOrigin) != null ? _a : _PartnerConnect.DEFAULT_HOST_ORIGIN;
|
|
49
|
+
const configured = Array.isArray(hostOrigin) ? hostOrigin : [hostOrigin];
|
|
50
|
+
const disableDefaults = (options == null ? void 0 : options.disableDefaultTenants) === true;
|
|
51
|
+
const merged = disableDefaults ? [...configured] : [...configured, ..._PartnerConnect.DEFAULT_TRUSTED_TENANTS];
|
|
52
|
+
this.hostOrigins = Array.from(new Set(merged));
|
|
17
53
|
this.protocol = (options == null ? void 0 : options.protocol) || "LEARNCARD_V1";
|
|
18
54
|
this.requestTimeout = (options == null ? void 0 : options.requestTimeout) || 3e4;
|
|
19
|
-
this.allowNativeAppOrigins = (
|
|
55
|
+
this.allowNativeAppOrigins = (_b = options == null ? void 0 : options.allowNativeAppOrigins) != null ? _b : true;
|
|
20
56
|
this.pendingRequests = /* @__PURE__ */ new Map();
|
|
21
57
|
this.configureActiveOrigin();
|
|
22
58
|
this.setupMessageListener();
|
|
23
59
|
}
|
|
24
60
|
/**
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
* 3. Fall back to DEFAULT_HOST_ORIGIN
|
|
29
|
-
*
|
|
30
|
-
* This origin will be used for all outgoing messages and incoming message validation.
|
|
61
|
+
* Read `window.location.ancestorOrigins[0]` without throwing if the
|
|
62
|
+
* property is unavailable (Firefox) or the list is empty (top-level
|
|
63
|
+
* context, e.g. running outside of an iframe).
|
|
31
64
|
*/
|
|
65
|
+
readAncestorOrigin() {
|
|
66
|
+
if (typeof window === "undefined") return null;
|
|
67
|
+
try {
|
|
68
|
+
const ancestors = window.location.ancestorOrigins;
|
|
69
|
+
if (ancestors && ancestors.length > 0) {
|
|
70
|
+
const parent = ancestors[0];
|
|
71
|
+
if (typeof parent === "string" && parent.length > 0) {
|
|
72
|
+
return parent;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
} catch {
|
|
76
|
+
}
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
32
79
|
configureActiveOrigin() {
|
|
33
80
|
if (typeof window === "undefined") {
|
|
34
81
|
this.activeHostOrigin = this.hostOrigins[0] || _PartnerConnect.DEFAULT_HOST_ORIGIN;
|
|
35
82
|
return;
|
|
36
83
|
}
|
|
37
84
|
try {
|
|
85
|
+
const ancestorOrigin = this.readAncestorOrigin();
|
|
38
86
|
const urlParams = new URLSearchParams(window.location.search);
|
|
39
87
|
const hostOverride = urlParams.get("lc_host_override");
|
|
40
|
-
if (
|
|
41
|
-
if (
|
|
88
|
+
if (ancestorOrigin && this.isOriginInWhitelist(ancestorOrigin)) {
|
|
89
|
+
if (hostOverride && hostOverride !== ancestorOrigin) {
|
|
42
90
|
console.warn(
|
|
43
|
-
"[LearnCard SDK] lc_host_override
|
|
44
|
-
hostOverride,
|
|
45
|
-
"Allowed:",
|
|
46
|
-
this.hostOrigins
|
|
91
|
+
"[LearnCard SDK] lc_host_override does not match the real parent origin; preferring parent.",
|
|
92
|
+
{ override: hostOverride, parent: ancestorOrigin }
|
|
47
93
|
);
|
|
48
|
-
|
|
49
|
-
|
|
94
|
+
}
|
|
95
|
+
this.activeHostOrigin = ancestorOrigin;
|
|
96
|
+
this.persistOverride(ancestorOrigin);
|
|
97
|
+
console.log("[LearnCard SDK] Using parent origin:", ancestorOrigin);
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
if (hostOverride) {
|
|
101
|
+
if (this.isOriginInWhitelist(hostOverride)) {
|
|
50
102
|
this.activeHostOrigin = hostOverride;
|
|
103
|
+
this.persistOverride(hostOverride);
|
|
51
104
|
console.log("[LearnCard SDK] Using lc_host_override:", hostOverride);
|
|
105
|
+
return;
|
|
52
106
|
}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
107
|
+
console.warn(
|
|
108
|
+
"[LearnCard SDK] lc_host_override value is not in the configured whitelist:",
|
|
109
|
+
hostOverride,
|
|
110
|
+
"Allowed:",
|
|
111
|
+
this.hostOrigins
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
let storedOverride = null;
|
|
115
|
+
try {
|
|
116
|
+
storedOverride = sessionStorage.getItem(_PartnerConnect.SESSION_STORAGE_KEY);
|
|
117
|
+
} catch {
|
|
56
118
|
}
|
|
119
|
+
if (storedOverride && this.isOriginInWhitelist(storedOverride)) {
|
|
120
|
+
this.activeHostOrigin = storedOverride;
|
|
121
|
+
console.log("[LearnCard SDK] Using stored lc_host_override:", storedOverride);
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
this.activeHostOrigin = this.hostOrigins[0] || _PartnerConnect.DEFAULT_HOST_ORIGIN;
|
|
125
|
+
console.log("[LearnCard SDK] Using configured origin:", this.activeHostOrigin);
|
|
57
126
|
} catch (error) {
|
|
58
127
|
console.error("[LearnCard SDK] Error configuring active origin:", error);
|
|
59
128
|
this.activeHostOrigin = this.hostOrigins[0] || _PartnerConnect.DEFAULT_HOST_ORIGIN;
|
|
60
129
|
}
|
|
61
130
|
}
|
|
131
|
+
persistOverride(origin) {
|
|
132
|
+
try {
|
|
133
|
+
sessionStorage.setItem(_PartnerConnect.SESSION_STORAGE_KEY, origin);
|
|
134
|
+
} catch {
|
|
135
|
+
}
|
|
136
|
+
}
|
|
62
137
|
isOriginNativeApp(origin) {
|
|
63
138
|
return origin.startsWith("capacitor://") || origin.startsWith("ionic://") || origin.startsWith("https://localhost") || origin.startsWith("http://localhost") || origin.startsWith("http://127.0.0.1");
|
|
64
139
|
}
|
|
65
140
|
/**
|
|
66
|
-
* Check
|
|
141
|
+
* Check whether a candidate origin matches a configured whitelist entry.
|
|
142
|
+
*
|
|
143
|
+
* Supports exact matches and wildcard patterns. A wildcard entry has the
|
|
144
|
+
* form `<protocol>://*.<domain>` and matches any origin with the same
|
|
145
|
+
* protocol, same port, and a host ending in `.<domain>` with at least
|
|
146
|
+
* one non-empty DNS label in place of the `*`.
|
|
147
|
+
*
|
|
148
|
+
* Examples with pattern `https://*.learncard.app`:
|
|
149
|
+
* - `https://staging.learncard.app` → match
|
|
150
|
+
* - `https://pr-1.preview.learncard.app` → match
|
|
151
|
+
* - `https://learncard.app` → no match (no subdomain)
|
|
152
|
+
* - `http://staging.learncard.app` → no match (protocol mismatch)
|
|
153
|
+
* - `https://learncard.app.attacker.com` → no match (suffix mismatch)
|
|
154
|
+
*
|
|
155
|
+
* Exposed as a public static so it can be unit-tested directly without
|
|
156
|
+
* standing up a full SDK instance.
|
|
157
|
+
*/
|
|
158
|
+
static matchesOriginPattern(candidate, pattern) {
|
|
159
|
+
if (candidate === pattern) return true;
|
|
160
|
+
if (!pattern.includes("*")) return false;
|
|
161
|
+
let patternUrl;
|
|
162
|
+
let candidateUrl;
|
|
163
|
+
try {
|
|
164
|
+
patternUrl = new URL(
|
|
165
|
+
pattern.replace(
|
|
166
|
+
_PartnerConnect.WILDCARD_REGEX,
|
|
167
|
+
_PartnerConnect.WILDCARD_PLACEHOLDER
|
|
168
|
+
)
|
|
169
|
+
);
|
|
170
|
+
candidateUrl = new URL(candidate);
|
|
171
|
+
} catch {
|
|
172
|
+
return false;
|
|
173
|
+
}
|
|
174
|
+
if (patternUrl.protocol !== candidateUrl.protocol) return false;
|
|
175
|
+
if (patternUrl.port !== candidateUrl.port) return false;
|
|
176
|
+
const patternHost = patternUrl.hostname;
|
|
177
|
+
const candidateHost = candidateUrl.hostname;
|
|
178
|
+
if (!patternHost.startsWith(_PartnerConnect.WILDCARD_LEADING_PREFIX)) return false;
|
|
179
|
+
const patternSuffix = patternHost.slice(_PartnerConnect.WILDCARD_LEADING_PREFIX.length);
|
|
180
|
+
if (patternSuffix.length === 0) return false;
|
|
181
|
+
if (patternSuffix.includes(_PartnerConnect.WILDCARD_PLACEHOLDER)) return false;
|
|
182
|
+
const required = "." + patternSuffix;
|
|
183
|
+
if (!candidateHost.endsWith(required)) return false;
|
|
184
|
+
const prefix = candidateHost.slice(0, candidateHost.length - required.length);
|
|
185
|
+
if (prefix.length === 0) return false;
|
|
186
|
+
if (prefix.startsWith(".") || prefix.endsWith(".")) return false;
|
|
187
|
+
if (prefix.split(".").some((label) => label.length === 0)) return false;
|
|
188
|
+
return true;
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Check if an origin is in the effective whitelist (exact origins +
|
|
192
|
+
* wildcard patterns + optional native-app origins).
|
|
67
193
|
*/
|
|
68
194
|
isOriginInWhitelist(origin) {
|
|
69
|
-
|
|
195
|
+
if (!origin) return false;
|
|
196
|
+
for (const entry of this.hostOrigins) {
|
|
197
|
+
if (_PartnerConnect.matchesOriginPattern(origin, entry)) return true;
|
|
198
|
+
}
|
|
199
|
+
if (this.allowNativeAppOrigins && this.isOriginNativeApp(origin)) return true;
|
|
200
|
+
return false;
|
|
70
201
|
}
|
|
71
202
|
/**
|
|
72
203
|
* Check if an event origin is valid against the active host origin
|
|
@@ -105,7 +236,12 @@ const _PartnerConnect = class _PartnerConnect {
|
|
|
105
236
|
pending.resolve(data.data);
|
|
106
237
|
} else if (data.type === "ERROR") {
|
|
107
238
|
pending.reject(
|
|
108
|
-
|
|
239
|
+
PartnerConnectError.from(
|
|
240
|
+
data.error || {
|
|
241
|
+
code: "UNKNOWN_ERROR",
|
|
242
|
+
message: "An unknown error occurred"
|
|
243
|
+
}
|
|
244
|
+
)
|
|
109
245
|
);
|
|
110
246
|
}
|
|
111
247
|
};
|
|
@@ -123,20 +259,21 @@ const _PartnerConnect = class _PartnerConnect {
|
|
|
123
259
|
*/
|
|
124
260
|
sendMessage(action, payload) {
|
|
125
261
|
if (!this.isInitialized) {
|
|
126
|
-
return Promise.reject(
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
});
|
|
262
|
+
return Promise.reject(
|
|
263
|
+
new PartnerConnectError("SDK_NOT_INITIALIZED", "SDK is not initialized")
|
|
264
|
+
);
|
|
130
265
|
}
|
|
131
266
|
return new Promise((resolve, reject) => {
|
|
132
267
|
const requestId = this.generateRequestId(action);
|
|
133
268
|
const timeoutId = setTimeout(() => {
|
|
134
269
|
if (this.pendingRequests.has(requestId)) {
|
|
135
270
|
this.pendingRequests.delete(requestId);
|
|
136
|
-
reject(
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
271
|
+
reject(
|
|
272
|
+
new PartnerConnectError(
|
|
273
|
+
"LC_TIMEOUT",
|
|
274
|
+
`Request ${action} timed out after ${this.requestTimeout}ms`
|
|
275
|
+
)
|
|
276
|
+
);
|
|
140
277
|
}
|
|
141
278
|
}, this.requestTimeout);
|
|
142
279
|
this.pendingRequests.set(requestId, {
|
|
@@ -354,22 +491,27 @@ const _PartnerConnect = class _PartnerConnect {
|
|
|
354
491
|
/**
|
|
355
492
|
* Request user consent for permissions
|
|
356
493
|
*
|
|
357
|
-
* @param contractUri - URI of the consent contract
|
|
494
|
+
* @param contractUri - URI of the consent contract (optional for App Store apps with configured contracts)
|
|
495
|
+
* @param options - Additional options including redirect behavior
|
|
358
496
|
* @returns Promise resolving to consent response
|
|
359
497
|
*
|
|
360
498
|
* @example
|
|
361
499
|
* ```typescript
|
|
362
|
-
* //
|
|
500
|
+
* // With explicit contract URI (for external/non-app store integrations)
|
|
363
501
|
* const response = await learnCard.requestConsent('lc:network:network.learncard.com/trpc:contract:abc123');
|
|
364
502
|
* if (response.granted) {
|
|
365
503
|
* console.log('User granted consent');
|
|
366
|
-
*
|
|
367
|
-
*
|
|
368
|
-
*
|
|
504
|
+
* }
|
|
505
|
+
*
|
|
506
|
+
* // Without contract URI (uses app's configured contract from integration)
|
|
507
|
+
* // This works for App Store apps that have configured a contract in their integration
|
|
508
|
+
* const response = await learnCard.requestConsent();
|
|
509
|
+
* if (response.granted) {
|
|
510
|
+
* console.log('User granted consent using listing contract');
|
|
369
511
|
* }
|
|
370
512
|
*
|
|
371
513
|
* // With redirect - redirects to contract's redirectUrl with VP in URL params
|
|
372
|
-
* const response = await learnCard.requestConsent(
|
|
514
|
+
* const response = await learnCard.requestConsent(undefined, { redirect: true });
|
|
373
515
|
* ```
|
|
374
516
|
*/
|
|
375
517
|
requestConsent(contractUri, options = {}) {
|
|
@@ -404,6 +546,45 @@ const _PartnerConnect = class _PartnerConnect {
|
|
|
404
546
|
draftRecipients: draftRecipients || []
|
|
405
547
|
});
|
|
406
548
|
}
|
|
549
|
+
/**
|
|
550
|
+
* Request comprehensive learner context for AI tutoring systems.
|
|
551
|
+
*
|
|
552
|
+
* This method retrieves the user's credentials and personal data,
|
|
553
|
+
* then formats them into an LLM-ready prompt that can be injected directly into
|
|
554
|
+
* an AI system prompt.
|
|
555
|
+
*
|
|
556
|
+
* @param options - Configuration options for what data to include and how to format it
|
|
557
|
+
* @returns Promise resolving to learner context with prompt and optional raw data
|
|
558
|
+
*
|
|
559
|
+
* @example
|
|
560
|
+
* ```typescript
|
|
561
|
+
* // Get LLM-ready prompt with credentials and personal data
|
|
562
|
+
* const context = await learnCard.requestLearnerContext({
|
|
563
|
+
* includeCredentials: true,
|
|
564
|
+
* includePersonalData: true,
|
|
565
|
+
* format: 'prompt',
|
|
566
|
+
* instructions: 'Focus on technical skills and certifications',
|
|
567
|
+
* detailLevel: 'expanded'
|
|
568
|
+
* });
|
|
569
|
+
*
|
|
570
|
+
* // Use in AI system prompt
|
|
571
|
+
* const systemPrompt = `You are a helpful tutor. ${context.prompt}`;
|
|
572
|
+
*
|
|
573
|
+
* // Access structured data if needed
|
|
574
|
+
* console.log('User DID:', context.did);
|
|
575
|
+
* console.log('Credentials count:', context.raw?.credentials.length);
|
|
576
|
+
* ```
|
|
577
|
+
*/
|
|
578
|
+
requestLearnerContext(options) {
|
|
579
|
+
var _a, _b, _c, _d;
|
|
580
|
+
return this.sendMessage("REQUEST_LEARNER_CONTEXT", {
|
|
581
|
+
includeCredentials: (_a = options == null ? void 0 : options.includeCredentials) != null ? _a : true,
|
|
582
|
+
includePersonalData: (_b = options == null ? void 0 : options.includePersonalData) != null ? _b : false,
|
|
583
|
+
format: (_c = options == null ? void 0 : options.format) != null ? _c : "prompt",
|
|
584
|
+
instructions: options == null ? void 0 : options.instructions,
|
|
585
|
+
detailLevel: (_d = options == null ? void 0 : options.detailLevel) != null ? _d : "compact"
|
|
586
|
+
});
|
|
587
|
+
}
|
|
407
588
|
/**
|
|
408
589
|
* Send a generic event to be processed by the brain service on behalf of this app.
|
|
409
590
|
* This is used for backend-like operations such as issuing credentials.
|
|
@@ -428,6 +609,62 @@ const _PartnerConnect = class _PartnerConnect {
|
|
|
428
609
|
sendAppEvent(event) {
|
|
429
610
|
return this.sendMessage("APP_EVENT", event);
|
|
430
611
|
}
|
|
612
|
+
/**
|
|
613
|
+
* Create and send an AI Session credential to the user.
|
|
614
|
+
*
|
|
615
|
+
* This method manages the AI Topic → AI Session hierarchy:
|
|
616
|
+
* - Ensures an AI Topic exists for this app (creates one if needed)
|
|
617
|
+
* - Creates a new AI Session as a child of the topic
|
|
618
|
+
* - The topic appears in the user's AI Sessions page with the app's name
|
|
619
|
+
* - All sessions from this app are organized under that topic
|
|
620
|
+
*
|
|
621
|
+
* @param input - Session details including title and optional metadata
|
|
622
|
+
* @returns Promise resolving to topic and session URIs
|
|
623
|
+
*/
|
|
624
|
+
sendAiSessionCredential(input) {
|
|
625
|
+
return this.sendAppEvent({
|
|
626
|
+
type: "send-ai-session-credential",
|
|
627
|
+
...input
|
|
628
|
+
});
|
|
629
|
+
}
|
|
630
|
+
/**
|
|
631
|
+
* Send a notification to the current user from this app.
|
|
632
|
+
* The notification appears in the user's LearnCard notification inbox.
|
|
633
|
+
*/
|
|
634
|
+
sendNotification(input) {
|
|
635
|
+
return this.sendAppEvent({
|
|
636
|
+
type: "send-notification",
|
|
637
|
+
...input
|
|
638
|
+
});
|
|
639
|
+
}
|
|
640
|
+
/**
|
|
641
|
+
* Increment or decrement an app-scoped counter for the current user.
|
|
642
|
+
*/
|
|
643
|
+
incrementCounter(key, amount) {
|
|
644
|
+
return this.sendAppEvent({
|
|
645
|
+
type: "increment-counter",
|
|
646
|
+
key,
|
|
647
|
+
amount
|
|
648
|
+
});
|
|
649
|
+
}
|
|
650
|
+
/**
|
|
651
|
+
* Read the current value of an app-scoped counter for the current user.
|
|
652
|
+
*/
|
|
653
|
+
getCounter(key) {
|
|
654
|
+
return this.sendAppEvent({
|
|
655
|
+
type: "get-counter",
|
|
656
|
+
key
|
|
657
|
+
});
|
|
658
|
+
}
|
|
659
|
+
/**
|
|
660
|
+
* Read multiple app-scoped counters at once for the current user.
|
|
661
|
+
*/
|
|
662
|
+
getCounters(keys) {
|
|
663
|
+
return this.sendAppEvent({
|
|
664
|
+
type: "get-counters",
|
|
665
|
+
...keys ? { keys } : {}
|
|
666
|
+
});
|
|
667
|
+
}
|
|
431
668
|
/**
|
|
432
669
|
* Clean up the SDK and remove event listeners
|
|
433
670
|
*/
|
|
@@ -438,10 +675,12 @@ const _PartnerConnect = class _PartnerConnect {
|
|
|
438
675
|
}
|
|
439
676
|
for (const [requestId, pending] of this.pendingRequests.entries()) {
|
|
440
677
|
clearTimeout(pending.timeoutId);
|
|
441
|
-
pending.reject(
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
678
|
+
pending.reject(
|
|
679
|
+
new PartnerConnectError(
|
|
680
|
+
"SDK_DESTROYED",
|
|
681
|
+
"SDK was destroyed before request completed"
|
|
682
|
+
)
|
|
683
|
+
);
|
|
445
684
|
}
|
|
446
685
|
this.pendingRequests.clear();
|
|
447
686
|
this.isInitialized = false;
|
|
@@ -449,10 +688,54 @@ const _PartnerConnect = class _PartnerConnect {
|
|
|
449
688
|
};
|
|
450
689
|
/** Default host origin (security anchor) */
|
|
451
690
|
__publicField(_PartnerConnect, "DEFAULT_HOST_ORIGIN", "https://learncard.app");
|
|
691
|
+
/**
|
|
692
|
+
* Built-in list of LearnCard-managed tenant origins.
|
|
693
|
+
*
|
|
694
|
+
* These are merged with the partner app's configured `hostOrigin` whitelist
|
|
695
|
+
* unless `disableDefaultTenants: true` is passed. This lets a partner app
|
|
696
|
+
* run inside any current or future LearnCard tenant (staging, preview,
|
|
697
|
+
* VetPass, etc.) without needing a re-deploy each time a new tenant is
|
|
698
|
+
* onboarded.
|
|
699
|
+
*
|
|
700
|
+
* Patterns follow the same rules as user-supplied `hostOrigin` entries:
|
|
701
|
+
* `*` is a wildcard for one or more DNS labels in the host portion.
|
|
702
|
+
*/
|
|
703
|
+
__publicField(_PartnerConnect, "DEFAULT_TRUSTED_TENANTS", [
|
|
704
|
+
"https://learncard.app",
|
|
705
|
+
"https://*.learncard.app",
|
|
706
|
+
"https://*.learncard.ai",
|
|
707
|
+
"https://vetpass.app",
|
|
708
|
+
"https://*.vetpass.app"
|
|
709
|
+
]);
|
|
710
|
+
/**
|
|
711
|
+
* Configure the active host origin using the following hierarchy:
|
|
712
|
+
* 1. `window.location.ancestorOrigins[0]` (when supported) — the browser's
|
|
713
|
+
* view of who our parent frame is. Cannot be forged by a malicious
|
|
714
|
+
* `lc_host_override` query param and therefore takes precedence.
|
|
715
|
+
* 2. `?lc_host_override=<origin>` query param (for staging / cross-tenant).
|
|
716
|
+
* 3. `sessionStorage` value saved from a previously-validated override.
|
|
717
|
+
* 4. First configured origin.
|
|
718
|
+
* 5. `DEFAULT_HOST_ORIGIN`.
|
|
719
|
+
*
|
|
720
|
+
* When a valid override is found in the query parameter, it is persisted
|
|
721
|
+
* to sessionStorage so subsequent in-iframe navigations in the same tab
|
|
722
|
+
* continue to use the same active origin.
|
|
723
|
+
*/
|
|
724
|
+
__publicField(_PartnerConnect, "SESSION_STORAGE_KEY", "lc_host_override");
|
|
725
|
+
/**
|
|
726
|
+
* Internal placeholder substituted in for `*` so that `new URL(...)` can
|
|
727
|
+
* parse a wildcard pattern. Chosen to be a syntactically-valid DNS label
|
|
728
|
+
* that cannot collide with a real hostname.
|
|
729
|
+
*/
|
|
730
|
+
__publicField(_PartnerConnect, "WILDCARD_PLACEHOLDER", "__lc_wildcard__");
|
|
731
|
+
/** `*` (any number of occurrences) for replacement in the pattern. */
|
|
732
|
+
__publicField(_PartnerConnect, "WILDCARD_REGEX", /\*/g);
|
|
733
|
+
/** The required leading-label form a wildcard pattern must take. */
|
|
734
|
+
__publicField(_PartnerConnect, "WILDCARD_LEADING_PREFIX", `${_PartnerConnect.WILDCARD_PLACEHOLDER}.`);
|
|
452
735
|
let PartnerConnect = _PartnerConnect;
|
|
453
736
|
function createPartnerConnect(options) {
|
|
454
737
|
return new PartnerConnect(options);
|
|
455
738
|
}
|
|
456
739
|
|
|
457
|
-
export { PartnerConnect, createPartnerConnect, createPartnerConnect as default };
|
|
740
|
+
export { PartnerConnect, PartnerConnectError, createPartnerConnect, createPartnerConnect as default };
|
|
458
741
|
//# sourceMappingURL=partner-connect.esm.js.map
|