@monterosa/sdk-identify-kit 2.0.0-rc.2 → 2.0.0-rc.4
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/dist/api.d.ts +187 -173
- package/dist/bridge.d.ts +12 -3
- package/dist/credentials_store.d.ts +31 -0
- package/dist/identify.d.ts +4 -10
- package/dist/identify_options.d.ts +28 -0
- package/dist/identify_url.d.ts +25 -0
- package/dist/identity_options.d.ts +31 -0
- package/dist/index.cjs +1224 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +444 -378
- package/dist/index.js.map +1 -1
- package/dist/login_policy_store.d.ts +28 -0
- package/dist/types.d.ts +16 -43
- package/package.json +11 -10
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,1224 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
+
|
|
5
|
+
var sdkUtil = require('@monterosa/sdk-util');
|
|
6
|
+
var sdkLauncherKit = require('@monterosa/sdk-launcher-kit');
|
|
7
|
+
var sdkCore = require('@monterosa/sdk-core');
|
|
8
|
+
var sdkConnectKit = require('@monterosa/sdk-connect-kit');
|
|
9
|
+
var sdkInteractInterop = require('@monterosa/sdk-interact-interop');
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* @license
|
|
13
|
+
* @monterosa/sdk-identify-kit
|
|
14
|
+
*
|
|
15
|
+
* Copyright © 2023-2026 Monterosa Productions Limited. All rights reserved.
|
|
16
|
+
*
|
|
17
|
+
* More details on the license can be found at https://www.monterosa.co/sdk/license
|
|
18
|
+
*/
|
|
19
|
+
/**
|
|
20
|
+
* @internal
|
|
21
|
+
*/
|
|
22
|
+
var IdentifyEvent;
|
|
23
|
+
(function (IdentifyEvent) {
|
|
24
|
+
IdentifyEvent["StateUpdated"] = "state_updated";
|
|
25
|
+
IdentifyEvent["LoginRequested"] = "login_requested";
|
|
26
|
+
IdentifyEvent["LogoutRequested"] = "logout_requested";
|
|
27
|
+
IdentifyEvent["CredentialsUpdated"] = "credentials_updated";
|
|
28
|
+
IdentifyEvent["LoginPolicyUpdated"] = "login_policy_updated";
|
|
29
|
+
})(IdentifyEvent || (IdentifyEvent = {}));
|
|
30
|
+
/**
|
|
31
|
+
* Defines a set of error codes that may be encountered when using the
|
|
32
|
+
* Identify kit of the Monterosa SDK.
|
|
33
|
+
*
|
|
34
|
+
* @example
|
|
35
|
+
* ```javascript
|
|
36
|
+
* try {
|
|
37
|
+
* // some code that uses the IdentifyKit
|
|
38
|
+
* } catch (err) {
|
|
39
|
+
* if (err.code === IdentifyError.NoCredentials) {
|
|
40
|
+
* // handle missing credentials error
|
|
41
|
+
* } else {
|
|
42
|
+
* // handle other error types
|
|
43
|
+
* }
|
|
44
|
+
* }
|
|
45
|
+
* ```
|
|
46
|
+
*
|
|
47
|
+
* @remarks
|
|
48
|
+
* - The `IdentifyError` enum provides a convenient way to handle errors
|
|
49
|
+
* encountered when using the `IdentifyKit` module. By checking the code
|
|
50
|
+
* property of the caught error against the values of the enum, the error
|
|
51
|
+
* type can be determined and appropriate action taken.
|
|
52
|
+
*
|
|
53
|
+
* - The `IdentifyError` enum is not intended to be instantiated or extended.
|
|
54
|
+
*/
|
|
55
|
+
exports.IdentifyError = void 0;
|
|
56
|
+
(function (IdentifyError) {
|
|
57
|
+
/**
|
|
58
|
+
* Indicates an error occurred during the call to the extension API.
|
|
59
|
+
*/
|
|
60
|
+
IdentifyError["ExtensionApiError"] = "extension_api_error";
|
|
61
|
+
/**
|
|
62
|
+
* Indicates the extension required by the IdentifyKit is not set up properly.
|
|
63
|
+
*/
|
|
64
|
+
IdentifyError["ExtensionNotSetup"] = "extension_not_setup";
|
|
65
|
+
/**
|
|
66
|
+
* Indicates the user's authentication credentials are not available
|
|
67
|
+
* or have expired.
|
|
68
|
+
*/
|
|
69
|
+
IdentifyError["NoCredentials"] = "no_credentials";
|
|
70
|
+
/**
|
|
71
|
+
* Indicates an error occurred in the parent app.
|
|
72
|
+
*/
|
|
73
|
+
IdentifyError["ParentAppError"] = "parent_app_error";
|
|
74
|
+
/**
|
|
75
|
+
* Indicates an unexpected or invalid login state was encountered.
|
|
76
|
+
*/
|
|
77
|
+
IdentifyError["UnexpectedState"] = "unexpected_state";
|
|
78
|
+
/**
|
|
79
|
+
* Indicates there is no parent application to delegate to.
|
|
80
|
+
*/
|
|
81
|
+
IdentifyError["NoParentApplication"] = "no_parent_application";
|
|
82
|
+
/**
|
|
83
|
+
* Indicates a timeout occurred when communicating with the parent app.
|
|
84
|
+
*/
|
|
85
|
+
IdentifyError["ParentTimeoutError"] = "parent_timeout_error";
|
|
86
|
+
})(exports.IdentifyError || (exports.IdentifyError = {}));
|
|
87
|
+
/**
|
|
88
|
+
* @internal
|
|
89
|
+
*/
|
|
90
|
+
const IdentifyErrorMessages = {
|
|
91
|
+
[exports.IdentifyError.ExtensionApiError]: (error) => `Identify extension API returned an error: ${error}`,
|
|
92
|
+
[exports.IdentifyError.ExtensionNotSetup]: () => 'Identify extension is not set up for this project',
|
|
93
|
+
[exports.IdentifyError.NoCredentials]: () => 'Identify credentials are not set',
|
|
94
|
+
[exports.IdentifyError.ParentAppError]: (error) => `Parent application error: ${error}`,
|
|
95
|
+
[exports.IdentifyError.UnexpectedState]: (state) => `Unexpected LoginState: ${state}`,
|
|
96
|
+
[exports.IdentifyError.NoParentApplication]: () => 'No parent application available for delegation',
|
|
97
|
+
[exports.IdentifyError.ParentTimeoutError]: (error) => `Parent application timeout: ${error}. Please check if the identify-kit is imported on the parent page.`,
|
|
98
|
+
};
|
|
99
|
+
var IdentifyAction;
|
|
100
|
+
(function (IdentifyAction) {
|
|
101
|
+
IdentifyAction["Login"] = "identifyLogin";
|
|
102
|
+
IdentifyAction["Logout"] = "identifyLogout";
|
|
103
|
+
IdentifyAction["RequestLogin"] = "identifyRequestLogin";
|
|
104
|
+
IdentifyAction["RequestLogout"] = "identifyRequestLogout";
|
|
105
|
+
IdentifyAction["GetCredentials"] = "identifyGetCredentials";
|
|
106
|
+
IdentifyAction["SetCredentials"] = "identifySetCredentials";
|
|
107
|
+
IdentifyAction["ClearCredentials"] = "identifyClearCredentials";
|
|
108
|
+
})(IdentifyAction || (IdentifyAction = {}));
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* @license
|
|
112
|
+
* @monterosa/sdk-identify-kit
|
|
113
|
+
*
|
|
114
|
+
* Copyright © 2023 Monterosa Productions Limited. All rights reserved.
|
|
115
|
+
*
|
|
116
|
+
* More details on the license can be found at https://www.monterosa.co/sdk/license
|
|
117
|
+
*/
|
|
118
|
+
const EXTENSION_ID = 'lvis-id-custom-tab';
|
|
119
|
+
const SIGNATURE_TTL = 10000;
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* @license
|
|
123
|
+
* @monterosa/sdk-identify-kit
|
|
124
|
+
*
|
|
125
|
+
* Copyright © 2023 Monterosa Productions Limited. All rights reserved.
|
|
126
|
+
*
|
|
127
|
+
* More details on the license can be found at https://www.monterosa.co/sdk/license
|
|
128
|
+
*/
|
|
129
|
+
class Identify extends sdkUtil.Emitter {
|
|
130
|
+
constructor(sdk, options = {}) {
|
|
131
|
+
super();
|
|
132
|
+
this.sdk = sdk;
|
|
133
|
+
this.wasLoggedIn = false;
|
|
134
|
+
this._signature = null;
|
|
135
|
+
this._userData = null;
|
|
136
|
+
this._state = {
|
|
137
|
+
state: 'logged_out',
|
|
138
|
+
};
|
|
139
|
+
this._options = Object.assign({ strategy: 'email', version: 1 }, options);
|
|
140
|
+
}
|
|
141
|
+
static async fetchIdentifyHost(host, projectId) {
|
|
142
|
+
var _a, _b;
|
|
143
|
+
const listings = await sdkInteractInterop.fetchListings(host, projectId);
|
|
144
|
+
const extensionAssets = (_b = (_a = listings.assets) === null || _a === void 0 ? void 0 : _a[EXTENSION_ID]) !== null && _b !== void 0 ? _b : [];
|
|
145
|
+
const endpointAsset = extensionAssets.find(({ name }) => name === 'endpoint');
|
|
146
|
+
if (!endpointAsset) {
|
|
147
|
+
throw sdkUtil.createError(exports.IdentifyError.ExtensionNotSetup, IdentifyErrorMessages);
|
|
148
|
+
}
|
|
149
|
+
return endpointAsset.data;
|
|
150
|
+
}
|
|
151
|
+
get state() {
|
|
152
|
+
return this._state;
|
|
153
|
+
}
|
|
154
|
+
set state(state) {
|
|
155
|
+
if (this._state.state === state.state) {
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
switch (state.state) {
|
|
159
|
+
case 'logged_in':
|
|
160
|
+
this.wasLoggedIn = true;
|
|
161
|
+
break;
|
|
162
|
+
case 'error':
|
|
163
|
+
this.wasLoggedIn = false;
|
|
164
|
+
break;
|
|
165
|
+
case 'logged_out':
|
|
166
|
+
case 'pending':
|
|
167
|
+
// preserve wasLoggedIn — these states reflect transient transport
|
|
168
|
+
// state, not a deliberate logout (credentials are the gate for that)
|
|
169
|
+
break;
|
|
170
|
+
default: {
|
|
171
|
+
// Exhaustiveness check: TypeScript errors if a new state is added
|
|
172
|
+
// but not handled. This is a compile-time error.
|
|
173
|
+
//
|
|
174
|
+
// See: https://www.typescriptlang.org/docs/handbook/2/narrowing.html#exhaustiveness-checking
|
|
175
|
+
const exhaustiveCheck = state.state;
|
|
176
|
+
throw sdkUtil.createError(exports.IdentifyError.UnexpectedState, IdentifyErrorMessages, String(exhaustiveCheck));
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
this._state = state;
|
|
180
|
+
this.emit(IdentifyEvent.StateUpdated, state);
|
|
181
|
+
}
|
|
182
|
+
get options() {
|
|
183
|
+
return this._options;
|
|
184
|
+
}
|
|
185
|
+
set signature(signature) {
|
|
186
|
+
if (this._signature === signature) {
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
this._signature = signature;
|
|
190
|
+
clearTimeout(this.signatureExpireTimeout);
|
|
191
|
+
if (signature !== null) {
|
|
192
|
+
this.signatureExpireTimeout = setTimeout(() => {
|
|
193
|
+
this.signature = null;
|
|
194
|
+
}, SIGNATURE_TTL);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
get signature() {
|
|
198
|
+
return this._signature;
|
|
199
|
+
}
|
|
200
|
+
set userData(data) {
|
|
201
|
+
if (this._userData === data) {
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
this._userData = data;
|
|
205
|
+
}
|
|
206
|
+
get userData() {
|
|
207
|
+
return this._userData;
|
|
208
|
+
}
|
|
209
|
+
async getUrl(path = '') {
|
|
210
|
+
const { host, projectId } = this.sdk.options;
|
|
211
|
+
if (this.host === undefined) {
|
|
212
|
+
this.host = await Identify.fetchIdentifyHost(host, projectId);
|
|
213
|
+
}
|
|
214
|
+
const url = new URL(this.host);
|
|
215
|
+
const { version, strategy, provider } = this._options;
|
|
216
|
+
url.pathname = `/v${version}${path}`;
|
|
217
|
+
url.searchParams.set('projectId', projectId);
|
|
218
|
+
url.searchParams.set('deviceId', sdkCore.getDeviceId());
|
|
219
|
+
url.searchParams.set('strategy', strategy);
|
|
220
|
+
if (provider !== undefined) {
|
|
221
|
+
url.searchParams.set('provider', provider);
|
|
222
|
+
}
|
|
223
|
+
return url.toString();
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* @license
|
|
229
|
+
* @monterosa/sdk-identify-kit
|
|
230
|
+
*
|
|
231
|
+
* Copyright © 2025-2026 Monterosa Productions Limited. All rights reserved.
|
|
232
|
+
*
|
|
233
|
+
* More details on the license can be found at https://www.monterosa.co/sdk/license
|
|
234
|
+
*/
|
|
235
|
+
const entries = new Map();
|
|
236
|
+
function getExperiences() {
|
|
237
|
+
return Array.from(entries.values()).map((entry) => entry.experience);
|
|
238
|
+
}
|
|
239
|
+
function addExperience(experience, unsubs = []) {
|
|
240
|
+
entries.set(experience.id, {
|
|
241
|
+
experience,
|
|
242
|
+
unsubs: new Set(unsubs),
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
function deleteExperience(experience) {
|
|
246
|
+
const entry = entries.get(experience.id);
|
|
247
|
+
if (entry) {
|
|
248
|
+
entry.unsubs.forEach((unsub) => unsub());
|
|
249
|
+
entries.delete(experience.id);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* @license
|
|
255
|
+
* @monterosa/sdk-identify-kit
|
|
256
|
+
*
|
|
257
|
+
* Copyright © 2026 Monterosa Productions Limited. All rights reserved.
|
|
258
|
+
*
|
|
259
|
+
* More details on the license can be found at https://www.monterosa.co/sdk/license
|
|
260
|
+
*/
|
|
261
|
+
/**
|
|
262
|
+
* Locally-owned login policy.
|
|
263
|
+
*
|
|
264
|
+
* Defaults to `'disabled'`. Set via the public `setLoginPolicy`. Never
|
|
265
|
+
* propagated across the wire — each level owns its own policy.
|
|
266
|
+
*
|
|
267
|
+
* @internal
|
|
268
|
+
*/
|
|
269
|
+
class LoginPolicyStore extends sdkUtil.Emitter {
|
|
270
|
+
constructor() {
|
|
271
|
+
super();
|
|
272
|
+
this._policy = 'disabled';
|
|
273
|
+
}
|
|
274
|
+
static getInstance() {
|
|
275
|
+
if (!LoginPolicyStore.instance) {
|
|
276
|
+
LoginPolicyStore.instance = new LoginPolicyStore();
|
|
277
|
+
}
|
|
278
|
+
return LoginPolicyStore.instance;
|
|
279
|
+
}
|
|
280
|
+
get policy() {
|
|
281
|
+
return this._policy;
|
|
282
|
+
}
|
|
283
|
+
set policy(next) {
|
|
284
|
+
if (this._policy === next) {
|
|
285
|
+
return;
|
|
286
|
+
}
|
|
287
|
+
this._policy = next;
|
|
288
|
+
this.emit(IdentifyEvent.LoginPolicyUpdated, next);
|
|
289
|
+
}
|
|
290
|
+
subscribe(handler) {
|
|
291
|
+
return sdkUtil.subscribe(this, IdentifyEvent.LoginPolicyUpdated, handler);
|
|
292
|
+
}
|
|
293
|
+
reset() {
|
|
294
|
+
this._policy = 'disabled';
|
|
295
|
+
this.off(IdentifyEvent.LoginPolicyUpdated);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
/**
|
|
300
|
+
* @license
|
|
301
|
+
* @monterosa/sdk-identify-kit
|
|
302
|
+
*
|
|
303
|
+
* Copyright © 2026 Monterosa Productions Limited. All rights reserved.
|
|
304
|
+
*
|
|
305
|
+
* More details on the license can be found at https://www.monterosa.co/sdk/license
|
|
306
|
+
*/
|
|
307
|
+
/**
|
|
308
|
+
* Root-owned credentials slot.
|
|
309
|
+
*
|
|
310
|
+
* Single source of truth at the root of an integration; an internal
|
|
311
|
+
* mirror at every other level, kept in sync via `Login` / `Logout`
|
|
312
|
+
* broadcasts. Not exposed publicly — public access flows through
|
|
313
|
+
* `setCredentials` / `getCredentials` / `clearCredentials` and
|
|
314
|
+
* `onCredentialsUpdated`.
|
|
315
|
+
*
|
|
316
|
+
* @internal
|
|
317
|
+
*/
|
|
318
|
+
class CredentialsStore extends sdkUtil.Emitter {
|
|
319
|
+
constructor() {
|
|
320
|
+
super();
|
|
321
|
+
this._credentials = null;
|
|
322
|
+
}
|
|
323
|
+
static getInstance() {
|
|
324
|
+
if (!CredentialsStore.instance) {
|
|
325
|
+
CredentialsStore.instance = new CredentialsStore();
|
|
326
|
+
}
|
|
327
|
+
return CredentialsStore.instance;
|
|
328
|
+
}
|
|
329
|
+
get credentials() {
|
|
330
|
+
return this._credentials;
|
|
331
|
+
}
|
|
332
|
+
set credentials(next) {
|
|
333
|
+
if (this._credentials === next) {
|
|
334
|
+
return;
|
|
335
|
+
}
|
|
336
|
+
this._credentials = next;
|
|
337
|
+
this.emit(IdentifyEvent.CredentialsUpdated, next);
|
|
338
|
+
}
|
|
339
|
+
subscribe(handler) {
|
|
340
|
+
return sdkUtil.subscribe(this, IdentifyEvent.CredentialsUpdated, handler);
|
|
341
|
+
}
|
|
342
|
+
reset() {
|
|
343
|
+
this._credentials = null;
|
|
344
|
+
this.off(IdentifyEvent.CredentialsUpdated);
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
/**
|
|
349
|
+
* @license
|
|
350
|
+
* identify-kit
|
|
351
|
+
*
|
|
352
|
+
* Copyright © 2023-2026 Monterosa Productions Limited. All rights reserved.
|
|
353
|
+
*
|
|
354
|
+
* More details on the license can be found at https://www.monterosa.co/sdk/license
|
|
355
|
+
*/
|
|
356
|
+
const loginPolicyStore = LoginPolicyStore.getInstance();
|
|
357
|
+
const credentialsStore = CredentialsStore.getInstance();
|
|
358
|
+
/**
|
|
359
|
+
* @internal
|
|
360
|
+
*/
|
|
361
|
+
const identifyKits = new Map();
|
|
362
|
+
/**
|
|
363
|
+
* Emitter for identify request events that are not tied to a specific
|
|
364
|
+
* identify instance (login / logout requests).
|
|
365
|
+
*
|
|
366
|
+
* @internal
|
|
367
|
+
*/
|
|
368
|
+
const requestsEmitter = new sdkUtil.Emitter();
|
|
369
|
+
/**
|
|
370
|
+
* Read the current locally-owned login policy.
|
|
371
|
+
*
|
|
372
|
+
* Returns `'disabled'` by default.
|
|
373
|
+
*
|
|
374
|
+
* @internal
|
|
375
|
+
*/
|
|
376
|
+
function getLoginPolicy() {
|
|
377
|
+
return loginPolicyStore.policy;
|
|
378
|
+
}
|
|
379
|
+
/**
|
|
380
|
+
* Set the locally-owned login policy.
|
|
381
|
+
*
|
|
382
|
+
* All `Identify` instances at this level honour the same policy; the
|
|
383
|
+
* value is never propagated across the wire (each level of a multi-level
|
|
384
|
+
* integration owns its own policy).
|
|
385
|
+
*
|
|
386
|
+
* Policy gates the Enmasse `loginTask` only — credentials always mirror
|
|
387
|
+
* from upstream regardless of policy. A `'disabled'` level still
|
|
388
|
+
* receives credentials via the module-load pull and the cascading
|
|
389
|
+
* `Login` / `Logout` broadcasts; it just doesn't attempt Enmasse
|
|
390
|
+
* validation against them.
|
|
391
|
+
*
|
|
392
|
+
* When the policy transitions to `'auto'`, enqueues a sync-state task
|
|
393
|
+
* for every existing identify so that any in-flight session is picked
|
|
394
|
+
* up.
|
|
395
|
+
*
|
|
396
|
+
* Idempotent: setting the same policy that's already in effect is a
|
|
397
|
+
* no-op.
|
|
398
|
+
*
|
|
399
|
+
* @param policy - `'auto'` to enable auto-login, `'disabled'` to opt out.
|
|
400
|
+
*/
|
|
401
|
+
function setLoginPolicy(policy) {
|
|
402
|
+
if (loginPolicyStore.policy === policy) {
|
|
403
|
+
return;
|
|
404
|
+
}
|
|
405
|
+
loginPolicyStore.policy = policy;
|
|
406
|
+
if (policy !== 'auto') {
|
|
407
|
+
return;
|
|
408
|
+
}
|
|
409
|
+
// On transition to 'auto': if the local credentials slot already
|
|
410
|
+
// has a value (typical after the non-root module-load pull, or after
|
|
411
|
+
// a setCredentials at the root, or after an upstream Login broadcast
|
|
412
|
+
// has cascaded down), enqueue a login task for each existing
|
|
413
|
+
// identify. If the slot is empty, do nothing — a future credentials
|
|
414
|
+
// arrival via `applyLogin` will trigger the login under this new
|
|
415
|
+
// policy.
|
|
416
|
+
const { credentials } = credentialsStore;
|
|
417
|
+
if (credentials === null) {
|
|
418
|
+
return;
|
|
419
|
+
}
|
|
420
|
+
for (const identify of identifyKits.values()) {
|
|
421
|
+
enqueueLogin(identify, credentials);
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
/**
|
|
425
|
+
* Whether the user should be auto-logged in on reconnect.
|
|
426
|
+
*
|
|
427
|
+
* True when the user was previously logged in, the policy is `'auto'`,
|
|
428
|
+
* and credentials are available on the identify instance.
|
|
429
|
+
*
|
|
430
|
+
* @internal
|
|
431
|
+
*/
|
|
432
|
+
function shouldLoginOnReconnect(identify) {
|
|
433
|
+
return (identify.wasLoggedIn &&
|
|
434
|
+
getLoginPolicy() === 'auto' &&
|
|
435
|
+
credentialsStore.credentials !== null);
|
|
436
|
+
}
|
|
437
|
+
/**
|
|
438
|
+
* @internal
|
|
439
|
+
*/
|
|
440
|
+
const taskQueue = new sdkUtil.TaskQueue();
|
|
441
|
+
function isSdk(value) {
|
|
442
|
+
return value instanceof sdkCore.Sdk;
|
|
443
|
+
}
|
|
444
|
+
async function api(url, token, method = 'GET') {
|
|
445
|
+
const headers = {
|
|
446
|
+
accept: 'application/json',
|
|
447
|
+
};
|
|
448
|
+
let credentials;
|
|
449
|
+
// Only include Authorization header for bearer token authentication
|
|
450
|
+
// When token is 'cookie', use credentials: 'include' to ensure HttpOnly
|
|
451
|
+
// cookies are sent
|
|
452
|
+
if (token === 'cookie') {
|
|
453
|
+
// Use credentials: 'include' to ensure HttpOnly cookies are sent
|
|
454
|
+
credentials = 'include';
|
|
455
|
+
}
|
|
456
|
+
else {
|
|
457
|
+
headers.Authorization = `Bearer ${token}`;
|
|
458
|
+
}
|
|
459
|
+
const response = await fetch(url, {
|
|
460
|
+
method,
|
|
461
|
+
headers,
|
|
462
|
+
credentials,
|
|
463
|
+
});
|
|
464
|
+
const data = (await response.json());
|
|
465
|
+
if (data.result < 0) {
|
|
466
|
+
throw sdkUtil.createError(exports.IdentifyError.ExtensionApiError, IdentifyErrorMessages, data.message);
|
|
467
|
+
}
|
|
468
|
+
return data;
|
|
469
|
+
}
|
|
470
|
+
/**
|
|
471
|
+
* @internal
|
|
472
|
+
*/
|
|
473
|
+
async function sendParentRequest(action, payload) {
|
|
474
|
+
const parentApp = sdkLauncherKit.getParentApplication();
|
|
475
|
+
if (parentApp === null) {
|
|
476
|
+
throw sdkUtil.createError(exports.IdentifyError.NoParentApplication, IdentifyErrorMessages);
|
|
477
|
+
}
|
|
478
|
+
try {
|
|
479
|
+
const response = await sdkLauncherKit.sendSdkRequest(parentApp, action, payload);
|
|
480
|
+
const { error, data } = response.payload;
|
|
481
|
+
if (error !== undefined) {
|
|
482
|
+
throw sdkUtil.createError(exports.IdentifyError.ParentAppError, IdentifyErrorMessages, error);
|
|
483
|
+
}
|
|
484
|
+
return data;
|
|
485
|
+
}
|
|
486
|
+
catch (err) {
|
|
487
|
+
if (err instanceof sdkUtil.MonterosaError &&
|
|
488
|
+
err.code === sdkLauncherKit.BridgeError.RequestTimeoutError) {
|
|
489
|
+
const errorMessage = sdkUtil.getErrorMessage(err);
|
|
490
|
+
throw sdkUtil.createError(exports.IdentifyError.ParentTimeoutError, IdentifyErrorMessages, errorMessage);
|
|
491
|
+
}
|
|
492
|
+
throw err;
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
/**
|
|
496
|
+
* @internal
|
|
497
|
+
*/
|
|
498
|
+
async function login(identify, credentials) {
|
|
499
|
+
const signature = await getSessionSignatureMemoized(identify, credentials);
|
|
500
|
+
const conn = await sdkConnectKit.getConnect(identify.sdk.options.host);
|
|
501
|
+
await sdkConnectKit.connect(conn);
|
|
502
|
+
await sdkConnectKit.login(conn, ...signature);
|
|
503
|
+
}
|
|
504
|
+
const loginWithRetry = sdkUtil.withRetryAsync(login);
|
|
505
|
+
/**
|
|
506
|
+
* @internal
|
|
507
|
+
*/
|
|
508
|
+
async function logout(identify) {
|
|
509
|
+
const conn = await sdkConnectKit.getConnect(identify.sdk.options.host);
|
|
510
|
+
await sdkConnectKit.connect(conn);
|
|
511
|
+
await sdkConnectKit.logout(conn);
|
|
512
|
+
}
|
|
513
|
+
const logoutWithRetry = sdkUtil.withRetryAsync(logout);
|
|
514
|
+
/**
|
|
515
|
+
* @internal
|
|
516
|
+
*/
|
|
517
|
+
async function loginTask(identify, credentials) {
|
|
518
|
+
if (identify.state.state === 'logged_in') {
|
|
519
|
+
return;
|
|
520
|
+
}
|
|
521
|
+
try {
|
|
522
|
+
identify.state = {
|
|
523
|
+
state: 'pending',
|
|
524
|
+
};
|
|
525
|
+
await loginWithRetry(identify, credentials);
|
|
526
|
+
// Update state only if there are no pending tasks in the queue
|
|
527
|
+
if (taskQueue.isEmpty()) {
|
|
528
|
+
identify.state = {
|
|
529
|
+
state: 'logged_in',
|
|
530
|
+
};
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
catch (err) {
|
|
534
|
+
// Abandon any pending login/logout/sync tasks on failure — letting
|
|
535
|
+
// them run after an error would put the user in an inconsistent
|
|
536
|
+
// state. Note: this also drops tasks for unrelated identifies that
|
|
537
|
+
// share the queue; acceptable because the typical runtime has one
|
|
538
|
+
// identify per project.
|
|
539
|
+
taskQueue.clear();
|
|
540
|
+
identify.state = {
|
|
541
|
+
state: 'error',
|
|
542
|
+
error: err instanceof Error ? err.message : 'Unknown error',
|
|
543
|
+
errorType: 'login',
|
|
544
|
+
};
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
/**
|
|
548
|
+
* @internal
|
|
549
|
+
*/
|
|
550
|
+
async function logoutTask(identify) {
|
|
551
|
+
if (identify.state.state === 'logged_out') {
|
|
552
|
+
return;
|
|
553
|
+
}
|
|
554
|
+
try {
|
|
555
|
+
identify.state = {
|
|
556
|
+
state: 'pending',
|
|
557
|
+
};
|
|
558
|
+
await logoutWithRetry(identify);
|
|
559
|
+
// Update state only if there are no pending tasks in the queue
|
|
560
|
+
if (taskQueue.isEmpty()) {
|
|
561
|
+
identify.state = {
|
|
562
|
+
state: 'logged_out',
|
|
563
|
+
};
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
catch (err) {
|
|
567
|
+
// Abandon any pending login/logout/sync tasks on failure — see
|
|
568
|
+
// `loginTask` for the rationale.
|
|
569
|
+
taskQueue.clear();
|
|
570
|
+
identify.state = {
|
|
571
|
+
state: 'error',
|
|
572
|
+
error: err instanceof Error ? err.message : 'Unknown error',
|
|
573
|
+
errorType: 'logout',
|
|
574
|
+
};
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
/**
|
|
578
|
+
* Enqueue a `loginTask` for the given identify when `policy === 'auto'`.
|
|
579
|
+
* The downstream `Login` broadcast is emitted by {@link applyLogin}.
|
|
580
|
+
*
|
|
581
|
+
* @internal
|
|
582
|
+
*/
|
|
583
|
+
function enqueueLogin(identify, credentials, prioritise = false) {
|
|
584
|
+
if (getLoginPolicy() !== 'auto') {
|
|
585
|
+
return;
|
|
586
|
+
}
|
|
587
|
+
if (prioritise) {
|
|
588
|
+
taskQueue.enqueueFront(() => loginTask(identify, credentials));
|
|
589
|
+
}
|
|
590
|
+
else {
|
|
591
|
+
taskQueue.enqueue(() => loginTask(identify, credentials));
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
/**
|
|
595
|
+
* Reset the identify's identity-derived caches and enqueue a `logoutTask`
|
|
596
|
+
* when `policy === 'auto'`.
|
|
597
|
+
*
|
|
598
|
+
* @internal
|
|
599
|
+
*/
|
|
600
|
+
function enqueueLogout(identify) {
|
|
601
|
+
identify.userData = null;
|
|
602
|
+
identify.signature = null;
|
|
603
|
+
if (getLoginPolicy() !== 'auto') {
|
|
604
|
+
return;
|
|
605
|
+
}
|
|
606
|
+
taskQueue.enqueue(() => logoutTask(identify));
|
|
607
|
+
}
|
|
608
|
+
/**
|
|
609
|
+
* Apply new credentials at this level: update the slot, enqueue a
|
|
610
|
+
* `loginTask` for every existing identify on the null-to-set transition,
|
|
611
|
+
* and broadcast `Login` downstream to every embedded experience.
|
|
612
|
+
*
|
|
613
|
+
* Idempotent on token refresh: re-setting an already-set slot updates
|
|
614
|
+
* the value and broadcasts, but does not re-enqueue logins (their
|
|
615
|
+
* `loginTask` would short-circuit on `state === 'logged_in'` anyway).
|
|
616
|
+
*
|
|
617
|
+
* @internal
|
|
618
|
+
*/
|
|
619
|
+
function applyLogin(credentials) {
|
|
620
|
+
const shouldLogin = credentialsStore.credentials === null;
|
|
621
|
+
credentialsStore.credentials = credentials;
|
|
622
|
+
if (shouldLogin) {
|
|
623
|
+
for (const identify of identifyKits.values()) {
|
|
624
|
+
enqueueLogin(identify, credentials);
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
for (const experience of getExperiences()) {
|
|
628
|
+
sdkLauncherKit.sendSdkMessage(experience, IdentifyAction.Login, credentials);
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
/**
|
|
632
|
+
* Clear credentials at this level: null out the slot, enqueue a
|
|
633
|
+
* `logoutTask` for every existing identify on the set-to-null
|
|
634
|
+
* transition, and broadcast `Logout` downstream to every embedded
|
|
635
|
+
* experience.
|
|
636
|
+
*
|
|
637
|
+
* @internal
|
|
638
|
+
*/
|
|
639
|
+
function applyLogout() {
|
|
640
|
+
const shouldLogout = credentialsStore.credentials !== null;
|
|
641
|
+
credentialsStore.credentials = null;
|
|
642
|
+
if (shouldLogout) {
|
|
643
|
+
for (const identify of identifyKits.values()) {
|
|
644
|
+
enqueueLogout(identify);
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
for (const experience of getExperiences()) {
|
|
648
|
+
sdkLauncherKit.sendSdkMessage(experience, IdentifyAction.Logout, {});
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
/**
|
|
652
|
+
* Pull state from the parent application and apply locally.
|
|
653
|
+
*
|
|
654
|
+
* Fires once at non-root bridge module load, regardless of
|
|
655
|
+
* `loginPolicy` — token presence is the customer-side sign-in state and
|
|
656
|
+
* must mirror upstream whether or not we choose to validate against
|
|
657
|
+
* Enmasse. Best-effort: a missing or non-identify-kit parent silently
|
|
658
|
+
* resolves with no slot change.
|
|
659
|
+
*
|
|
660
|
+
* Uses {@link applyLogin} so the slot update cascades downstream to
|
|
661
|
+
* this level's own embedded experiences and per-identify loginTasks
|
|
662
|
+
* are enqueued under `loginPolicy === 'auto'`.
|
|
663
|
+
*
|
|
664
|
+
* @internal
|
|
665
|
+
*/
|
|
666
|
+
async function pullStateFromParent() {
|
|
667
|
+
let credentials;
|
|
668
|
+
try {
|
|
669
|
+
credentials = await getCredentials();
|
|
670
|
+
}
|
|
671
|
+
catch (_a) {
|
|
672
|
+
return;
|
|
673
|
+
}
|
|
674
|
+
if (credentials !== null) {
|
|
675
|
+
applyLogin(credentials);
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
function getIdentify(sdkOrOptions, options) {
|
|
679
|
+
let sdk;
|
|
680
|
+
let identifyOptions;
|
|
681
|
+
if (isSdk(sdkOrOptions)) {
|
|
682
|
+
/**
|
|
683
|
+
* Interface: getIdentify(sdk, options?)
|
|
684
|
+
*/
|
|
685
|
+
sdk = sdkOrOptions;
|
|
686
|
+
if (options !== undefined) {
|
|
687
|
+
identifyOptions = options;
|
|
688
|
+
}
|
|
689
|
+
else {
|
|
690
|
+
identifyOptions = {};
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
else if (sdkOrOptions !== undefined) {
|
|
694
|
+
/**
|
|
695
|
+
* Interface: getIdentify(options)
|
|
696
|
+
*/
|
|
697
|
+
sdk = sdkCore.getSdk();
|
|
698
|
+
identifyOptions = sdkOrOptions;
|
|
699
|
+
}
|
|
700
|
+
else {
|
|
701
|
+
/**
|
|
702
|
+
* Interface: getIdentify()
|
|
703
|
+
*/
|
|
704
|
+
sdk = sdkCore.getSdk();
|
|
705
|
+
identifyOptions = {};
|
|
706
|
+
}
|
|
707
|
+
const { projectId } = sdk.options;
|
|
708
|
+
if (identifyKits.has(projectId)) {
|
|
709
|
+
return identifyKits.get(projectId);
|
|
710
|
+
}
|
|
711
|
+
const identify = new Identify(sdk, identifyOptions);
|
|
712
|
+
watchConnectionStatus(identify);
|
|
713
|
+
identifyKits.set(projectId, identify);
|
|
714
|
+
return identify;
|
|
715
|
+
}
|
|
716
|
+
/**
|
|
717
|
+
* Requests a user login.
|
|
718
|
+
*
|
|
719
|
+
* @example
|
|
720
|
+
* ```javascript
|
|
721
|
+
* import { configure } from '@monterosa/sdk-core';
|
|
722
|
+
* import { requestLogin } from '@monterosa/sdk-identify-kit';
|
|
723
|
+
*
|
|
724
|
+
* configure({ host: '...', projectId: '...' });
|
|
725
|
+
*
|
|
726
|
+
* try {
|
|
727
|
+
* await requestLogin();
|
|
728
|
+
*
|
|
729
|
+
* console.log('Login request successful');
|
|
730
|
+
* } catch (err) {
|
|
731
|
+
* console.error('Error requesting login:', err.message)
|
|
732
|
+
* }
|
|
733
|
+
* ```
|
|
734
|
+
*
|
|
735
|
+
* @remarks
|
|
736
|
+
* - If the app is running within a third-party application that uses
|
|
737
|
+
* the Monterosa SDK, the function delegates to the parent app
|
|
738
|
+
* to handle the login process.
|
|
739
|
+
*
|
|
740
|
+
* @returns A Promise that resolves with `void` when the login request
|
|
741
|
+
* is completed.
|
|
742
|
+
*/
|
|
743
|
+
async function requestLogin() {
|
|
744
|
+
if (sdkLauncherKit.getParentApplication() !== null) {
|
|
745
|
+
await sendParentRequest(IdentifyAction.RequestLogin);
|
|
746
|
+
return;
|
|
747
|
+
}
|
|
748
|
+
requestsEmitter.emit(IdentifyEvent.LoginRequested);
|
|
749
|
+
}
|
|
750
|
+
/**
|
|
751
|
+
* Read the current credentials, pulling from the parent application when
|
|
752
|
+
* called from an embedded experience.
|
|
753
|
+
*
|
|
754
|
+
* At the root this returns the canonical slot value. At a non-root
|
|
755
|
+
* experience this dispatches a `GetCredentials` request upstream and
|
|
756
|
+
* resolves with the response, ensuring the caller always sees the
|
|
757
|
+
* authoritative value rather than a possibly-stale local mirror.
|
|
758
|
+
*
|
|
759
|
+
* @returns A Promise that resolves to the current credentials, or `null`
|
|
760
|
+
* if none are set.
|
|
761
|
+
*/
|
|
762
|
+
async function getCredentials() {
|
|
763
|
+
if (sdkLauncherKit.getParentApplication() === null) {
|
|
764
|
+
return credentialsStore.credentials;
|
|
765
|
+
}
|
|
766
|
+
return sendParentRequest(IdentifyAction.GetCredentials);
|
|
767
|
+
}
|
|
768
|
+
/**
|
|
769
|
+
* Requests a user logout.
|
|
770
|
+
*
|
|
771
|
+
* @example
|
|
772
|
+
* ```javascript
|
|
773
|
+
* import { configure } from '@monterosa/sdk-core';
|
|
774
|
+
* import { requestLogout } from '@monterosa/sdk-identify-kit';
|
|
775
|
+
*
|
|
776
|
+
* configure({ host: '...', projectId: '...' });
|
|
777
|
+
*
|
|
778
|
+
* try {
|
|
779
|
+
* await requestLogout();
|
|
780
|
+
*
|
|
781
|
+
* console.log('Logout request successful');
|
|
782
|
+
* } catch (err) {
|
|
783
|
+
* console.error('Error requesting logout:', err.message)
|
|
784
|
+
* }
|
|
785
|
+
* ```
|
|
786
|
+
*
|
|
787
|
+
* @remarks
|
|
788
|
+
* - If the app is running within a third-party application that uses
|
|
789
|
+
* the Monterosa SDK, the function delegates to the parent app
|
|
790
|
+
* to handle the logout process.
|
|
791
|
+
*
|
|
792
|
+
* @returns A Promise that resolves with `void` when the logout request
|
|
793
|
+
* is completed.
|
|
794
|
+
*/
|
|
795
|
+
async function requestLogout() {
|
|
796
|
+
if (sdkLauncherKit.getParentApplication() !== null) {
|
|
797
|
+
await sendParentRequest(IdentifyAction.RequestLogout);
|
|
798
|
+
return;
|
|
799
|
+
}
|
|
800
|
+
requestsEmitter.emit(IdentifyEvent.LogoutRequested);
|
|
801
|
+
}
|
|
802
|
+
/**
|
|
803
|
+
* @internal
|
|
804
|
+
*
|
|
805
|
+
* Returns a signature for a user session. The signature is based on the
|
|
806
|
+
* user's identifying information provided in the `IdentifyKit` instance.
|
|
807
|
+
*
|
|
808
|
+
* @remarks
|
|
809
|
+
* This function does not handle errors. Callers are responsible for catching
|
|
810
|
+
* and handling failures.
|
|
811
|
+
*
|
|
812
|
+
* @param identify - An instance of the `IdentifyKit` class used for user
|
|
813
|
+
* identification.
|
|
814
|
+
* @param credentials - The credentials of the user.
|
|
815
|
+
*
|
|
816
|
+
* @returns A Promise that resolves to a `Signature` object.
|
|
817
|
+
*/
|
|
818
|
+
async function getSessionSignature(identify, credentials) {
|
|
819
|
+
if (identify.signature !== null) {
|
|
820
|
+
return identify.signature;
|
|
821
|
+
}
|
|
822
|
+
const url = await identify.getUrl('/user/check');
|
|
823
|
+
const { data } = await api(url, credentials.token);
|
|
824
|
+
const signature = [data.userId, data.timeStamp, data.signature];
|
|
825
|
+
identify.signature = signature;
|
|
826
|
+
return signature;
|
|
827
|
+
}
|
|
828
|
+
/**
|
|
829
|
+
* A memoized version of the `getSessionSignature` function. Memo key is
|
|
830
|
+
* the credentials token, so both leaf-side and bridge-side callers share
|
|
831
|
+
* the same cache for the same token.
|
|
832
|
+
*/
|
|
833
|
+
const getSessionSignatureMemoized = sdkUtil.memoizePromise(getSessionSignature, (identify, credentials) => credentials.token, {
|
|
834
|
+
clearOnResolve: true,
|
|
835
|
+
clearOnReject: true,
|
|
836
|
+
});
|
|
837
|
+
/**
|
|
838
|
+
* Fetches user data for the given credentials.
|
|
839
|
+
*
|
|
840
|
+
* Credentials are required explicitly so the precondition is visible at
|
|
841
|
+
* the call site — without them the function cannot be called. Obtain a
|
|
842
|
+
* `Credentials` value via {@link getCredentials} or the
|
|
843
|
+
* {@link onCredentialsUpdated} callback.
|
|
844
|
+
*
|
|
845
|
+
* @example
|
|
846
|
+
* ```javascript
|
|
847
|
+
* import { configure } from '@monterosa/sdk-core';
|
|
848
|
+
* import {
|
|
849
|
+
* getIdentify,
|
|
850
|
+
* getUserData,
|
|
851
|
+
* getCredentials,
|
|
852
|
+
* } from '@monterosa/sdk-identify-kit';
|
|
853
|
+
*
|
|
854
|
+
* configure({ host: '...', projectId: '...' });
|
|
855
|
+
*
|
|
856
|
+
* const identify = getIdentify();
|
|
857
|
+
* const credentials = await getCredentials();
|
|
858
|
+
*
|
|
859
|
+
* if (credentials !== null) {
|
|
860
|
+
* const userData = await getUserData(identify, credentials);
|
|
861
|
+
* console.log('User data:', userData);
|
|
862
|
+
* }
|
|
863
|
+
* ```
|
|
864
|
+
*
|
|
865
|
+
* @remarks
|
|
866
|
+
* Cached on the identify instance after the first successful fetch.
|
|
867
|
+
* Cleared on {@link clearCredentials} via the logout path.
|
|
868
|
+
*
|
|
869
|
+
* @param identify - An instance of `IdentifyKit` that provides the user
|
|
870
|
+
* identification functionality.
|
|
871
|
+
* @param credentials - The user's authentication credentials. The
|
|
872
|
+
* request authorises against the value's token.
|
|
873
|
+
* @returns A Promise that resolves to a key-value object representing
|
|
874
|
+
* user data. The structure and content of the user data object depend
|
|
875
|
+
* on the identify service provider and may vary.
|
|
876
|
+
*/
|
|
877
|
+
async function getUserData(identify, credentials) {
|
|
878
|
+
if (identify.userData !== null) {
|
|
879
|
+
return identify.userData;
|
|
880
|
+
}
|
|
881
|
+
const url = await identify.getUrl('/user');
|
|
882
|
+
const { data } = await api(url, credentials.token);
|
|
883
|
+
identify.userData = data;
|
|
884
|
+
return data;
|
|
885
|
+
}
|
|
886
|
+
/**
|
|
887
|
+
* Sets the user's authentication credentials.
|
|
888
|
+
*
|
|
889
|
+
* @example
|
|
890
|
+
* ```javascript
|
|
891
|
+
* import { configure } from '@monterosa/sdk-core';
|
|
892
|
+
* import { setCredentials } from '@monterosa/sdk-identify-kit';
|
|
893
|
+
*
|
|
894
|
+
* configure({ host: '...', projectId: '...' });
|
|
895
|
+
*
|
|
896
|
+
* await setCredentials({ token: 'abc123' });
|
|
897
|
+
* ```
|
|
898
|
+
*
|
|
899
|
+
* @remarks
|
|
900
|
+
* - At the root of an integration, the credentials are stored locally
|
|
901
|
+
* and a `Login` broadcast is sent to every embedded experience.
|
|
902
|
+
* - When called from an embedded experience, the call is relayed
|
|
903
|
+
* upstream via the `SetCredentials` wire action. The credentials are
|
|
904
|
+
* ultimately applied at the root and propagate back down via the
|
|
905
|
+
* cascading `Login` broadcast.
|
|
906
|
+
*
|
|
907
|
+
* @param credentials - An object representing the user's authentication
|
|
908
|
+
* credentials.
|
|
909
|
+
* @returns A Promise that resolves to `void` once the credentials have
|
|
910
|
+
* been written (root) or the upstream relay has completed (non-root).
|
|
911
|
+
*/
|
|
912
|
+
async function setCredentials(credentials) {
|
|
913
|
+
if (sdkLauncherKit.getParentApplication() !== null) {
|
|
914
|
+
await sendParentRequest(IdentifyAction.SetCredentials, credentials);
|
|
915
|
+
return;
|
|
916
|
+
}
|
|
917
|
+
applyLogin(credentials);
|
|
918
|
+
}
|
|
919
|
+
/**
|
|
920
|
+
* Clears the user's authentication credentials.
|
|
921
|
+
*
|
|
922
|
+
* @example
|
|
923
|
+
* ```javascript
|
|
924
|
+
* import { configure } from '@monterosa/sdk-core';
|
|
925
|
+
* import { clearCredentials } from '@monterosa/sdk-identify-kit';
|
|
926
|
+
*
|
|
927
|
+
* configure({ host: '...', projectId: '...' });
|
|
928
|
+
*
|
|
929
|
+
* await clearCredentials();
|
|
930
|
+
* ```
|
|
931
|
+
*
|
|
932
|
+
* @remarks
|
|
933
|
+
* - At the root of an integration, the credentials slot is cleared and
|
|
934
|
+
* a `Logout` broadcast is sent to every embedded experience.
|
|
935
|
+
* - When called from an embedded experience, the call is relayed
|
|
936
|
+
* upstream via the `ClearCredentials` wire action. The slot is
|
|
937
|
+
* cleared at the root and the cascading `Logout` broadcast propagates
|
|
938
|
+
* back down.
|
|
939
|
+
*
|
|
940
|
+
* @returns A Promise that resolves to `void` once the slot has been
|
|
941
|
+
* cleared (root) or the upstream relay has completed (non-root).
|
|
942
|
+
*/
|
|
943
|
+
async function clearCredentials() {
|
|
944
|
+
if (sdkLauncherKit.getParentApplication() !== null) {
|
|
945
|
+
await sendParentRequest(IdentifyAction.ClearCredentials);
|
|
946
|
+
return;
|
|
947
|
+
}
|
|
948
|
+
applyLogout();
|
|
949
|
+
}
|
|
950
|
+
/**
|
|
951
|
+
* Registers a callback function that will be called whenever the `LoginState`
|
|
952
|
+
* object associated with the `IdentifyKit` instance is updated.
|
|
953
|
+
*
|
|
954
|
+
* @example
|
|
955
|
+
* ```javascript
|
|
956
|
+
* import { configure } from '@monterosa/sdk-core';
|
|
957
|
+
* import { getIdentify, onStateUpdated } from '@monterosa/sdk-identify-kit';
|
|
958
|
+
*
|
|
959
|
+
* configure({ host: '...', projectId: '...' });
|
|
960
|
+
*
|
|
961
|
+
* const identify = getIdentify();
|
|
962
|
+
*
|
|
963
|
+
* const unsubscribe = onStateUpdated(identify, (state) => {
|
|
964
|
+
* console.log("State updated:", state);
|
|
965
|
+
* });
|
|
966
|
+
*
|
|
967
|
+
* // Call unsubscribe() to unregister the callback function
|
|
968
|
+
* // unsubscribe();
|
|
969
|
+
* ```
|
|
970
|
+
*
|
|
971
|
+
* @param identify - An instance of `IdentifyKit` that provides the user
|
|
972
|
+
* identification functionality.
|
|
973
|
+
* @param callback - The callback function to register. This function will be
|
|
974
|
+
* called with the updated `LoginState` object as its only argument.
|
|
975
|
+
* @returns An `Unsubscribe` function that can be called to unregister
|
|
976
|
+
* the callback function.
|
|
977
|
+
*/
|
|
978
|
+
function onStateUpdated(identify, callback) {
|
|
979
|
+
return sdkUtil.subscribe(identify, IdentifyEvent.StateUpdated, callback);
|
|
980
|
+
}
|
|
981
|
+
/**
|
|
982
|
+
* Registers a callback function that will be called whenever the
|
|
983
|
+
* credentials slot is updated.
|
|
984
|
+
*
|
|
985
|
+
* @example
|
|
986
|
+
* ```javascript
|
|
987
|
+
* import { configure } from '@monterosa/sdk-core';
|
|
988
|
+
* import { onCredentialsUpdated } from '@monterosa/sdk-identify-kit';
|
|
989
|
+
*
|
|
990
|
+
* configure({ host: '...', projectId: '...' });
|
|
991
|
+
*
|
|
992
|
+
* const unsubscribe = onCredentialsUpdated((credentials) => {
|
|
993
|
+
* if (credentials !== null) {
|
|
994
|
+
* console.log("Credentials updated:", credentials);
|
|
995
|
+
* } else {
|
|
996
|
+
* console.log("Credentials cleared.");
|
|
997
|
+
* }
|
|
998
|
+
* });
|
|
999
|
+
*
|
|
1000
|
+
* // Call unsubscribe() to unregister the callback function
|
|
1001
|
+
* // unsubscribe();
|
|
1002
|
+
* ```
|
|
1003
|
+
*
|
|
1004
|
+
* @param callback - The callback function to register. This function will be
|
|
1005
|
+
* called with the updated `Credentials` object as its only argument.
|
|
1006
|
+
* If the value `null` is received, it indicates that the credentials have
|
|
1007
|
+
* been cleared.
|
|
1008
|
+
* @returns An `Unsubscribe` function that can be called to unregister
|
|
1009
|
+
* the callback function.
|
|
1010
|
+
*/
|
|
1011
|
+
function onCredentialsUpdated(callback) {
|
|
1012
|
+
return credentialsStore.subscribe(callback);
|
|
1013
|
+
}
|
|
1014
|
+
/**
|
|
1015
|
+
* Registers a callback function that will be called when a login is requested
|
|
1016
|
+
* by an Experience.
|
|
1017
|
+
*
|
|
1018
|
+
* @example
|
|
1019
|
+
* ```javascript
|
|
1020
|
+
* import { configure } from '@monterosa/sdk-core';
|
|
1021
|
+
* import { onLoginRequestedByExperience } from '@monterosa/sdk-identify-kit';
|
|
1022
|
+
*
|
|
1023
|
+
* configure({ host: '...', projectId: '...' });
|
|
1024
|
+
*
|
|
1025
|
+
* const unsubscribe = onLoginRequestedByExperience(() => {
|
|
1026
|
+
* showLoginForm();
|
|
1027
|
+
* });
|
|
1028
|
+
*
|
|
1029
|
+
* // Call unsubscribe() to unregister the callback function
|
|
1030
|
+
* // unsubscribe();
|
|
1031
|
+
* ```
|
|
1032
|
+
*
|
|
1033
|
+
* @param callback - The callback function to register. This function will
|
|
1034
|
+
* be called when a login is requested by an Experience.
|
|
1035
|
+
* @returns An `Unsubscribe` function that can be called to unregister
|
|
1036
|
+
* the callback function.
|
|
1037
|
+
*/
|
|
1038
|
+
function onLoginRequestedByExperience(callback) {
|
|
1039
|
+
return sdkUtil.subscribe(requestsEmitter, IdentifyEvent.LoginRequested, callback);
|
|
1040
|
+
}
|
|
1041
|
+
/**
|
|
1042
|
+
* Registers a callback function that will be called when a logout is requested
|
|
1043
|
+
* by an Experience.
|
|
1044
|
+
*
|
|
1045
|
+
* @example
|
|
1046
|
+
* ```javascript
|
|
1047
|
+
* import { configure } from '@monterosa/sdk-core';
|
|
1048
|
+
* import { onLogoutRequestedByExperience } from '@monterosa/sdk-identify-kit';
|
|
1049
|
+
*
|
|
1050
|
+
* configure({ host: '...', projectId: '...' });
|
|
1051
|
+
*
|
|
1052
|
+
* const unsubscribe = onLogoutRequestedByExperience(() => {
|
|
1053
|
+
* // logout requested by experience
|
|
1054
|
+
* // perform logout from Auth service
|
|
1055
|
+
* });
|
|
1056
|
+
*
|
|
1057
|
+
* // Call unsubscribe() to unregister the callback function
|
|
1058
|
+
* // unsubscribe();
|
|
1059
|
+
* ```
|
|
1060
|
+
*
|
|
1061
|
+
* @param callback - The callback function to register. This function will
|
|
1062
|
+
* be called when a logout is requested by an Experience.
|
|
1063
|
+
* @returns An `Unsubscribe` function that can be called to unregister
|
|
1064
|
+
* the callback function.
|
|
1065
|
+
*/
|
|
1066
|
+
function onLogoutRequestedByExperience(callback) {
|
|
1067
|
+
return sdkUtil.subscribe(requestsEmitter, IdentifyEvent.LogoutRequested, callback);
|
|
1068
|
+
}
|
|
1069
|
+
/**
|
|
1070
|
+
*
|
|
1071
|
+
* @internal
|
|
1072
|
+
*/
|
|
1073
|
+
async function watchConnectionStatus(identify) {
|
|
1074
|
+
const conn = await sdkConnectKit.getConnect(identify.sdk.options.host);
|
|
1075
|
+
sdkConnectKit.onConnected(conn, () => {
|
|
1076
|
+
if (shouldLoginOnReconnect(identify)) {
|
|
1077
|
+
enqueueLogin(identify, credentialsStore.credentials, true);
|
|
1078
|
+
}
|
|
1079
|
+
taskQueue.resume();
|
|
1080
|
+
});
|
|
1081
|
+
sdkConnectKit.onConnecting(conn, () => {
|
|
1082
|
+
taskQueue.pause();
|
|
1083
|
+
// If the user was logged in before the connection loss, set the state
|
|
1084
|
+
// to pending to notify that the user is no longer logged in and a login
|
|
1085
|
+
// attempt will be made when the connection is restored.
|
|
1086
|
+
if (identify.state.state === 'logged_in') {
|
|
1087
|
+
identify.state = {
|
|
1088
|
+
state: 'pending',
|
|
1089
|
+
};
|
|
1090
|
+
}
|
|
1091
|
+
});
|
|
1092
|
+
sdkConnectKit.onDisconnected(conn, () => {
|
|
1093
|
+
taskQueue.pause();
|
|
1094
|
+
taskQueue.clear();
|
|
1095
|
+
// If the user was logged in before the connection loss, set the state
|
|
1096
|
+
// to logged_out to notify that the user is no longer logged in.
|
|
1097
|
+
if (identify.state.state === 'logged_in') {
|
|
1098
|
+
identify.state = {
|
|
1099
|
+
state: 'logged_out',
|
|
1100
|
+
};
|
|
1101
|
+
}
|
|
1102
|
+
});
|
|
1103
|
+
}
|
|
1104
|
+
|
|
1105
|
+
/**
|
|
1106
|
+
* @license
|
|
1107
|
+
* @monterosa/sdk-identify-kit
|
|
1108
|
+
*
|
|
1109
|
+
* Copyright © 2023-2026 Monterosa Productions Limited. All rights reserved.
|
|
1110
|
+
*
|
|
1111
|
+
* More details on the license can be found at https://www.monterosa.co/sdk/license
|
|
1112
|
+
*/
|
|
1113
|
+
/**
|
|
1114
|
+
* Process an incoming `Login` / `Logout` broadcast from the parent
|
|
1115
|
+
* application. Updates the local credentials slot, enqueues login/logout
|
|
1116
|
+
* for every existing identify, and forwards the broadcast downstream to
|
|
1117
|
+
* this level's own embedded experiences — the mechanism that cascades a
|
|
1118
|
+
* root-level `setCredentials` through every nested experience.
|
|
1119
|
+
*
|
|
1120
|
+
* @internal
|
|
1121
|
+
*/
|
|
1122
|
+
function handleParentMessage(message) {
|
|
1123
|
+
switch (message.action) {
|
|
1124
|
+
case IdentifyAction.Login: {
|
|
1125
|
+
applyLogin(message.payload);
|
|
1126
|
+
break;
|
|
1127
|
+
}
|
|
1128
|
+
case IdentifyAction.Logout: {
|
|
1129
|
+
applyLogout();
|
|
1130
|
+
break;
|
|
1131
|
+
}
|
|
1132
|
+
}
|
|
1133
|
+
}
|
|
1134
|
+
/**
|
|
1135
|
+
* @internal
|
|
1136
|
+
*/
|
|
1137
|
+
function handleExperienceEmbedded(experience) {
|
|
1138
|
+
const sdkMessageUnsub = sdkLauncherKit.onSdkMessage(experience, async (message) => {
|
|
1139
|
+
if (!Object.values(IdentifyAction).includes(message.action)) {
|
|
1140
|
+
return;
|
|
1141
|
+
}
|
|
1142
|
+
const respond = (payload) => sdkLauncherKit.respondToSdkMessage(experience, message, payload);
|
|
1143
|
+
try {
|
|
1144
|
+
switch (message.action) {
|
|
1145
|
+
case IdentifyAction.RequestLogin: {
|
|
1146
|
+
await requestLogin();
|
|
1147
|
+
respond();
|
|
1148
|
+
break;
|
|
1149
|
+
}
|
|
1150
|
+
case IdentifyAction.RequestLogout: {
|
|
1151
|
+
await requestLogout();
|
|
1152
|
+
respond();
|
|
1153
|
+
break;
|
|
1154
|
+
}
|
|
1155
|
+
case IdentifyAction.GetCredentials: {
|
|
1156
|
+
const data = await getCredentials();
|
|
1157
|
+
respond({ data });
|
|
1158
|
+
break;
|
|
1159
|
+
}
|
|
1160
|
+
case IdentifyAction.SetCredentials: {
|
|
1161
|
+
await setCredentials(message.payload);
|
|
1162
|
+
respond();
|
|
1163
|
+
break;
|
|
1164
|
+
}
|
|
1165
|
+
case IdentifyAction.ClearCredentials: {
|
|
1166
|
+
await clearCredentials();
|
|
1167
|
+
respond();
|
|
1168
|
+
break;
|
|
1169
|
+
}
|
|
1170
|
+
}
|
|
1171
|
+
}
|
|
1172
|
+
catch (err) {
|
|
1173
|
+
respond({
|
|
1174
|
+
error: sdkUtil.getErrorMessage(err),
|
|
1175
|
+
});
|
|
1176
|
+
}
|
|
1177
|
+
});
|
|
1178
|
+
addExperience(experience, [sdkMessageUnsub]);
|
|
1179
|
+
}
|
|
1180
|
+
/**
|
|
1181
|
+
* @internal
|
|
1182
|
+
*/
|
|
1183
|
+
function handleExperienceUnmounted(experience) {
|
|
1184
|
+
deleteExperience(experience);
|
|
1185
|
+
}
|
|
1186
|
+
/**
|
|
1187
|
+
* @internal
|
|
1188
|
+
*/
|
|
1189
|
+
function handleExperienceStateChange(experience, state) {
|
|
1190
|
+
if (state === 'mounted') {
|
|
1191
|
+
handleExperienceEmbedded(experience);
|
|
1192
|
+
return;
|
|
1193
|
+
}
|
|
1194
|
+
if (state === 'unmounted') {
|
|
1195
|
+
handleExperienceUnmounted(experience);
|
|
1196
|
+
}
|
|
1197
|
+
}
|
|
1198
|
+
function subscribeToExperienceLifecycle() {
|
|
1199
|
+
sdkLauncherKit.onStateChanged(handleExperienceStateChange);
|
|
1200
|
+
}
|
|
1201
|
+
function connectToParentApplication() {
|
|
1202
|
+
const parentApp = sdkLauncherKit.getParentApplication();
|
|
1203
|
+
if (parentApp === null) {
|
|
1204
|
+
return;
|
|
1205
|
+
}
|
|
1206
|
+
sdkLauncherKit.onSdkMessage(parentApp, handleParentMessage);
|
|
1207
|
+
pullStateFromParent();
|
|
1208
|
+
}
|
|
1209
|
+
subscribeToExperienceLifecycle();
|
|
1210
|
+
connectToParentApplication();
|
|
1211
|
+
|
|
1212
|
+
exports.clearCredentials = clearCredentials;
|
|
1213
|
+
exports.getCredentials = getCredentials;
|
|
1214
|
+
exports.getIdentify = getIdentify;
|
|
1215
|
+
exports.getUserData = getUserData;
|
|
1216
|
+
exports.onCredentialsUpdated = onCredentialsUpdated;
|
|
1217
|
+
exports.onLoginRequestedByExperience = onLoginRequestedByExperience;
|
|
1218
|
+
exports.onLogoutRequestedByExperience = onLogoutRequestedByExperience;
|
|
1219
|
+
exports.onStateUpdated = onStateUpdated;
|
|
1220
|
+
exports.requestLogin = requestLogin;
|
|
1221
|
+
exports.requestLogout = requestLogout;
|
|
1222
|
+
exports.setCredentials = setCredentials;
|
|
1223
|
+
exports.setLoginPolicy = setLoginPolicy;
|
|
1224
|
+
//# sourceMappingURL=index.cjs.map
|