@dotrino/identity 0.48.1 → 0.49.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/vault/core.js +51 -37
package/package.json
CHANGED
package/vault/core.js
CHANGED
|
@@ -350,18 +350,20 @@ export async function createIdentityCore ({ kv: rawKv, peers, makeSync = null, k
|
|
|
350
350
|
* de perfiles, con su nombre y su foto, sin poder hacer nada y sin que nadie supiera qué
|
|
351
351
|
* era ni cómo quitarlo.
|
|
352
352
|
*
|
|
353
|
-
* El aparato
|
|
354
|
-
*
|
|
355
|
-
*
|
|
356
|
-
*
|
|
353
|
+
* El aparato no se queda sin cuenta utilizable: al recargar, el arranque estrena una si
|
|
354
|
+
* no quedó ninguna, o entra en la primera que haya. Así al terminar hay exactamente lo
|
|
355
|
+
* que tiene que haber: un dispositivo con su cuenta, listo para usarse o para volver a
|
|
356
|
+
* conectarse a una bóveda.
|
|
357
357
|
*
|
|
358
358
|
* Los pasos van EN ESTE ORDEN a propósito:
|
|
359
359
|
* 1. fuera el enlace y el acta (deja de poder hablar con la bóveda y de enseñar el
|
|
360
360
|
* perfil del que lo echaron);
|
|
361
361
|
* 2. 'revoked' → `@dotrino/store` borra el store de ESE perfil (apunta al id que ya
|
|
362
362
|
* tenía fijado, así que da igual lo que hagamos después con el perfil activo);
|
|
363
|
-
* 3. se borra la cuenta
|
|
364
|
-
* 4. 'account-removed' → la app RECARGA (multi-perfil no es reactivo, por diseño)
|
|
363
|
+
* 3. se borra la cuenta;
|
|
364
|
+
* 4. 'account-removed' → la app RECARGA (multi-perfil no es reactivo, por diseño), y
|
|
365
|
+
* es el ARRANQUE quien deja puesta la que toque: si no queda ninguna estrena la
|
|
366
|
+
* primera, y si quedan cae a la primera de la lista. Esa decisión ya vivía ahí.
|
|
365
367
|
*
|
|
366
368
|
* El paso 3 es SOLO del navegador (`removeAccountOnExpulsion`). En Node las cuentas las
|
|
367
369
|
* lleva quien hospeda —el daemon del vault tiene su propio registro de perfiles, en
|
|
@@ -377,30 +379,51 @@ export async function createIdentityCore ({ kv: rawKv, peers, makeSync = null, k
|
|
|
377
379
|
if (removeAccountOnExpulsion) removeThisAccount().catch(() => {})
|
|
378
380
|
}
|
|
379
381
|
|
|
380
|
-
/**
|
|
381
|
-
* Borra la cuenta de ESTE dispositivo y deja otra activa (existente o recién creada).
|
|
382
|
-
* Se usa al ser expulsado; ver `wipeVaultLink`.
|
|
383
|
-
*/
|
|
382
|
+
/** Borra la cuenta de ESTE dispositivo al ser expulsado; ver `wipeVaultLink`. */
|
|
384
383
|
let removingAccount = false
|
|
385
384
|
async function removeThisAccount () {
|
|
386
|
-
// UNA VEZ
|
|
387
|
-
// sin este cerrojo, la segunda borraría la cuenta RECIÉN CREADA (para entonces
|
|
388
|
-
// `currentPid` ya es la nueva) en vez de la que echaron.
|
|
385
|
+
// UNA VEZ: puede haber varias peticiones en vuelo y a todas les llega el mismo aviso.
|
|
389
386
|
if (removingAccount) return
|
|
390
387
|
removingAccount = true
|
|
391
388
|
const gone = currentPid
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
// activa; si había otras, es el propio `deleteProfile` quien pasa a la primera.
|
|
395
|
-
const created = loadProfiles().filter((p) => p.id !== gone).length === 0
|
|
396
|
-
try {
|
|
397
|
-
if (created) await handlers.createProfile({ name: '' })
|
|
398
|
-
await handlers.deleteProfile({ id: gone })
|
|
399
|
-
} catch (e) {
|
|
400
|
-
emitVault({ phase: 'account-removed', removed: gone, current: currentPid, error: e?.message || String(e) })
|
|
389
|
+
try { await purgeProfile(gone) } catch (e) {
|
|
390
|
+
emitVault({ phase: 'account-removed', removed: gone, error: e?.message || String(e) })
|
|
401
391
|
return
|
|
402
392
|
}
|
|
403
|
-
|
|
393
|
+
// Y ya está: qué cuenta queda puesta lo resuelve el ARRANQUE al recargar, que es donde
|
|
394
|
+
// esa decisión ya vivía —si no queda ninguna estrena la primera, y si quedan cae a la
|
|
395
|
+
// primera de la lista—. No hace falta decidirlo aquí también.
|
|
396
|
+
emitVault({ phase: 'account-removed', removed: gone, current: currentPid })
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
/**
|
|
400
|
+
* Borra un perfil y todo lo suyo. Sin preguntas: el freno de «no te quedes sin ninguna»
|
|
401
|
+
* es de la interfaz y vive en `deleteProfile`.
|
|
402
|
+
*/
|
|
403
|
+
async function purgeProfile (id) {
|
|
404
|
+
const list = loadProfiles().filter((p) => p.id !== id)
|
|
405
|
+
saveProfiles(list)
|
|
406
|
+
for (const s of ['keypair', 'enc-keypair', 'me', 'nonces', 'delegations', 'revocations', 'vault.device', 'vault.cert', 'acta', 'renounced']) {
|
|
407
|
+
rawKv.removeItem(`dotrino.identity.p.${id}.${s}`)
|
|
408
|
+
}
|
|
409
|
+
// …y sus CryptoKeys no extractables del keyStore (IndexedDB).
|
|
410
|
+
if (keyStore) {
|
|
411
|
+
for (const s of ['keypair', 'enc-keypair']) {
|
|
412
|
+
try { await keyStore.remove(`dotrino.identity.p.${id}.${s}`) } catch (_) {}
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
if (currentPid === id) {
|
|
416
|
+
if (list.length) { currentPid = list[0].id; rawKv.setItem(CURRENT_STORAGE, currentPid) }
|
|
417
|
+
else {
|
|
418
|
+
// No queda ninguna: el arranque estrenará la primera. Se borra el puntero, pero
|
|
419
|
+
// `currentPid` se deja como está a propósito — sin él, el kv deja de estar
|
|
420
|
+
// scopeado y cualquier escritura de aquí a la recarga caería en las claves SIN
|
|
421
|
+
// namespace, que son justo las que el arranque adopta como «Perfil 1». Apuntando
|
|
422
|
+
// a un perfil que ya no existe, lo que se escriba es inerte.
|
|
423
|
+
rawKv.removeItem(CURRENT_STORAGE)
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
return { ok: true, current: currentPid }
|
|
404
427
|
}
|
|
405
428
|
|
|
406
429
|
/**
|
|
@@ -1260,22 +1283,13 @@ export async function createIdentityCore ({ kv: rawKv, peers, makeSync = null, k
|
|
|
1260
1283
|
return { id: e.id, name: e.name }
|
|
1261
1284
|
},
|
|
1262
1285
|
async deleteProfile ({ id } = {}) {
|
|
1263
|
-
|
|
1286
|
+
const list = loadProfiles()
|
|
1287
|
+
// El freno es de la INTERFAZ: el botón «Borrar» de la página de perfiles no puede
|
|
1288
|
+
// dejarte sin ninguna de un clic. La expulsión no pasa por aquí (ver `purgeProfile`):
|
|
1289
|
+
// ahí sí se va la última, porque no es un descuido sino que te echaron.
|
|
1264
1290
|
if (list.length <= 1) throw new Error('cannot delete the only profile')
|
|
1265
1291
|
if (!list.find((p) => p.id === id)) throw new Error('perfil no existe')
|
|
1266
|
-
|
|
1267
|
-
// Borrado directo del namespace del perfil (incluye su store del vault si lo tuviera).
|
|
1268
|
-
for (const s of ['keypair', 'enc-keypair', 'me', 'nonces', 'delegations', 'revocations', 'vault.device', 'vault.cert', 'acta', 'renounced']) {
|
|
1269
|
-
rawKv.removeItem(`dotrino.identity.p.${id}.${s}`)
|
|
1270
|
-
}
|
|
1271
|
-
// …y sus CryptoKeys no extractables del keyStore (IndexedDB).
|
|
1272
|
-
if (keyStore) {
|
|
1273
|
-
for (const s of ['keypair', 'enc-keypair']) {
|
|
1274
|
-
try { await keyStore.remove(`dotrino.identity.p.${id}.${s}`) } catch (_) {}
|
|
1275
|
-
}
|
|
1276
|
-
}
|
|
1277
|
-
if (currentPid === id) { currentPid = list[0].id; rawKv.setItem(CURRENT_STORAGE, currentPid) }
|
|
1278
|
-
return { ok: true, current: currentPid }
|
|
1292
|
+
return purgeProfile(id)
|
|
1279
1293
|
},
|
|
1280
1294
|
|
|
1281
1295
|
// ----- ACTA DE PERFIL -----
|