Skip to content

Releases: interop-alliance/was-react

v0.23.0

Choose a tag to compare

@dmitrizagidulin dmitrizagidulin released this 09 Sep 19:32

Changed

  • The App Connect request vocabulary and the app-key credential issuer
    (serializedAppUrl, issueAppKeyCredential, the credential's pinned wire
    constants, the wallet-request types) are imported from
    @interop/wallet-request 0.1.0; wallet-core 0.70.0 no longer ships the
    request subpath. @interop/wallet-core stays for keyring and identity.

v0.22.2

Choose a tag to compare

@dmitrizagidulin dmitrizagidulin released this 08 Sep 22:12

Changed

  • The rxdb peer floor is ^17.5.0. Under 17.4 the Dexie storage never evicted
    a closed connection from its name cache, so logout({ wipe: true }) followed
    by a login in the same page reopened the replica on a closed Dexie instance
    and failed with DatabaseClosedError (rxdb #8793, fixed in 17.5.0).

v0.22.1

Choose a tag to compare

@dmitrizagidulin dmitrizagidulin released this 08 Sep 21:36

Changed

  • createWasSyncPort returns the client's port as is. The runtime putMeta
    probe and the feed-page cast are gone, since @interop/was-client 0.54.0
    types the port as what it implements (putMeta required, Json bodies), so a
    divergence at the seam is now a compile error.

  • setLogger also installs the logger into @interop/was-sync, so the
    replication driver's diagnostics (the controller core, conflict handling, push
    and pull) arrive under the app's namespace. The driver's per-call log option
    is gone with @interop/was-sync's adoption of the logging port.

  • The descriptor seam imports (EncryptionDescriptorCache,
    EncryptionDescriptorSource, DescriptorRefreshPolicy,
    createRefreshingEdvDocCipher) come from @interop/was-client/edv, their
    owner since @interop/wallet-core 0.69.0 stopped re-exporting them from
    ./descriptors.

  • WasRemoteStore.declareCollectionIndexes reads the public collection's
    description first and skips the PUT when the stored plaintext.indexes
    already matches the configured list, like the encryption-descriptor and
    blinded-index declarations, so a returning session writes nothing. A read that
    fails still leads to the PUT.

  • The collection-description PUT body is typed as the client's
    CollectionDescription rather than an untyped record, so a drift from the
    shape the server reads is a compile error.

Fixed

  • A public collection's equality indexes are declared under the collection
    description's plaintext member (plaintext: { indexes }), the shape
    was-teaching-server 0.26.0 reads (after @interop/storage-core 0.10.0). A
    top-level indexes is no longer read by the server, so the bootstrap's
    declaration was being accepted and dropped, and equality queries on a public
    collection failed with a 400.

  • test/node/conditionalWrites.test.ts follows the opaque ETag contract: the
    assumed rows carry the acked etag / metaEtag, which the push handler
    echoes as If-Match, instead of rebuilding a validator from version. The
    dev server moves to was-teaching-server 0.29.0, whose ETag carries the
    generation prefix parseEtag reads the revision from.

v0.22.0

Choose a tag to compare

@dmitrizagidulin dmitrizagidulin released this 06 Sep 03:13

Added

  • The logging port. setLogger(logger) on the package root installs the logger
    every call site in this package emits through and returns the one installed
    before it; the structural Logger type (debug, info, warn, error,
    each (msg, data?)) is exported beside it. An app wires it once at bootstrap,
    e.g. setLogger(createLogger('wr')) with @interop/logger. @interop/logger
    is a devDependency only: the published artifact carries no reference to it.

Changed

  • Every console.* call site in src/ (39, across 11 files) emits through the
    port. An unwired consumer keeps today's channel and level through the console
    fallback, but not byte-identical output: messages carry a [was-react]
    prefix, values previously interpolated into the message text (collection ids,
    statuses, database names) now arrive in one trailing data object, and an
    Error rides as data.err. Anything matching console text by substring should
    not anchor at position 0.
  • A failed wallet-presentation verification no longer logs the presentation
    body. An App Connect response embeds the app-key credential and its seed,
    which no log sink may receive; the per-check failures are still logged.

v0.21.0

Choose a tag to compare

@dmitrizagidulin dmitrizagidulin released this 06 Sep 02:35

Changed

  • The WAS replication driver now lives in @interop/was-sync and is a
    dependency of this package. What moved: the changes-feed pull handler, the
    conditional-write push handler, the replicateRxCollection wiring, the
    feed-backed conflict re-read, the synced-document schema, the conflict-handler
    seam with its last-write-wins default, the writer-id mint, and the controller
    core. src/sync/ is gone; docCipher.ts and wasSyncPort.ts moved to
    src/storage/, which is where this package's key handling stays.
  • SyncController (src/storage/syncController.ts) is now a binding over the
    package's createSyncController. Its public shape is unchanged
    (start({ remoteStore, localStore, onRemoteChange, onAuthError }), reSync,
    a terminal stop), and so are the skip of an uncovered collection, the status
    keying on the logical collection key, and the failed bring-up that flags every
    collection and rethrows. One behavior is new: the periodic re-sync now skips
    its tick while the browser reports itself offline, instead of firing into a
    known-down network.
  • storage/writerId.ts is a thin binding over the package's mint: it supplies
    DEFAULT_STORAGE_KEY_PREFIX and localStorage. getWriterId and
    clearPersistedWriterId keep their names and their optional-prefix signature.
  • isAuthError keeps its name and is re-exported from @interop/was-sync/rxdb.
    It is still exported from this package's root.
  • SyncStatus is re-exported from @interop/was-client/sync, which now owns
    the four-string vocabulary the replication driver and the wallets' own sync
    engine both report. The Zustand status store, deriveSyncRollup, and
    useSyncStatus are unchanged.
  • The "master" names became "primary" in the package: MasterState is
    PrimaryState, MasterReadCache is PrimaryReadCache, and
    withFeedMasterRead is withFeedPrimaryRead. RxDB's own assumedMasterState
    and realMasterState fields are RxDB's API and are untouched.
  • The replica schema is the package's merged one, which adds createdBy. RxDB
    hashes a schema and refuses to open a replica whose stored hash differs at the
    same version, and it ships at version: 0 with no migration strategy. Every
    existing local replica must therefore be cleared once: clear local data and
    log in again, and the collections re-pull from WAS. Anything written only
    locally and never synced is lost with it.

Removed

The root barrel no longer re-exports the driver. Every name below is gone from
@interop/was-react; import it from the package named beside it.

  • From @interop/was-sync: syncedDocSchema, makeLwwConflictHandler,
    lwwFields, and the types LwwFields, SyncCheckpoint, SyncedDoc,
    WireDoc, WasSyncBasePort, WasSyncPort, and MasterState /
    MasterReadCache (as PrimaryState / PrimaryReadCache).
  • From @interop/was-sync/rxdb: createWasReplication, createPullHandler,
    wireDocToRxDoc, createPushHandler, the type PushWriteAck, and
    withFeedMasterRead (as withFeedPrimaryRead).
  • From @interop/was-client/sync: errorStatus, errorMessage, formatEtag,
    isEncryptedEnvelope, isUnknownEpochError, WasSyncConflictError,
    WasSyncAuthError, and the type Json (also available from
    @interop/was-sync). These were pass-throughs; the client owns them.
  • From @interop/was-client/edv: hasKeyEpochs, epochRostersEqual, also
    pass-throughs.

createDocCipher, createPlaintextDocCodec, createUnprovisionedDocCipher,
the type DocCipher, and createWasSyncPort stay exported from this package's
root: they hold this library's key handling and the client seam, which the
driver never touches.

Dependencies

  • @interop/was-sync ^0.1.0, added.
  • rxdb ^17 is still a required peer: LocalStore is exported from the root
    entry and imports it. A replica-less app can drop it once that entry point is
    split (WR-44).

Changed

  • The last-write-wins comparison is remotePayloadWins from
    @interop/social-core, and src/sync/lww.ts is deleted. Its stamp accessor
    (lwwFields, LwwFields) moves to src/sync/types.ts and still ships from
    the root barrel. This is a behavior change on the three sites that compare
    outside conflict resolution: the anonymous-to-connected adopt, the
    duplicate-id pick in hydrateSingleton, and the entity store's patch. On a
    pair where exactly one updatedAt parses, the parseable side no longer wins;
    the raw strings compare lexically. One rule now, the one every wallet's stored
    data already converges under.
  • A 412-refused delete whose re-read body is unchanged is re-issued against
    the current ETag. A locally created row pushed with a revision the server has
    since moved past would otherwise be refused forever and stay live on the
    server. Every other 412 is still a real conflict.
  • bodiesEqual compares JCS-canonicalized JSON. A stored body a host
    re-serializes with a different key order no longer reads as a change, which is
    what keeps the delete retry firing and stops a spurious PUT on an immutable
    content-addressed row.
  • The push handler and the sync controller's isAuthError match the port's
    typed signals by err.name, through isSyncConflictError, isSyncAuthError,
    and isUnknownEpochError from @interop/was-client/sync, never by
    instanceof. An error raised by a second physical copy of the client is
    classified the same as this copy's. err.status is read off the matched value
    as a plain property.
  • The server-managed createdBy creator DID is carried across the sync layer:
    it joins OptionalBodyFields, WireDoc, and SyncedDoc, and rides the pull
    mapping (live documents and tombstones) and the assembled conflict entry. The
    RxDB schema does not declare it yet; that lands with the merged schema when
    the driver moves into its own package.
  • MasterState.deleted is optional. A feed-backed read sets it; a client get
    resolves null for a tombstone and sets nothing, and the push handler reads
    an absent flag as false.
  • getWriterId no longer adopts a value left under the pre-rename
    <prefix>clientId key, and clearPersistedWriterId no longer removes that
    key. Where localStorage cannot answer, getWriterId mints a fresh id per
    call instead of remembering one for the process: a shared fallback stamped one
    label into two accounts' histories in the same tab.
  • docCipher.ts no longer declares its own unknown-epoch predicate or its own
    base cipher shape. isUnknownEpochError ships from @interop/was-client/sync
    (still re-exported from the root barrel), and DocCipher extends the
    client's, adding the required encryptUpdate and the optional blinded-index
    applyMeta.
  • Dependency floors: @interop/was-client ^0.49.0, @interop/wallet-core
    ^0.67.0, @interop/webkms-client ^14.7.5 (matching wallet-core's).
    @interop/social-core and json-canonicalize are added.

Removed

  • The root barrel no longer exports remotePayloadWins. It is
    @interop/social-core's name, and one name has one owner; import it from
    there.

v0.20.1

Choose a tag to compare

@dmitrizagidulin dmitrizagidulin released this 05 Sep 17:43

Fixed

  • A transient failure of a collection-description read (a dropped connection, a
    5xx) is no longer taken for "no encryption descriptor".
    WasRemoteStore.readCollectionEncryption answers undefined only for a
    not-found response or a description with no encryption member; any other
    failure is thrown, wrapped with the collection id. A login fails its connected
    activation on it (falling back to local with the anonymous replica intact
    for a retry), a hot restore opens that collection fail-closed and still lands
    connected, the sync bootstrap skips the collection for the session with a
    warning, and the unknown-epoch refresh warns and leaves the cipher as it was.
  • A transient failure of a collection's /meta read is no longer taken for "no
    metadata". WasRemoteStore.readCollectionMeta answers undefined only for a
    not-found response or a backend without metadata support (501); any other
    failure is thrown, wrapped with the collection id, and the sync bootstrap's
    blinded-index schema install warns and skips that collection.
  • Neither read retries on its own: the HTTP client underneath already retries
    transient status codes and network errors, so a second retry layer only
    multiplied the attempts and the delay before a failure surfaced.
  • A session that loses the attach-time storage-context claim (two providers
    booting at once, or a keyed remount overlapping the old provider's teardown)
    now closes the replica it opened before failing, instead of leaving an open
    RxDB database behind.
  • StorageContext.detachStore now releases the active storage-context pointer
    when that context holds it, so every teardown (destroy(), logout,
    clear-data, the connected activation's fallback to local) releases it.
    Previously nothing did, and after a provider unmount the facades kept
    resolving the retired context; they now throw until the next boot claims a
    live one.
  • A connected activation that fails and whose fallback to local also fails now
    surfaces the activation's own error; the fallback's is warned about.

v0.20.0

Choose a tag to compare

@dmitrizagidulin dmitrizagidulin released this 04 Sep 21:07

Changed

  • BREAKING: The process-wide storage holders are replaced by a
    session-scoped StorageContext (src/storage/storageContext.ts), created by
    createAuthStore and exposed as storageContext on the auth store's state.
    It owns the open replica, the remote store, the writer id, the sync status
    store, and the re-hydrate timers, and its whileAttached is the one staleness
    mechanism the change patches, the scheduled re-hydrates, and hasLocalData
    run on. The app-facing facades (requireStore, hasStore,
    requireRemoteStore, hasRemoteStore, requireWriterId, stampLww,
    publicUrlFor, the entity-store verbs) now resolve the active context, and
    opening a replica while another context still holds one throws (before the
    database is opened) instead of clobbering it. createAuthStore skips its
    creation-time activation while another session is live, so a keyed provider
    remount fails the new provider's boot rather than throwing during render.
  • hasLocalData() keeps resolving false (with a warning) when a count fails
    against the attached replica, rather than rejecting into the login handler.
  • Tearing down a session now cancels the pending re-hydrates before draining the
    sync controller, so a debounced re-hydrate cannot fire against a replica that
    is about to close.
  • BREAKING: Removed setLocalStore, clearLocalStore, setRemoteStore,
    clearRemoteStore, setWriterId, clearWriterId, useSyncStatusStore,
    hydrateAll, clearAllEntityStores, patchFromChange, scheduleRehydrate,
    and cancelScheduledRehydrates. Test harnesses attach a store through
    new StorageContext({ registry, writerId }).attachStore(store); the
    re-hydrate verbs are StorageContext methods; sync status is
    storageContext.syncStatus (a vanilla zustand store,
    createSyncStatusStore).
  • BREAKING: SyncController takes a syncStatus store in its constructor;
    mergeAdopted takes the writerId explicitly.
  • getWriterId moved to src/storage/writerId.ts beside the new
    clearPersistedWriterId (localStorage only); the running session's id is
    replaced by StorageContext.resetWriterId.
  • issueSeedCredential delegates to wallet-core's issueAppKeyCredential (the
    issuer the wallet mints with) instead of re-implementing the app-key
    credential's shape; its signature and return value are unchanged. Requires
    @interop/wallet-core >= 0.66.0. The test teaching server is bumped to
    0.25.1, which verifies the eddsa-jcs-2022 zcap proofs newer wallet-core
    releases sign with.

Added

  • New exports: StorageContext, activateStorageContext,
    deactivateStorageContext, hasStorageContext, requireStorageContext,
    createSyncStatusStore, clearPersistedWriterId, and the SyncStatusStore
    type.

v0.19.0

Choose a tag to compare

@dmitrizagidulin dmitrizagidulin released this 26 Aug 19:40

Added

  • clearLocalData() now resolves with a LocalWipeReport, describing what was
    removed and, on an engine that could not confirm a deletion, what stays
    unverified.
  • New exports: executeLocalWipe and snapshotWipeTargets
    (src/session/localWipe.ts) and clearWriterId
    (src/storage/storageManager.ts), plus the LocalWipeReport and
    WipeTargets types.
  • Test coverage confirming that conditional writes (ifMatch / ifNoneMatch)
    on encrypted collections now produce an RxDB conflict entry on a lost race,
    rather than the silent last-write-wins the preconditions previously degraded
    to on this class of collection.
  • Test coverage for connecting to a wallet account anchored by a standing unlock
    credential with no enrolled durable clients: a deeper delegation chain, an
    ephemeral, non-stable presentation holder, a clamped grant expiry, a mid-life
    authorization failure routing to reconnect, and a key epoch this app cannot
    open degrading a shared read to a warn-and-skip and a descriptor-less private
    collection to its fail-closed cipher. A wallet-composed counterpart case now
    exercises the same annex-shaped grant against @interop/wallet-core's real
    request/response composition.

Fixed

  • clearLocalData() no longer orphans the anonymous replica: run from a
    connected session, it used to remove only the open replica, discard the
    anonymous seed, and mint a fresh identity, stranding the previous anonymous
    replica under a database name nothing could derive again. It now snapshots
    every database name before deleting anything and reaches both replicas.
  • logout({ wipe: true }) no longer leaves empty database shells behind: RxDB's
    own removal clears a collection's table but leaves its IndexedDB database
    standing, and the erase grade now deletes those shells too.
  • The writer id is now cleared by clearLocalData(); it previously survived
    every wipe grade.

Changed

  • The logout and clear-data dialog copy is more precise about what each grade
    erases: the logout dialog names the connected replica specifically, and the
    clear-data dialog distinguishes the connected case (the synced copy survives)
    from the local-only case (this browser is the only copy).
  • Upgraded @interop/wallet-core to ^0.56.0 and @interop/was-client to
    ^0.44.5.

v0.18.1

Choose a tag to compare

@dmitrizagidulin dmitrizagidulin released this 13 Aug 04:22

Changed

  • hasKeyEpochs is now re-exported from @interop/was-client/edv instead of
    being implemented here, and the local store's descriptor-refresh comparison
    uses that subpath's epochRostersEqual (also re-exported). Requires
    @interop/was-client >= 0.37.0.

v0.18.0

Choose a tag to compare

@dmitrizagidulin dmitrizagidulin released this 13 Aug 04:02

Added

  • Documents written through the sync path now carry blinded indexed entries:
    the sync bootstrap reads each hmac-bearing private collection's stored /meta
    (after declaring its blinded-index attributes) and installs the persisted
    index schema on that collection's document cipher
    (LocalStore.applyCollectionMeta), so app writes are findable by equality
    queries. The installed schema survives cipher rebuilds (epoch rotations).
    Documents sealed before the schema was installed remain unfindable until
    rewritten.
  • WasRemoteStore.readCollectionMeta reads a collection's raw stored /meta
    value.

Changed

  • Upgraded @interop/was-client to 0.36.0 (adds the applyMeta / build-time
    meta support on createEdvDocCipher that the above uses).