@dotrino/identity 0.47.0 → 0.48.1

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dotrino/identity",
3
- "version": "0.47.0",
3
+ "version": "0.48.1",
4
4
  "description": "Identidad y rating de usuarios compartidos entre apps de Dotrino (vault iframe + postMessage)",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
package/src/node.js CHANGED
@@ -122,7 +122,11 @@ export class Identity {
122
122
  this._core = await createIdentityCore({
123
123
  kv: fileKv(path.join(this._dir, 'identity.json'), this._atRest),
124
124
  peers: filePeers(path.join(this._dir, 'peers.json')),
125
- makeSync: null
125
+ makeSync: null,
126
+ // Aquí las cuentas las lleva quien hospeda (el daemon del vault tiene su propio
127
+ // registro de perfiles): que una expulsión borre una por debajo le dejaría el suyo
128
+ // apuntando a algo que ya no existe. Se borra el enlace y el acta, nada más.
129
+ removeAccountOnExpulsion: false
126
130
  })
127
131
  this._core.onSyncStatus((payload) => this._emit('sync', payload))
128
132
  this._core.onVaultEvent((payload) => this._emit('vault', payload))
package/vault/core.js CHANGED
@@ -158,7 +158,7 @@ function sanitizeProfilePatch (patch = {}) {
158
158
  return out
159
159
  }
160
160
 
161
- export async function createIdentityCore ({ kv: rawKv, peers, makeSync = null, keyStore = null, sessionKv = null }) {
161
+ export async function createIdentityCore ({ kv: rawKv, peers, makeSync = null, keyStore = null, sessionKv = null, removeAccountOnExpulsion = true }) {
162
162
  const {
163
163
  initPeerStorage, loadPeers, savePeers, setPeersDirect, upsertPeer, onDirty
164
164
  } = peers
@@ -342,19 +342,65 @@ export async function createIdentityCore ({ kv: rawKv, peers, makeSync = null, k
342
342
 
343
343
  /**
344
344
  * BORRADO por revocación. Solo lo dispara un `vault.revoked` FIRMADO por la maestra
345
- * pineada (lo verifica `remote.js` antes de llamar aquí). Emite 'revoked' →
346
- * `@dotrino/store` borra el store de ESTE perfil (los demás quedan intactos).
345
+ * pineada (lo verifica `remote.js` antes de llamar aquí).
346
+ *
347
+ * Se va la CUENTA ENTERA, no solo el enlace. Una cuenta que vivía en una bóveda y de la
348
+ * que te echaron no es «una cuenta sin bóveda»: no es nada. Dejarla ahí —sin acta, sin
349
+ * certificado, con su llave— era dejar un cascarón que seguía saliendo en el conmutador
350
+ * de perfiles, con su nombre y su foto, sin poder hacer nada y sin que nadie supiera qué
351
+ * era ni cómo quitarlo.
352
+ *
353
+ * El aparato NUNCA se queda sin cuenta utilizable, que es la otra mitad: si había otras,
354
+ * se pasa a una de ellas; si esa era la única, se estrena una vacía. Así al terminar hay
355
+ * exactamente lo que tiene que haber: un dispositivo con su cuenta, listo para usarse o
356
+ * para volver a conectarse a una bóveda.
357
+ *
358
+ * Los pasos van EN ESTE ORDEN a propósito:
359
+ * 1. fuera el enlace y el acta (deja de poder hablar con la bóveda y de enseñar el
360
+ * perfil del que lo echaron);
361
+ * 2. 'revoked' → `@dotrino/store` borra el store de ESE perfil (apunta al id que ya
362
+ * tenía fijado, así que da igual lo que hagamos después con el perfil activo);
363
+ * 3. se borra la cuenta y se deja otra puesta;
364
+ * 4. 'account-removed' → la app RECARGA (multi-perfil no es reactivo, por diseño).
365
+ *
366
+ * El paso 3 es SOLO del navegador (`removeAccountOnExpulsion`). En Node las cuentas las
367
+ * lleva quien hospeda —el daemon del vault tiene su propio registro de perfiles, en
368
+ * `profiles.js`— y borrar una por debajo le dejaría el suyo apuntando a algo que ya no
369
+ * existe. Ahí se borra el enlace y el acta, y de la cuenta decide su dueño.
347
370
  */
348
371
  const wipeVaultLink = () => {
349
372
  try { kv.removeItem(VAULT_CERT_STORAGE); kv.removeItem(VAULT_DEVICE_STORAGE) } catch (_) {}
350
- // Y el ACTA. Sin esto el aparato borraba su enlace con la bóveda pero seguía
351
- // enseñando el perfil del que acababan de echarlo —con sus miembros y sus permisos—,
352
- // porque la copia local se quedaba ahí y ya no podía refrescarla (está revocado). El
353
- // dueño lo describió exacto: «el device ni por enterado de que está fuera».
354
- // Queda como lo que es: un dispositivo sin cuenta, que puede crear la suya o
355
- // emparejarse otra vez.
356
373
  try { kv.removeItem(ACTA_STORAGE) } catch (_) {}
357
374
  emitVault({ phase: 'revoked' })
375
+ // Sin `await`: quien llama es un manejador de mensajes que no espera nada (y si algo
376
+ // aquí revienta, el enlace y el acta ya se fueron, que es lo que no puede quedar).
377
+ if (removeAccountOnExpulsion) removeThisAccount().catch(() => {})
378
+ }
379
+
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
+ */
384
+ let removingAccount = false
385
+ async function removeThisAccount () {
386
+ // UNA VEZ. Puede haber varias peticiones en vuelo y a todas les llega el mismo aviso;
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.
389
+ if (removingAccount) return
390
+ removingAccount = true
391
+ const gone = currentPid
392
+ // Si esa era la única cuenta hay que estrenar otra ANTES de borrarla: `deleteProfile`
393
+ // se niega a dejar el dispositivo sin ninguna, y con razón. Crear ya deja la nueva
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) })
401
+ return
402
+ }
403
+ emitVault({ phase: 'account-removed', removed: gone, current: currentPid, created })
358
404
  }
359
405
 
360
406
  /**