Repository navigation
v0.31.0
Changed
restoreAppSessionis removed from the root entry andpeekAppSessionis
exported in its place: the one reader of the persisted session, which never
wipes anything and leaves the expiry to the caller (isExpired).RestoredAppSessionis renamedAppSession(thepersistAppSessioninput:
the record plus the seed).snapshotWipeTargetstakessessionStoreDbNameandanonStoreDbName, and
SeedStorecarries thedbNameit is bound to, so a wipe deletes an injected
session store by its own name instead of assuming<dbName>-session.WipeTargetscarriessweepDbNamein place ofidbNamePrefixes. The sweep
no longer matches a broadcast-channel store:LocalStore.removeDatabase
removes single-instance, asinitopens, so RxDB opens no channel beside a
replica.seedStoreDbName(src/identity/seedStore.ts) andisReplicaDbNamebeside
dbNameForControllername the seed-store and replica database shapes once,
for the auth store that mints them and the sweep that matches them.
Fixed
-
The entity store's write verbs (
insert,update,upsert) commit to the
Map before the persist is awaited, and the persists of one id (remove
included) run in call order, so a read-modify-write issued before the previous
write has persisted merges onto what that one wrote instead of onto the
pre-write value and dropping it, and two unawaited upserts of a new id no
longer mint two rows. A persist that rejects puts the last persisted doc back,
and a hydrate that lands mid-write keeps showing the write.
defineDocumentApp'supdateandexportFileinherit this. -
The clear-data sweep matches only the database names this library derives from
itsdbName(<dbName>-session,<dbName>-anon,<dbName>-<8 hex>, and the
Dexie shells RxDB opens beside a replica), so clearing one app no longer wipes
a sibling app on the same origin whosedbNameextends it. -
LocalStore.initcloses the database it opened whenaddCollectionsrefuses
(a stored schema hash that no longer matches), instead of leaving it open with
its collections counted toward RxDB's process-wide cap. -
The adoption merge fills a missing
updatedAtand a missingwriterIdeach
on its own. A payload carryingupdatedAtbut nowriterIdkeeps its
original instant instead of being re-stamped to the adoption time and
outranking newer connected edits. -
An epoch-roster entry that names this app but fails its key-wrap check
(IntegrityErrorfrom@interop/was-client0.99.1) is routed as a tamper
signal: the local store records it as the collection's spent refusal, and the
shared-collection reader surfaces it as itself rather than as "not a
recipient". -
A boot over a persisted session whose grants have expired lands
reconnect
over the connected replica, with the seed intact and no replication started,
instead of wiping the session and landing an anonymouslocalreplica with no
reconnect banner. The replica opens on the cached descriptors alone, and
reconnect()renews the grants, persists the record, and starts sync. When
that expired restore fails withKeyUnwrapErrororIntegrityError(a cached
roster that no longer wraps a key to this app, or whose entry for it fails its
key-wrap check), the record is cleared, so the session lands a cleanlocal
rather than re-running the failed bring-up on every reload; any other failure
keeps the record for a reload or the reconnect to repair. -
A singleton duplicate that another client tombstoned while a hydrate was
reconciling it is no longer re-removed or warned about, anddeleteEntityno
longer rewrites a tombstone a pull landed under it. A winner tombstoned while
its losers were being removed is warned about. -
loginresolvesnullinstead of connecting when the session changed while
the wallet popup was open (a provider unmount with no re-mount, a
clearLocalData, aconnectWithGrants). A sync-controller stop that throws
no longer aborts the teardown, anddestroyresets the session state even
when its teardown throws. -
logoutclears the persisted session record before the anonymous replica
re-opens, so a re-open that fails no longer leaves the wallet seed on disk for
a reload to hot-restore. A failed re-open, inlogoutand in a connected
activation's fallback, now landslocalwith no replica and the failure on
errorinstead of keeping the previous status with no replica attached. -
AdoptDialogswallows the rejection of thelogin({ adopt })it starts. The
store already records a failed login onuseLogin().error, so a wallet
without App Connect support or a failed activation no longer also escapes the
dialog as an unhandled rejection. -
LocalStore.setDescriptorSourcere-arms the refresh guard, the in-flight
refresh, and the recorded refusals together, and a refresh that outlives its
source records nothing. -
A refresh whose fetched roster excludes this app is recorded and surfaces as
KeyUnwrapError; a refresh that fails on the wire is logged and left
un-spent. -
Per-collection cipher changes (the refresh,
applyRemoteDescriptor,
applyCollectionMeta) are serialized, and a descriptor that is an earlier
head of the log than the held one is not swapped back in. -
A public (plaintext) collection row whose body is an EDV envelope (an object
jwemember) no longer breaks the collection. The plaintext codec raises it
asIntegrityError, the same failure a mis-bound payload id is, so hydration
skips the row with a warning and hydrates the rest, and the conflict handler
settles a 412 on it by last-write-wins stamp instead of failing the
replication cycle on every retry. Any grant holder can write such a body, and
before this every hydrate of that collection rejected. -
The sync bootstrap guards its per-collection hook. A cipher rebuild that
throwsKeyUnwrapError(a roster rotated off this app since the cached
descriptor) now costs that one collection, which is warned about and left on
the cipher it opened with, while every other collection replicates. Before,
the throw rejectedstartWasSyncas a whole and no collection replicated
while the session stayedconnected. -
The adopted anonymous replica is removed before its seed is cleared. The
database goes through the wipe executor (so the Dexie shells RxDB's removal
leaves standing go with it), and the seed is cleared only when nothing failed.
Before, the seed was cleared first, so a failed removal (another tab holding
the database) orphaned the replica under a name no later run could derive. -
The connect transition's replica swap opens only for an
adopt: 'merge'login
fromlocal, the one case anonymous data is carried over. A write queued
during a login fromreconnectnow rejects instead of waiting on the swap and
landing in the newly connected account's replica as a create. -
reconnect()runs its post-popup epilogue (controller stop, record persist,
sync restart,connectedtransition) on the lifecycle chain and re-checks the
session first. Alogout,clearLocalData, ordestroycompleted while the
re-grant popup was open is no longer undone when the popup answers: the
renewed grants are discarded with a warning, nothing is persisted, and no sync
or expiry watch starts over the torn-down session. -
AudienceReader.pull()no longer reports a replay (or a listing read) cut by
a denial of a request that left under a since-replaced zcap as
snapshot: true. Such a cut pauses nothing but leaves the read partial; the
pull now returnssnapshot: falsewith the pages it did read, and the
checkpoint stays at the last fully read page. A consumer applying the partial
set as a snapshot would have dropped every post the cut kept it from reading. -
hydrateSingletontombstones a duplicate row with a conditional write on the
revision it read, anddeleteEntityremoves through RxDB's incremental write
queue, so a pull that updated or tombstoned the row after it was read (the
singleton decrypts can wait on a descriptor refresh) no longer fails the call
with a storage conflict. A duplicate whose row moved meanwhile is skipped with
a warning and reconciled by the next hydrate rather than removed unweighed,
and a winner whose row moved tombstones no loser at all.