@ibgib/web-gib 0.0.59 → 0.0.61
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/AUTO-GENERATED-version.d.mts +1 -1
- package/dist/AUTO-GENERATED-version.mjs +1 -1
- package/dist/helpers.d.mts.map +1 -1
- package/dist/helpers.mjs +1 -0
- package/dist/helpers.mjs.map +1 -1
- package/dist/identity/device-delegate-helper.d.mts +43 -0
- package/dist/identity/device-delegate-helper.d.mts.map +1 -0
- package/dist/identity/device-delegate-helper.mjs +106 -0
- package/dist/identity/device-delegate-helper.mjs.map +1 -0
- package/dist/identity/device-delegate.respec.d.mts +2 -0
- package/dist/identity/device-delegate.respec.d.mts.map +1 -0
- package/dist/identity/device-delegate.respec.mjs +106 -0
- package/dist/identity/device-delegate.respec.mjs.map +1 -0
- package/dist/identity/identity-constants.mjs +2 -2
- package/dist/identity/identity-constants.mjs.map +1 -1
- package/dist/identity/identity-types.d.mts +4 -4
- package/dist/identity/identity-types.d.mts.map +1 -1
- package/dist/identity/keystone-sync-service.d.mts +18 -0
- package/dist/identity/keystone-sync-service.d.mts.map +1 -1
- package/dist/identity/keystone-sync-service.mjs +77 -1
- package/dist/identity/keystone-sync-service.mjs.map +1 -1
- package/dist/index.d.mts +1 -1
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +1 -2
- package/dist/index.mjs.map +1 -1
- package/dist/storage/storage-helpers.web.d.mts +9 -0
- package/dist/storage/storage-helpers.web.d.mts.map +1 -1
- package/dist/storage/storage-helpers.web.mjs +42 -0
- package/dist/storage/storage-helpers.web.mjs.map +1 -1
- package/dist/ui/component/identity/add-device/add-device.css +93 -17
- package/dist/ui/component/identity/add-device/add-device.d.mts +24 -7
- package/dist/ui/component/identity/add-device/add-device.d.mts.map +1 -1
- package/dist/ui/component/identity/add-device/add-device.html +97 -23
- package/dist/ui/component/identity/add-device/add-device.mjs +489 -198
- package/dist/ui/component/identity/add-device/add-device.mjs.map +1 -1
- package/dist/ui/component/identity/keystone-creator/keystone-creator.d.mts.map +1 -1
- package/dist/ui/component/identity/keystone-creator/keystone-creator.mjs +48 -61
- package/dist/ui/component/identity/keystone-creator/keystone-creator.mjs.map +1 -1
- package/dist/ui/component/identity/keystone-details/keystone-details.css +1 -1
- package/dist/ui/component/identity/keystone-details/keystone-details.d.mts +11 -8
- package/dist/ui/component/identity/keystone-details/keystone-details.d.mts.map +1 -1
- package/dist/ui/component/identity/keystone-details/keystone-details.html +29 -20
- package/dist/ui/component/identity/keystone-details/keystone-details.mjs +358 -97
- package/dist/ui/component/identity/keystone-details/keystone-details.mjs.map +1 -1
- package/dist/ui/component/identity/remove-device/remove-device.d.mts +1 -1
- package/dist/ui/component/identity/remove-device/remove-device.d.mts.map +1 -1
- package/dist/ui/component/identity/remove-device/remove-device.html +3 -3
- package/dist/ui/component/identity/remove-device/remove-device.mjs +7 -7
- package/dist/ui/component/identity/remove-device/remove-device.mjs.map +1 -1
- package/dist/ui/ui-constants.d.mts +1 -0
- package/dist/ui/ui-constants.d.mts.map +1 -1
- package/dist/ui/ui-constants.mjs +1 -0
- package/dist/ui/ui-constants.mjs.map +1 -1
- package/dist/ui/ui-types.d.mts +5 -0
- package/dist/ui/ui-types.d.mts.map +1 -1
- package/dist/util/device-helper.d.mts +30 -0
- package/dist/util/device-helper.d.mts.map +1 -0
- package/dist/util/device-helper.mjs +90 -0
- package/dist/util/device-helper.mjs.map +1 -0
- package/dist/util/device-helper.respec.d.mts +2 -0
- package/dist/util/device-helper.respec.d.mts.map +1 -0
- package/dist/util/device-helper.respec.mjs +83 -0
- package/dist/util/device-helper.respec.mjs.map +1 -0
- package/package.json +3 -3
- package/src/AUTO-GENERATED-version.mts +1 -1
- package/src/helpers.mts +1 -0
- package/src/identity/custodian-delegate-helper.mts.bak +7 -0
- package/src/identity/device-delegate-helper.mts +135 -0
- package/src/identity/device-delegate.respec.mts +125 -0
- package/src/identity/identity-constants.mts +2 -2
- package/src/identity/identity-types.mts +4 -4
- package/src/identity/keystone-sync-service.mts +95 -2
- package/src/index.mts +1 -2
- package/src/storage/storage-helpers.web.mts +47 -0
- package/src/ui/component/identity/add-device/add-device.css +93 -17
- package/src/ui/component/identity/add-device/add-device.html +97 -23
- package/src/ui/component/identity/add-device/add-device.mts +579 -208
- package/src/ui/component/identity/keystone-creator/keystone-creator.mts +55 -69
- package/src/ui/component/identity/keystone-details/keystone-details.css +1 -1
- package/src/ui/component/identity/keystone-details/keystone-details.html +29 -20
- package/src/ui/component/identity/keystone-details/keystone-details.mts +374 -106
- package/src/ui/component/identity/remove-device/remove-device.html +3 -3
- package/src/ui/component/identity/remove-device/remove-device.mts +8 -8
- package/src/ui/ui-constants.mts +1 -0
- package/src/ui/ui-types.mts +6 -0
- package/src/util/device-helper.mts +104 -0
- package/src/util/device-helper.respec.mts +90 -0
- package/dist/identity/custodian-delegate-helper.d.mts +0 -22
- package/dist/identity/custodian-delegate-helper.d.mts.map +0 -1
- package/dist/identity/custodian-delegate-helper.mjs +0 -50
- package/dist/identity/custodian-delegate-helper.mjs.map +0 -1
- package/src/identity/custodian-delegate-helper.mts +0 -61
|
@@ -4,16 +4,19 @@ import commonComponentStylesCss from "../../common-component-styles.css";
|
|
|
4
4
|
import { delay, extractErrorMsg, } from "@ibgib/helper-gib/dist/helpers/utils-helper.mjs";
|
|
5
5
|
import { getIbGibAddr } from "@ibgib/ts-gib/dist/helper.mjs";
|
|
6
6
|
import { KeystoneService_V1 } from "@ibgib/core-gib/dist/keystone/keystone-service-v1.mjs";
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import { getTimelinesGroupedByTjp, getTjpAddr, toDto } from "@ibgib/core-gib/dist/common/other/ibgib-helper.mjs";
|
|
7
|
+
import { KEYSTONE_USERNAME_REGEXP, KEYSTONE_VERB_SYNC, KEYSTONE_VERB_CONNECT } from "@ibgib/core-gib/dist/keystone/keystone-constants.mjs";
|
|
8
|
+
import { deriveDelegateSecret, createSyncDelegateKeystone } from "@ibgib/core-gib/dist/keystone/delegate/delegate-helpers.mjs";
|
|
9
|
+
import { getTimelinesGroupedByTjp, toDto } from "@ibgib/core-gib/dist/common/other/ibgib-helper.mjs";
|
|
11
10
|
import { updateSpecialIndex } from "@ibgib/core-gib/dist/timeline/timeline-api.mjs";
|
|
11
|
+
import { validateEmail } from "@ibgib/core-gib/dist/keystone/keystone-helpers.mjs";
|
|
12
12
|
import { getApiBridge } from "../../../../api/ibgib-api-bridge.mjs";
|
|
13
13
|
import { getGlobalMetaspace_waitIfNeeded } from "../../../../helpers.mjs";
|
|
14
14
|
import { IbGibDynamicComponentMetaBase, IbGibFormInstanceBase, } from "../../ibgib-dynamic-component-bases.mjs";
|
|
15
15
|
import { getComponentCtorArg } from "../../../../app-bootstrap/init-orchestration.mjs";
|
|
16
16
|
import { EVENT_IBGIB_IDENTITY_REQUEST_CHANGE, EVENT_IBGIB_UI_MESSAGE } from "../../../ui-constants.mjs";
|
|
17
|
+
import { generateDefaultDeviceTag } from "../../../../util/device-helper.mjs";
|
|
18
|
+
import { executeSsoOAuthPopup } from "../../../../identity/sso-popup-helper.mjs";
|
|
19
|
+
import { getOrCreateDeviceDelegateSecret, setDeviceDelegateSecret } from "../../../../identity/device-delegate-helper.mjs";
|
|
17
20
|
export const ADD_DEVICE_COMPONENT_NAME = 'ibgib-add-device';
|
|
18
21
|
/**
|
|
19
22
|
* Metadata for the Add Device / Pairing component.
|
|
@@ -44,6 +47,11 @@ export class AddDeviceComponentMeta extends IbGibDynamicComponentMetaBase {
|
|
|
44
47
|
*/
|
|
45
48
|
export class AddDeviceComponentInstance extends IbGibFormInstanceBase {
|
|
46
49
|
lc = `[AddDeviceComponentInstance]`;
|
|
50
|
+
// Staged state across progressive surfaces
|
|
51
|
+
_stagedEmail;
|
|
52
|
+
_stagedPrimaryTjpAddr;
|
|
53
|
+
_stagedPrimaryKeystone;
|
|
54
|
+
_stagedPrimaryGraph;
|
|
47
55
|
constructor() {
|
|
48
56
|
super();
|
|
49
57
|
}
|
|
@@ -51,19 +59,34 @@ export class AddDeviceComponentInstance extends IbGibFormInstanceBase {
|
|
|
51
59
|
await super.initialize(opts);
|
|
52
60
|
}
|
|
53
61
|
async created() {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
62
|
+
const shadow = this.shadowRoot;
|
|
63
|
+
const elements = {};
|
|
64
|
+
elements.containerEl = shadow.getElementById('container');
|
|
65
|
+
elements.titleEl = shadow.getElementById('title');
|
|
66
|
+
elements.subtitleEl = shadow.getElementById('subtitle');
|
|
67
|
+
elements.surfaceEntryEl = shadow.getElementById('surface-entry');
|
|
68
|
+
elements.btnGoogleSso = shadow.getElementById('btn-google-sso');
|
|
69
|
+
elements.btnGithubSso = shadow.getElementById('btn-github-sso');
|
|
70
|
+
elements.inputIdentifier = shadow.getElementById('input-identifier');
|
|
71
|
+
elements.btnContinueEmail = shadow.getElementById('btn-continue-email');
|
|
72
|
+
elements.sectionCodeVerification = shadow.getElementById('section-code-verification');
|
|
73
|
+
elements.inputCode = shadow.getElementById('input-code');
|
|
74
|
+
elements.btnVerifyCode = shadow.getElementById('btn-verify-code');
|
|
75
|
+
elements.sectionSovereignPassword = shadow.getElementById('section-sovereign-password');
|
|
76
|
+
elements.inputPassword = shadow.getElementById('input-password');
|
|
77
|
+
elements.btnAuthorizePassword = shadow.getElementById('btn-authorize-password');
|
|
78
|
+
elements.inputDeviceTag = shadow.getElementById('input-device-tag');
|
|
79
|
+
elements.checkboxMakeActive = shadow.getElementById('checkbox-make-active');
|
|
80
|
+
elements.statusArea = shadow.getElementById('status-area');
|
|
81
|
+
elements.statusText = shadow.getElementById('status-text');
|
|
82
|
+
elements.resultAreaEl = shadow.getElementById('result-area');
|
|
83
|
+
elements.delegateAddrEl = shadow.getElementById('delegate-addr');
|
|
84
|
+
elements.contentEl = elements.containerEl;
|
|
85
|
+
this.elements = elements;
|
|
86
|
+
// Auto-generate clean, friendly default device tag
|
|
87
|
+
if (elements.inputDeviceTag && !elements.inputDeviceTag.value) {
|
|
88
|
+
elements.inputDeviceTag.value = generateDefaultDeviceTag();
|
|
89
|
+
}
|
|
67
90
|
this.initHandlers();
|
|
68
91
|
await this.renderUI();
|
|
69
92
|
}
|
|
@@ -71,7 +94,12 @@ export class AddDeviceComponentInstance extends IbGibFormInstanceBase {
|
|
|
71
94
|
// cleanup if needed
|
|
72
95
|
}
|
|
73
96
|
initHandlers() {
|
|
74
|
-
|
|
97
|
+
const elements = this.elements;
|
|
98
|
+
elements.btnGoogleSso?.addEventListener('click', () => this.handleSsoClick('google'));
|
|
99
|
+
elements.btnGithubSso?.addEventListener('click', () => this.handleSsoClick('github'));
|
|
100
|
+
elements.btnContinueEmail?.addEventListener('click', () => this.handleContinueEmail());
|
|
101
|
+
elements.btnVerifyCode?.addEventListener('click', () => this.handleVerifyCode());
|
|
102
|
+
elements.btnAuthorizePassword?.addEventListener('click', () => this.handleAuthorizePassword());
|
|
75
103
|
this.shadowRoot.getElementById('btn-switch-create-account')?.addEventListener('click', (e) => {
|
|
76
104
|
e.preventDefault();
|
|
77
105
|
window.dispatchEvent(new CustomEvent(EVENT_IBGIB_UI_MESSAGE, {
|
|
@@ -80,249 +108,512 @@ export class AddDeviceComponentInstance extends IbGibFormInstanceBase {
|
|
|
80
108
|
composed: true
|
|
81
109
|
}));
|
|
82
110
|
});
|
|
83
|
-
|
|
84
|
-
|
|
111
|
+
elements.inputIdentifier?.addEventListener('input', () => {
|
|
112
|
+
elements.inputIdentifier.classList.remove('invalid');
|
|
113
|
+
});
|
|
114
|
+
elements.inputCode?.addEventListener('input', () => {
|
|
115
|
+
elements.inputCode.classList.remove('invalid');
|
|
85
116
|
});
|
|
86
|
-
|
|
87
|
-
|
|
117
|
+
elements.inputPassword?.addEventListener('input', () => {
|
|
118
|
+
elements.inputPassword.classList.remove('invalid');
|
|
88
119
|
});
|
|
89
|
-
|
|
90
|
-
|
|
120
|
+
elements.inputDeviceTag?.addEventListener('input', () => {
|
|
121
|
+
elements.inputDeviceTag.classList.remove('invalid');
|
|
91
122
|
});
|
|
92
123
|
}
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
124
|
+
getDeviceTag() {
|
|
125
|
+
const elements = this.elements;
|
|
126
|
+
const raw = elements.inputDeviceTag?.value?.trim();
|
|
127
|
+
if (raw && KEYSTONE_USERNAME_REGEXP.test(raw)) {
|
|
128
|
+
return raw;
|
|
97
129
|
}
|
|
130
|
+
const generated = generateDefaultDeviceTag();
|
|
131
|
+
if (elements.inputDeviceTag) {
|
|
132
|
+
elements.inputDeviceTag.value = generated;
|
|
133
|
+
}
|
|
134
|
+
return generated;
|
|
98
135
|
}
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
async
|
|
103
|
-
const lc = `${this.lc}[${this.
|
|
136
|
+
// =========================================================================
|
|
137
|
+
// Flow 1: OAuth2 SSO Sign In (Google / GitHub)
|
|
138
|
+
// =========================================================================
|
|
139
|
+
async handleSsoClick(providerId) {
|
|
140
|
+
const lc = `${this.lc}[${this.handleSsoClick.name}]`;
|
|
141
|
+
const providerName = providerId === 'google' ? 'Google' : 'GitHub';
|
|
142
|
+
this.setStatus(`Opening ${providerName} sign-in window...`, 'info');
|
|
143
|
+
this.showBusy({
|
|
144
|
+
isBusy: true,
|
|
145
|
+
title: 'Sign In via SSO',
|
|
146
|
+
msg: `Opening ${providerName} authorization window...\nPlease complete sign-in in the popup window.`,
|
|
147
|
+
animationEmoji: '🌐'
|
|
148
|
+
});
|
|
104
149
|
try {
|
|
105
|
-
const
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
this.elements.inputIdentifier.value = identifier;
|
|
109
|
-
if (!identifier) {
|
|
110
|
-
this.elements.inputIdentifier.classList.add('invalid');
|
|
111
|
-
this.setStatus("Please enter a registered email.", "error");
|
|
150
|
+
const resPopup = await executeSsoOAuthPopup({ providerId });
|
|
151
|
+
if (!resPopup.success || !resPopup.code) {
|
|
152
|
+
this.setStatus(resPopup.message || `${providerName} sign-in cancelled.`, 'error');
|
|
112
153
|
return;
|
|
113
154
|
}
|
|
114
|
-
|
|
115
|
-
this.
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
155
|
+
this.setStatus(`Validating ${providerName} authorization...`, 'info');
|
|
156
|
+
this.showBusy({
|
|
157
|
+
isBusy: true,
|
|
158
|
+
title: 'Exchanging SSO Code...',
|
|
159
|
+
msg: `Validating ${providerName} authorization with server...`,
|
|
160
|
+
animationEmoji: '🔐'
|
|
161
|
+
});
|
|
162
|
+
const apiBridge = getApiBridge();
|
|
163
|
+
const resLogin = await apiBridge.postSsoLogin({
|
|
164
|
+
code: resPopup.code,
|
|
165
|
+
providerId,
|
|
166
|
+
redirectUri: resPopup.redirectUri
|
|
167
|
+
});
|
|
168
|
+
if (!resLogin.success || !resLogin.primaryTjpAddr || !resLogin.custodianReqs) {
|
|
169
|
+
this.setStatus(resLogin.message || `${providerName} login failed.`, 'error');
|
|
119
170
|
return;
|
|
120
171
|
}
|
|
121
|
-
const
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
172
|
+
const { primaryTjpAddr, custodianReqs, primaryKeystone } = resLogin;
|
|
173
|
+
const email = resLogin.userInfo?.email || '';
|
|
174
|
+
const username = email ? email.split('@')[0] : 'user';
|
|
175
|
+
await this.completeCustodialDelegateOnboarding({
|
|
176
|
+
primaryTjpAddr,
|
|
177
|
+
custodianReqs,
|
|
178
|
+
fallbackPrimaryKeystone: primaryKeystone,
|
|
179
|
+
fallbackPrimaryGraph: resLogin.primaryGraph,
|
|
180
|
+
email,
|
|
181
|
+
username,
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
catch (error) {
|
|
185
|
+
const emsg = extractErrorMsg(error);
|
|
186
|
+
console.error(`${lc} SSO login error: ${emsg}`, error);
|
|
187
|
+
this.setStatus(`Error: ${emsg}`, 'error');
|
|
188
|
+
}
|
|
189
|
+
finally {
|
|
190
|
+
this.showBusy({ isBusy: false });
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
// =========================================================================
|
|
194
|
+
// Flow 2: Email Sign In (2FA OTP Gate)
|
|
195
|
+
// =========================================================================
|
|
196
|
+
async handleContinueEmail() {
|
|
197
|
+
const lc = `${this.lc}[${this.handleContinueEmail.name}]`;
|
|
198
|
+
const elements = this.elements;
|
|
199
|
+
const email = elements.inputIdentifier?.value?.trim();
|
|
200
|
+
if (!email || !validateEmail(email)) {
|
|
201
|
+
elements.inputIdentifier.classList.add('invalid');
|
|
202
|
+
this.setStatus('Please enter a valid email address.', 'error');
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
elements.btnContinueEmail.disabled = true;
|
|
206
|
+
elements.btnContinueEmail.textContent = 'Checking...';
|
|
207
|
+
this.setStatus('Checking identity on server...', 'info');
|
|
208
|
+
this.showBusy({
|
|
209
|
+
isBusy: true,
|
|
210
|
+
title: 'Locating Identity...',
|
|
211
|
+
msg: 'Locating account on server...',
|
|
212
|
+
animationEmoji: '🔍'
|
|
213
|
+
});
|
|
214
|
+
try {
|
|
215
|
+
// 1. Look up primary identity keystone for this email
|
|
216
|
+
const resLookup = await getApiBridge().lookupPrimaryKeystone({ identifier: email });
|
|
217
|
+
if (!resLookup.success || !resLookup.tjpAddr) {
|
|
218
|
+
this.setStatus('No account found for this email address. Please click "Create Account" below.', 'error');
|
|
126
219
|
return;
|
|
127
220
|
}
|
|
128
|
-
this.
|
|
221
|
+
this._stagedEmail = email;
|
|
222
|
+
this._stagedPrimaryTjpAddr = resLookup.tjpAddr;
|
|
223
|
+
// 2. Dispatch 6-digit verification code to email
|
|
224
|
+
this.setStatus('Dispatching 6-digit verification code...', 'info');
|
|
129
225
|
this.showBusy({
|
|
130
226
|
isBusy: true,
|
|
131
|
-
title: '
|
|
132
|
-
msg: '
|
|
133
|
-
animationEmoji: '
|
|
227
|
+
title: 'Sending Code...',
|
|
228
|
+
msg: 'Dispatching 6-digit verification code to your email...',
|
|
229
|
+
animationEmoji: '✉️'
|
|
134
230
|
});
|
|
135
|
-
const
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
231
|
+
const resCode = await getApiBridge().postVerificationCode({
|
|
232
|
+
email,
|
|
233
|
+
primaryTjpAddr: this._stagedPrimaryTjpAddr
|
|
234
|
+
});
|
|
235
|
+
if (!resCode.success) {
|
|
236
|
+
this.setStatus(resCode.message || 'Failed to dispatch verification code.', 'error');
|
|
237
|
+
return;
|
|
139
238
|
}
|
|
140
|
-
//
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
239
|
+
// 3. Transition UI to 6-digit PIN input prompt
|
|
240
|
+
elements.sectionCodeVerification.classList.remove('hidden');
|
|
241
|
+
elements.sectionCodeVerification.scrollIntoView({ behavior: 'smooth' });
|
|
242
|
+
this.setStatus(`Verification code sent to ${email}! Enter the 6-digit code above to continue.`, 'info');
|
|
243
|
+
elements.inputCode?.focus();
|
|
244
|
+
}
|
|
245
|
+
catch (error) {
|
|
246
|
+
const emsg = extractErrorMsg(error);
|
|
247
|
+
console.error(`${lc} ${emsg}`, error);
|
|
248
|
+
this.setStatus(`Error: ${emsg}`, 'error');
|
|
249
|
+
}
|
|
250
|
+
finally {
|
|
251
|
+
elements.btnContinueEmail.disabled = false;
|
|
252
|
+
elements.btnContinueEmail.textContent = 'Continue with Email';
|
|
253
|
+
this.showBusy({ isBusy: false });
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
async handleVerifyCode() {
|
|
257
|
+
const lc = `${this.lc}[${this.handleVerifyCode.name}]`;
|
|
258
|
+
const elements = this.elements;
|
|
259
|
+
const code = elements.inputCode?.value?.trim();
|
|
260
|
+
const email = this._stagedEmail;
|
|
261
|
+
const primaryTjpAddr = this._stagedPrimaryTjpAddr;
|
|
262
|
+
if (!email || !primaryTjpAddr) {
|
|
263
|
+
this.setStatus('Please enter your email and request a code first.', 'error');
|
|
264
|
+
return;
|
|
265
|
+
}
|
|
266
|
+
if (!code || code.length !== 6) {
|
|
267
|
+
elements.inputCode.classList.add('invalid');
|
|
268
|
+
this.setStatus('Please enter a 6-digit verification code.', 'error');
|
|
269
|
+
return;
|
|
270
|
+
}
|
|
271
|
+
elements.btnVerifyCode.disabled = true;
|
|
272
|
+
elements.btnVerifyCode.textContent = 'Verifying...';
|
|
273
|
+
this.setStatus('Verifying 6-digit code...', 'info');
|
|
274
|
+
this.showBusy({
|
|
275
|
+
isBusy: true,
|
|
276
|
+
title: 'Verifying Code...',
|
|
277
|
+
msg: 'Validating verification code with server...',
|
|
278
|
+
animationEmoji: '🔐'
|
|
279
|
+
});
|
|
280
|
+
try {
|
|
281
|
+
// First, attempt Custodian Genesis / Sign-In resolution
|
|
282
|
+
const resGenesis = await getApiBridge().postCustodianGenesis({ email, code });
|
|
283
|
+
if (resGenesis.success && resGenesis.primaryTjpAddr && resGenesis.custodianReqs) {
|
|
284
|
+
// -------------------------------------------------------------
|
|
285
|
+
// Path A: Custodial Account (100% Passwordless)
|
|
286
|
+
// -------------------------------------------------------------
|
|
287
|
+
const { primaryTjpAddr, custodianReqs, primaryKeystone, primaryGraph } = resGenesis;
|
|
288
|
+
const username = email ? email.split('@')[0] : 'user';
|
|
289
|
+
await this.completeCustodialDelegateOnboarding({
|
|
290
|
+
primaryTjpAddr,
|
|
291
|
+
custodianReqs,
|
|
292
|
+
fallbackPrimaryKeystone: primaryKeystone,
|
|
293
|
+
fallbackPrimaryGraph: primaryGraph,
|
|
294
|
+
email,
|
|
295
|
+
username,
|
|
296
|
+
});
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
299
|
+
// -----------------------------------------------------------------
|
|
300
|
+
// Path B: Sovereign Account (Fetch Verified Graph, Reveal Password Prompt)
|
|
301
|
+
// -----------------------------------------------------------------
|
|
302
|
+
const resVerify = await getApiBridge().postVerifyCode({ email, primaryTjpAddr, code });
|
|
303
|
+
if (!resVerify.success) {
|
|
304
|
+
this.setStatus(resVerify.message || 'Verification failed. Please check the code.', 'error');
|
|
305
|
+
return;
|
|
306
|
+
}
|
|
307
|
+
this.setStatus('Retrieving identity graph from server...', 'info');
|
|
308
|
+
this.showBusy({
|
|
309
|
+
isBusy: true,
|
|
310
|
+
title: 'Loading Identity Graph...',
|
|
311
|
+
msg: 'Fetching cryptographic identity timeline from server...',
|
|
312
|
+
animationEmoji: '📥'
|
|
145
313
|
});
|
|
146
|
-
const
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
const found = res?.ibGibs?.[0];
|
|
151
|
-
if (found && found.data) {
|
|
152
|
-
const username = found.data.frameDetails?.username ?? '';
|
|
153
|
-
const emails = found.data.emails ?? found.data.frameDetails?.emails ?? [];
|
|
154
|
-
if (username.toLowerCase() === identifier.toLowerCase() || emails.some((e) => e.toLowerCase() === identifier.toLowerCase())) {
|
|
155
|
-
primaryKeystone = found;
|
|
156
|
-
break;
|
|
157
|
-
}
|
|
158
|
-
}
|
|
314
|
+
const resGraph = await getApiBridge().getKeystoneGraph(primaryTjpAddr);
|
|
315
|
+
if (!resGraph.success || !resGraph.graph) {
|
|
316
|
+
this.setStatus('Failed to retrieve identity graph from server.', 'error');
|
|
317
|
+
return;
|
|
159
318
|
}
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
const
|
|
169
|
-
|
|
170
|
-
const graphIbGibs = Object.values(resGraph.graph);
|
|
171
|
-
await metaspace.put({ ibGibs: graphIbGibs, space });
|
|
172
|
-
// Order timelines by TJP and register each frame in ascending order
|
|
173
|
-
const timelinesMap = getTimelinesGroupedByTjp({ ibGibs: graphIbGibs });
|
|
174
|
-
for (const tjpKey of Object.keys(timelinesMap)) {
|
|
175
|
-
const timeline = timelinesMap[tjpKey];
|
|
176
|
-
for (const frame of timeline) {
|
|
177
|
-
await metaspace.registerNewIbGib({ ibGib: frame, space });
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
// Set primaryKeystone to the latest frame in the domain timeline
|
|
181
|
-
const domainTimeline = timelinesMap[domainAddr] || timelinesMap[Object.keys(timelinesMap)[0]];
|
|
182
|
-
if (domainTimeline && domainTimeline.length > 0) {
|
|
183
|
-
primaryKeystone = domainTimeline[domainTimeline.length - 1];
|
|
184
|
-
}
|
|
185
|
-
else {
|
|
186
|
-
primaryKeystone = fetchedKeystone;
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
else {
|
|
190
|
-
await metaspace.put({ ibGibs: [fetchedKeystone], space });
|
|
191
|
-
await metaspace.registerNewIbGib({ ibGib: fetchedKeystone, space });
|
|
192
|
-
primaryKeystone = fetchedKeystone;
|
|
319
|
+
this._stagedPrimaryGraph = resGraph.graph;
|
|
320
|
+
const metaspace = await getGlobalMetaspace_waitIfNeeded();
|
|
321
|
+
const space = await metaspace.getLocalUserSpace({});
|
|
322
|
+
if (space) {
|
|
323
|
+
const graphIbGibs = Object.values(resGraph.graph);
|
|
324
|
+
await metaspace.put({ ibGibs: graphIbGibs, space });
|
|
325
|
+
const timelinesMap = getTimelinesGroupedByTjp({ ibGibs: graphIbGibs });
|
|
326
|
+
for (const tjpKey of Object.keys(timelinesMap)) {
|
|
327
|
+
for (const frame of timelinesMap[tjpKey]) {
|
|
328
|
+
await metaspace.registerNewIbGib({ ibGib: frame, space });
|
|
193
329
|
}
|
|
194
330
|
}
|
|
331
|
+
const domainTimeline = timelinesMap[primaryTjpAddr] || timelinesMap[Object.keys(timelinesMap)[0]];
|
|
332
|
+
if (domainTimeline && domainTimeline.length > 0) {
|
|
333
|
+
this._stagedPrimaryKeystone = domainTimeline[domainTimeline.length - 1];
|
|
334
|
+
}
|
|
195
335
|
}
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
336
|
+
// Hide OTP section, reveal Sovereign Master Password prompt
|
|
337
|
+
elements.sectionCodeVerification.classList.add('hidden');
|
|
338
|
+
elements.sectionSovereignPassword.classList.remove('hidden');
|
|
339
|
+
elements.sectionSovereignPassword.scrollIntoView({ behavior: 'smooth' });
|
|
340
|
+
this.setStatus('2FA verified! Please enter your master password to authorize this device.', 'info');
|
|
341
|
+
elements.inputPassword?.focus();
|
|
342
|
+
}
|
|
343
|
+
catch (error) {
|
|
344
|
+
const emsg = extractErrorMsg(error);
|
|
345
|
+
console.error(`${lc} ${emsg}`, error);
|
|
346
|
+
this.setStatus(`Error: ${emsg}`, 'error');
|
|
347
|
+
}
|
|
348
|
+
finally {
|
|
349
|
+
elements.btnVerifyCode.disabled = false;
|
|
350
|
+
elements.btnVerifyCode.textContent = 'Verify Code';
|
|
351
|
+
this.showBusy({ isBusy: false });
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
// =========================================================================
|
|
355
|
+
// DRY Custodial Delegation Helper (Shared by SSO & Email OTP)
|
|
356
|
+
// =========================================================================
|
|
357
|
+
async completeCustodialDelegateOnboarding({ primaryTjpAddr, custodianReqs, fallbackPrimaryKeystone, fallbackPrimaryGraph, email, username, }) {
|
|
358
|
+
const lc = `${this.lc}[${this.completeCustodialDelegateOnboarding.name}]`;
|
|
359
|
+
const deviceTag = this.getDeviceTag();
|
|
360
|
+
this.setStatus('Deriving local device delegate keys...', 'info');
|
|
361
|
+
this.showBusy({
|
|
362
|
+
isBusy: true,
|
|
363
|
+
title: 'Creating Sync Delegate...',
|
|
364
|
+
msg: `Generating cryptographic sync delegate keys for ${deviceTag}...`,
|
|
365
|
+
animationEmoji: '🔑'
|
|
366
|
+
});
|
|
367
|
+
const metaspace = await getGlobalMetaspace_waitIfNeeded();
|
|
368
|
+
const space = await metaspace.getLocalUserSpace({ lock: false });
|
|
369
|
+
if (!space) {
|
|
370
|
+
this.setStatus('Could not access local user space.', 'error');
|
|
371
|
+
return;
|
|
372
|
+
}
|
|
373
|
+
const delegateSecret = await getOrCreateDeviceDelegateSecret({ primaryTjpAddr, deviceTag });
|
|
374
|
+
const userAlias = username || (email ? email.split('@')[0] : 'user');
|
|
375
|
+
const custodianSyncProfileName = process.env.KEYSTONE_PROFILE_CUSTODIAN_SYNC;
|
|
376
|
+
if (!custodianSyncProfileName || !custodianSyncProfileName.trim()) {
|
|
377
|
+
throw new Error('KEYSTONE_PROFILE_CUSTODIAN_SYNC environment variable is missing. Expected valid profile name like "sync-profile.dev" or "sync-profile.prod". (E: 4d5e6f7890123456789abcdef4)');
|
|
378
|
+
}
|
|
379
|
+
const delegateKeystone = await createSyncDelegateKeystone({
|
|
380
|
+
profileName: custodianSyncProfileName.trim(),
|
|
381
|
+
username: userAlias,
|
|
382
|
+
deviceTag,
|
|
383
|
+
primaryTjpAddr,
|
|
384
|
+
delegateSecret,
|
|
385
|
+
custodianReqs,
|
|
386
|
+
additionalFrameDetails: { client: 'space-gib-web' },
|
|
387
|
+
metaspace,
|
|
388
|
+
space,
|
|
389
|
+
});
|
|
390
|
+
this.setStatus('Registering device delegate with custodian server...', 'info');
|
|
391
|
+
this.showBusy({
|
|
392
|
+
isBusy: true,
|
|
393
|
+
title: 'Registering Delegate...',
|
|
394
|
+
msg: 'Registering local delegate with custodian server...',
|
|
395
|
+
animationEmoji: '🔄'
|
|
396
|
+
});
|
|
397
|
+
const resRegister = await getApiBridge().postCustodianRegisterDelegate({ delegateKeystone });
|
|
398
|
+
if (!resRegister.success) {
|
|
399
|
+
this.setStatus(resRegister.message || 'Custodian delegate registration failed.', 'error');
|
|
400
|
+
return;
|
|
401
|
+
}
|
|
402
|
+
// Finalize onboarding into local space & special index
|
|
403
|
+
await this.finalizeDeviceOnboarding({
|
|
404
|
+
primaryKeystone: resRegister.evolvedPrimaryKeystone || fallbackPrimaryKeystone,
|
|
405
|
+
primaryGraph: resRegister.primaryGraph || fallbackPrimaryGraph,
|
|
406
|
+
delegateKeystone,
|
|
407
|
+
primaryTjpAddr,
|
|
408
|
+
delegateSecret,
|
|
409
|
+
makeActive: this.elements?.checkboxMakeActive?.checked ?? true
|
|
410
|
+
});
|
|
411
|
+
}
|
|
412
|
+
// =========================================================================
|
|
413
|
+
// Flow 3: Sovereign Master Password Unlock & Local Evolution
|
|
414
|
+
// =========================================================================
|
|
415
|
+
async handleAuthorizePassword() {
|
|
416
|
+
const lc = `${this.lc}[${this.handleAuthorizePassword.name}]`;
|
|
417
|
+
const elements = this.elements;
|
|
418
|
+
const masterSecret = elements.inputPassword?.value?.trim();
|
|
419
|
+
const primaryKeystone = this._stagedPrimaryKeystone;
|
|
420
|
+
const primaryTjpAddr = this._stagedPrimaryTjpAddr;
|
|
421
|
+
if (!masterSecret) {
|
|
422
|
+
elements.inputPassword.classList.add('invalid');
|
|
423
|
+
this.setStatus('Please enter your master password.', 'error');
|
|
424
|
+
return;
|
|
425
|
+
}
|
|
426
|
+
if (!primaryKeystone || !primaryTjpAddr) {
|
|
427
|
+
this.setStatus('Missing staged primary identity context. Please restart sign-in.', 'error');
|
|
428
|
+
return;
|
|
429
|
+
}
|
|
430
|
+
elements.btnAuthorizePassword.disabled = true;
|
|
431
|
+
elements.btnAuthorizePassword.textContent = 'Authorizing...';
|
|
432
|
+
this.setStatus('Verifying master password locally...', 'info');
|
|
433
|
+
this.showBusy({
|
|
434
|
+
isBusy: true,
|
|
435
|
+
title: 'Verifying Password...',
|
|
436
|
+
msg: 'Verifying cryptographic password locally in browser...',
|
|
437
|
+
animationEmoji: '🔐'
|
|
438
|
+
});
|
|
439
|
+
try {
|
|
440
|
+
const keystoneService = new KeystoneService_V1();
|
|
441
|
+
const isValid = await keystoneService.verifySigningSecret({
|
|
442
|
+
keystoneIbGib: primaryKeystone,
|
|
443
|
+
signingSecret: masterSecret,
|
|
444
|
+
poolId: 'manage'
|
|
445
|
+
});
|
|
446
|
+
if (!isValid) {
|
|
447
|
+
elements.inputPassword.classList.add('invalid');
|
|
448
|
+
this.setStatus('Incorrect master password. Please try again.', 'error');
|
|
202
449
|
return;
|
|
203
450
|
}
|
|
204
|
-
const
|
|
205
|
-
|
|
206
|
-
this.setStatus(
|
|
451
|
+
const deviceTag = this.getDeviceTag();
|
|
452
|
+
const primaryUsername = primaryKeystone.data?.frameDetails?.username || 'user';
|
|
453
|
+
this.setStatus(`Generating local sync delegate for ${deviceTag}...`, 'info');
|
|
207
454
|
this.showBusy({
|
|
208
455
|
isBusy: true,
|
|
209
456
|
title: 'Generating Keys...',
|
|
210
|
-
msg:
|
|
457
|
+
msg: `Generating cryptographic delegate keys for ${deviceTag}...`,
|
|
211
458
|
animationEmoji: '🔑'
|
|
212
459
|
});
|
|
213
|
-
const
|
|
214
|
-
|
|
215
|
-
|
|
460
|
+
const metaspace = await getGlobalMetaspace_waitIfNeeded();
|
|
461
|
+
const space = await metaspace.getLocalUserSpace({});
|
|
462
|
+
if (!space) {
|
|
463
|
+
throw new Error('No default space found in metaspace.');
|
|
216
464
|
}
|
|
217
465
|
const delegateSecret = await deriveDelegateSecret({ masterSecret });
|
|
218
|
-
|
|
219
|
-
|
|
466
|
+
await setDeviceDelegateSecret({ primaryTjpAddr, delegateSecret });
|
|
467
|
+
const sovereignSyncProfileName = process.env.KEYSTONE_PROFILE_SOVEREIGN_SYNC;
|
|
468
|
+
if (!sovereignSyncProfileName || !sovereignSyncProfileName.trim()) {
|
|
220
469
|
throw new Error('KEYSTONE_PROFILE_SOVEREIGN_SYNC environment variable is missing. Expected valid profile name like "sync-profile.dev" or "sync-profile.prod". (E: 8f9e0a1b2c3d4e5f67890123456789ab)');
|
|
221
470
|
}
|
|
222
|
-
const
|
|
223
|
-
.
|
|
224
|
-
|
|
225
|
-
.withDetails({
|
|
226
|
-
client: 'space-gib-web',
|
|
227
|
-
role: 'sync-delegate',
|
|
471
|
+
const delegateKeystone = await createSyncDelegateKeystone({
|
|
472
|
+
profileName: sovereignSyncProfileName.trim(),
|
|
473
|
+
username: primaryUsername,
|
|
228
474
|
deviceTag,
|
|
229
475
|
primaryTjpAddr,
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
const keystoneService = new KeystoneService_V1();
|
|
233
|
-
const delegateKeystone = await keystoneService.genesis({
|
|
234
|
-
masterSecret: delegateSecret,
|
|
235
|
-
configs: delegateConfigs,
|
|
476
|
+
delegateSecret,
|
|
477
|
+
additionalFrameDetails: { client: 'space-gib-web' },
|
|
236
478
|
metaspace,
|
|
237
479
|
space,
|
|
238
|
-
frameDetails: delegateBuilder.getFrameDetails()
|
|
239
480
|
});
|
|
240
|
-
|
|
241
|
-
console.log(`${lc} Local device delegate generated: ${delegateAddr}`);
|
|
242
|
-
await delay(humanEyeDelay);
|
|
243
|
-
// 3. Evolve parent primary keystone to register delegate
|
|
244
|
-
this.setStatus("Registering device on primary identity...", "info");
|
|
481
|
+
this.setStatus('Evolving primary identity to register delegate...', 'info');
|
|
245
482
|
this.showBusy({
|
|
246
483
|
isBusy: true,
|
|
247
484
|
title: 'Registering Device...',
|
|
248
|
-
msg: '
|
|
249
|
-
animationEmoji: '
|
|
485
|
+
msg: 'Evolving primary identity to register new device delegate...',
|
|
486
|
+
animationEmoji: '⚙️'
|
|
250
487
|
});
|
|
251
|
-
const
|
|
488
|
+
const evolvedParentKeystone = await keystoneService.registerDelegate({
|
|
252
489
|
parentKeystone: primaryKeystone,
|
|
253
490
|
delegateKeystone,
|
|
254
491
|
masterSecret,
|
|
492
|
+
allowedVerbs: [KEYSTONE_VERB_SYNC, KEYSTONE_VERB_CONNECT],
|
|
255
493
|
metaspace,
|
|
256
494
|
space,
|
|
257
|
-
|
|
258
|
-
deviceName: deviceTag,
|
|
495
|
+
deviceName: deviceTag
|
|
259
496
|
});
|
|
260
|
-
|
|
261
|
-
console.log(`${lc} Primary identity evolved with delegate: ${evolvedPrimaryAddr}`);
|
|
262
|
-
await delay(humanEyeDelay);
|
|
263
|
-
// 4. Post evolved primary keystone + delegate to server
|
|
264
|
-
this.setStatus("Syncing device pairing to server...", "info");
|
|
497
|
+
this.setStatus('Publishing evolved identity to server...', 'info');
|
|
265
498
|
this.showBusy({
|
|
266
499
|
isBusy: true,
|
|
267
500
|
title: 'Syncing to Server...',
|
|
268
|
-
msg: '
|
|
269
|
-
animationEmoji: '
|
|
501
|
+
msg: 'Publishing evolved identity and delegate to server...',
|
|
502
|
+
animationEmoji: '⚡'
|
|
270
503
|
});
|
|
271
|
-
// const domainAddr = primaryTjpAddr ? getIbGibAddr({ ibGib: primaryKeystone }) : getIbGibAddr({ ibGib: primaryKeystone });
|
|
272
504
|
const resEvolve = await getApiBridge().putEvolveKeystone({
|
|
273
505
|
domainAddr: primaryTjpAddr,
|
|
274
|
-
keystoneIbGib:
|
|
506
|
+
keystoneIbGib: evolvedParentKeystone,
|
|
275
507
|
relatedKeystoneIbGibs: [delegateKeystone]
|
|
276
508
|
});
|
|
277
509
|
if (!resEvolve.success) {
|
|
278
|
-
|
|
279
|
-
console.error(`${lc} ${msg}`);
|
|
280
|
-
this.setStatus(msg, "error");
|
|
281
|
-
this.showBusy({ isBusy: false });
|
|
282
|
-
this.setButtonLoading(false);
|
|
283
|
-
return;
|
|
510
|
+
console.warn(`${lc} Warning syncing delegate evolution to server: ${resEvolve.message}`);
|
|
284
511
|
}
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
type: "keystones",
|
|
294
|
-
rel8nInfos: [
|
|
295
|
-
{
|
|
296
|
-
rel8nName: "keystone",
|
|
297
|
-
ibGibs: [
|
|
298
|
-
toDto({ ibGib: evolvedPrimaryKeystone }),
|
|
299
|
-
toDto({ ibGib: delegateKeystone })
|
|
300
|
-
],
|
|
301
|
-
},
|
|
302
|
-
],
|
|
303
|
-
metaspace,
|
|
304
|
-
space,
|
|
512
|
+
await this.finalizeDeviceOnboarding({
|
|
513
|
+
primaryKeystone: evolvedParentKeystone,
|
|
514
|
+
primaryGraph: this._stagedPrimaryGraph,
|
|
515
|
+
delegateKeystone,
|
|
516
|
+
primaryTjpAddr,
|
|
517
|
+
masterSecret,
|
|
518
|
+
delegateSecret,
|
|
519
|
+
makeActive: elements.checkboxMakeActive?.checked ?? true
|
|
305
520
|
});
|
|
306
|
-
// 6. Set as active identity if checked
|
|
307
|
-
if (this.elements.checkboxMakeActive.checked) {
|
|
308
|
-
window.dispatchEvent(new CustomEvent(EVENT_IBGIB_IDENTITY_REQUEST_CHANGE, {
|
|
309
|
-
detail: { activeIdentityAddr: evolvedPrimaryAddr },
|
|
310
|
-
bubbles: true,
|
|
311
|
-
composed: true
|
|
312
|
-
}));
|
|
313
|
-
}
|
|
314
|
-
this.showBusy({ isBusy: false });
|
|
315
|
-
this.setButtonLoading(false);
|
|
316
|
-
this.elements.delegateAddrEl.innerText = delegateAddr;
|
|
317
|
-
this.elements.resultAreaEl.classList.remove('collapsed');
|
|
318
|
-
this.setStatus("Device successfully paired!", "success");
|
|
319
521
|
}
|
|
320
522
|
catch (error) {
|
|
321
|
-
|
|
322
|
-
|
|
523
|
+
const emsg = extractErrorMsg(error);
|
|
524
|
+
console.error(`${lc} ${emsg}`, error);
|
|
525
|
+
this.setStatus(`Error: ${emsg}`, 'error');
|
|
526
|
+
}
|
|
527
|
+
finally {
|
|
528
|
+
elements.btnAuthorizePassword.disabled = false;
|
|
529
|
+
elements.btnAuthorizePassword.textContent = 'Authorize & Pair Device';
|
|
323
530
|
this.showBusy({ isBusy: false });
|
|
324
|
-
this.setButtonLoading(false);
|
|
325
531
|
}
|
|
326
532
|
}
|
|
533
|
+
// =========================================================================
|
|
534
|
+
// Finalize Device Onboarding & Navigation
|
|
535
|
+
// =========================================================================
|
|
536
|
+
async finalizeDeviceOnboarding({ primaryKeystone, primaryGraph, delegateKeystone, primaryTjpAddr, masterSecret, delegateSecret, makeActive = true, }) {
|
|
537
|
+
const lc = `${this.lc}[${this.finalizeDeviceOnboarding.name}]`;
|
|
538
|
+
const humanEyeDelay = 500;
|
|
539
|
+
const metaspace = await getGlobalMetaspace_waitIfNeeded();
|
|
540
|
+
if (!metaspace) {
|
|
541
|
+
throw new Error('Metaspace not found');
|
|
542
|
+
}
|
|
543
|
+
const space = await metaspace.getLocalUserSpace({});
|
|
544
|
+
if (!space) {
|
|
545
|
+
throw new Error('Local user space not found');
|
|
546
|
+
}
|
|
547
|
+
// 1. Put primary frames and delegate into local space
|
|
548
|
+
if (primaryGraph) {
|
|
549
|
+
const graphIbGibs = Object.values(primaryGraph);
|
|
550
|
+
await metaspace.put({ ibGibs: graphIbGibs, space });
|
|
551
|
+
const timelinesMap = getTimelinesGroupedByTjp({ ibGibs: graphIbGibs });
|
|
552
|
+
for (const tjpKey of Object.keys(timelinesMap)) {
|
|
553
|
+
for (const frame of timelinesMap[tjpKey]) {
|
|
554
|
+
await metaspace.registerNewIbGib({ ibGib: frame, space });
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
else if (primaryKeystone) {
|
|
559
|
+
await metaspace.put({ ibGibs: [primaryKeystone], space });
|
|
560
|
+
await metaspace.registerNewIbGib({ ibGib: primaryKeystone, space });
|
|
561
|
+
}
|
|
562
|
+
await metaspace.put({ ibGibs: [delegateKeystone], space });
|
|
563
|
+
await metaspace.registerNewIbGib({ ibGib: delegateKeystone, space });
|
|
564
|
+
// 2. Register in keystones special index
|
|
565
|
+
const ibGibsToRegister = [toDto({ ibGib: delegateKeystone })];
|
|
566
|
+
if (primaryKeystone) {
|
|
567
|
+
ibGibsToRegister.unshift(toDto({ ibGib: primaryKeystone }));
|
|
568
|
+
}
|
|
569
|
+
await updateSpecialIndex({
|
|
570
|
+
type: 'keystones',
|
|
571
|
+
rel8nInfos: [{ rel8nName: 'keystone', ibGibs: ibGibsToRegister }],
|
|
572
|
+
metaspace,
|
|
573
|
+
space
|
|
574
|
+
});
|
|
575
|
+
// 3. Make active if checked
|
|
576
|
+
if (makeActive) {
|
|
577
|
+
window.dispatchEvent(new CustomEvent(EVENT_IBGIB_IDENTITY_REQUEST_CHANGE, {
|
|
578
|
+
detail: { activeIdentityAddr: primaryTjpAddr },
|
|
579
|
+
bubbles: true,
|
|
580
|
+
composed: true
|
|
581
|
+
}));
|
|
582
|
+
}
|
|
583
|
+
// 4. Update UI results
|
|
584
|
+
const delegateAddr = getIbGibAddr({ ibGib: delegateKeystone });
|
|
585
|
+
if (this.elements?.delegateAddrEl) {
|
|
586
|
+
this.elements.delegateAddrEl.textContent = delegateAddr;
|
|
587
|
+
}
|
|
588
|
+
if (this.elements?.resultAreaEl) {
|
|
589
|
+
this.elements.resultAreaEl.classList.remove('collapsed');
|
|
590
|
+
}
|
|
591
|
+
const successMsg = 'Device paired and identity activated successfully!';
|
|
592
|
+
this.setStatus(successMsg, 'success');
|
|
593
|
+
this.showBusy({
|
|
594
|
+
isBusy: true,
|
|
595
|
+
title: 'Success!',
|
|
596
|
+
msg: successMsg,
|
|
597
|
+
animationEmoji: '✅'
|
|
598
|
+
});
|
|
599
|
+
await delay(humanEyeDelay);
|
|
600
|
+
// 4.5 Sanitize password input fields from DOM
|
|
601
|
+
if (this.elements?.inputPassword) {
|
|
602
|
+
this.elements.inputPassword.value = '';
|
|
603
|
+
}
|
|
604
|
+
// 5. Navigate to identity manager
|
|
605
|
+
window.dispatchEvent(new CustomEvent(EVENT_IBGIB_UI_MESSAGE, {
|
|
606
|
+
detail: { action: 'show-identity-manager', addr: primaryTjpAddr },
|
|
607
|
+
bubbles: true,
|
|
608
|
+
composed: true
|
|
609
|
+
}));
|
|
610
|
+
this.showBusy({ isBusy: false });
|
|
611
|
+
}
|
|
612
|
+
async renderUI() {
|
|
613
|
+
// No complex rendering needed
|
|
614
|
+
}
|
|
615
|
+
async handleContextUpdated() {
|
|
616
|
+
await this.renderUI();
|
|
617
|
+
}
|
|
327
618
|
}
|
|
328
619
|
//# sourceMappingURL=add-device.mjs.map
|