Skip to content

baukit v0.5.1

Choose a tag to compare

@PatrickKoss PatrickKoss released this 29 Sep 04:59
· 383 commits to main since this release

baukit v0.5.1

One release train for the Rust crates, TypeScript packages, and templates.

Summary

baukit 0.5.1 closes the gaps that Leitbild, Hebkit, and Redemut hit while adopting 0.5.0, so those products can delete code instead of wrapping Baukit. It is a patch release by number, but a few interfaces gained required methods. Since no product is live, those breaks shipped without shims. They are listed below.

New

  • @baukit/data-contracts: encodeCsv can quote every cell and write null as a marker that stays distinct from empty text. The Rust baukit_core::export::CsvOptions gets the same options, and shared vectors pin both languages to the same bytes.
  • @baukit/data-contracts: KeyValueStore.clearPrefix, a WebStorageKeyValueStore for localStorage and sessionStorage, and DurableDraft.move plus isScopeActive, so a draft can follow a newly created document and stop writing once another editor owns it.
  • @baukit/data-contracts-expo-sqlite: a transaction on ExpoSqliteConnection can nest. The inner work runs in a savepoint on the same handle, and a caught inner failure rolls back only the inner work.
  • @baukit/api-runtime: mutation bodies can be interface types, products can plug in their own error classifier, overlapping keyFor calls for one intent share a key, and the full-jitter backoff is public on @baukit/api-runtime/backoff.
  • @baukit/a11y-core/web: useReducedMotion and useReducedMotionPreference without React Native.
  • @baukit/sync-client: SyncScheduler retries failed runs when given a retry option with a product-chosen delay.
  • baukit-push: DeliveryClaimStore::purge for PostgreSQL and in-memory stores.
  • @baukit/auth-node/keycloak-testing: the Keycloak e2e helpers that used to live only in the template. Generated auth web products import them from the package.

Breaking

  • KeyValueStore implementations must add clearPrefix, and DurableDraft implementations must add move.
  • SqliteTransaction.transaction runs nested work and returns its result instead of always rejecting. Code that typed it as Promise<never> stops compiling.
  • A hand-written IdempotencyKeyStore must add classifyError, and its keyFor and settle are generic.
  • DeliveryClaimStore has a new required purge method.
  • Auth web templates add @baukit/auth-node as a dev dependency, and the helper allowWebOrigin is now allowKeycloakWebOrigin.

The sections below list every change per crate and package.

Verification

Every step of the local CI mirror passed on main before the version bump, and make ci passed again on the release commit. The mirror covered make ci, all Rust tests including Docker-gated ones (711), cargo deny, MSRV 1.95, all seven generated fixture flavors with coverage floors, the MCP fixture gate, browser Dexie conformance (36 tests), the Android compile gate, Expo SQLite on an Android emulator (33 cases), and the Hermes vector and notifications checks. The auth fixture's Keycloak stack e2e ran against the new helpers. iOS and physical devices were not tested.

Rust crates

baukit-auth

  • No component-specific changes in this train.

baukit-config

  • No component-specific changes in this train.

baukit-core

Added

  • Add CsvOptions::with_all_cells_quoted, which quotes every text and numeric cell and leaves
    empty cells unquoted, and CsvOptions::with_null_marker, which writes a marker such as \N for
    an empty cell and quotes text with the same content. Either keeps an empty cell apart from empty
    text, so a product's importer can read its own export back. with_null_marker panics on an empty
    marker or one holding a double quote, comma, CR, or LF. The shared vectors gain five cases.

baukit-credential-vault

  • No component-specific changes in this train.

baukit-egress

  • No component-specific changes in this train.

baukit-events

  • No component-specific changes in this train.

baukit-http

  • No component-specific changes in this train.

baukit-integrations

  • No component-specific changes in this train.

baukit-jobs

  • No component-specific changes in this train.

baukit-openapi

  • No component-specific changes in this train.

baukit-ops

  • No component-specific changes in this train.

baukit-push

Added

  • Add DeliveryClaimStore::purge(before, limit), which deletes one batch of
    claims for local dates before before, oldest date first, and returns how
    many went. PostgresDeliveryClaimStore runs purge_delivery_claims on its
    pool, and MemoryDeliveryClaimStore purges in the same order, so scheduled
    purge jobs no longer need a product repository method or a store they cannot
    fake in tests.

Breaking

  • DeliveryClaimStore has a new required method, purge. Custom
    implementations must add it.

baukit-ratelimit

  • No component-specific changes in this train.

baukit-runtime

  • No component-specific changes in this train.

baukit-sync

  • No component-specific changes in this train.

baukit-telemetry

  • No component-specific changes in this train.

baukit-test

  • No component-specific changes in this train.

TypeScript packages

a11y-core

Patch Changes

  • 5a29c63: @baukit/a11y-core/web exports useReducedMotion, useReducedMotionPreference, and ReducedMotionPreference. The web versions use matchMedia and useSyncExternalStore and never import react-native, so a plain React web app can use them. They return the same shapes as the root hooks. During server rendering and hydration useReducedMotionPreference reports { reducedMotion: false, resolved: false }. The root entry's hooks are unchanged.
  • Release the coordinated baukit 0.5.1 train.

analytics-core

Patch Changes

  • Release the coordinated baukit 0.5.1 train.

analytics-posthog-native

Patch Changes

  • Release the coordinated baukit 0.5.1 train.
  • Updated dependencies
    • @baukit/analytics-core@0.5.1

analytics-posthog-web

Patch Changes

  • Release the coordinated baukit 0.5.1 train.
  • Updated dependencies
    • @baukit/analytics-core@0.5.1

api-runtime

Patch Changes

  • fa3e0c3: MutationIntent takes any body that JSON carries unchanged. It is now generic, MutationIntent<Body extends JsonCompatible<Body> = JsonValue>, so interface-typed request bodies no longer need a cast to JsonValue. keyFor, settle, sendIdempotentMutation, and canonicalJson infer the body type. Members set to undefined are left out of the key, and undefined array items encode as null, as JSON.stringify does. New type: JsonCompatible.

    createIdempotencyKeyStore accepts classifyError, a MutationErrorClassifier that maps the product's own API errors to an outcome and returns undefined for the rest. The store exposes classifyError(error), which applies it before classifyMutationError, and sendIdempotentMutation settles failed sends with it. classifyMutationStatus takes an optional error code and treats idempotency_key_in_progress as possibly committed.

    Overlapping calls for the same intent share one key from the first keyFor until the intent settles, even while storage is still reading or when storage drops writes. A failed lookup does not block the next one.

    New subpath @baukit/api-runtime/backoff exports fullJitterBackoffMs and BackoffPolicy, the delay formula createApiFetch already used. It imports nothing.

    Breaking: IdempotencyKeyStore gains a required classifyError method, and its keyFor and settle are generic. A hand-written store or test double must add the method.

  • Release the coordinated baukit 0.5.1 train.

auth-native

Patch Changes

  • Release the coordinated baukit 0.5.1 train.

auth-node

Patch Changes

  • 306a46f: Add the Node-only @baukit/auth-node/keycloak-testing subpath for end-to-end tests against a disposable development Keycloak. keycloakStack(defaults, environment) reads E2E_KEYCLOAK_URL, E2E_KEYCLOAK_REALM, E2E_KEYCLOAK_ADMIN_USERNAME, E2E_KEYCLOAK_ADMIN_PASSWORD, and E2E_KEYCLOAK_WEB_CLIENT_ID over the product's defaults. createKeycloakTestUser(stack, user?, options?) creates a verified user, random e2e-<uuid> by default, and returns its username, password, email, and subject. revokeKeycloakUserSessions(stack, subject) ends a user's sessions. allowKeycloakWebOrigin(stack, origin) adds an origin to the web client's redirect URIs and web origins when it lacks them. signInWithKeycloak(page, user, stack, { timeoutMs }) fills the login form by its #username, #password, and #kc-login IDs. Admin calls use fetch with a 30-second default timeout and take an injected fetch; errors name the step and HTTP status, never a response body. The page parameter is a structural type that a Playwright Page satisfies, so the package still has no runtime or peer dependencies.

    No breaking changes to existing exports.

  • Release the coordinated baukit 0.5.1 train.

auth-web

Patch Changes

  • Release the coordinated baukit 0.5.1 train.

data-contracts-dexie

Patch Changes

  • e26045b: Add KeyValueStore.clearPrefix(prefix), which deletes every key that starts with prefix. Matching is exact and case-sensitive, no character is a wildcard, and an empty prefix clears the store. InMemoryKeyValueStore, DexieKeyValueStore (one IndexedDB key range), and the Expo SQLite key-value store (a UTF-8 byte prefix match inside the store's namespace) implement it, and describeKeyValueContract checks it, including SQL LIKE wildcards, case, emoji, and U+FFFF. The Dexie real-browser suite now runs the key-value contract too.

    Breaking: KeyValueStore gains a required method, so a product's own KeyValueStore implementation must add clearPrefix.

  • Release the coordinated baukit 0.5.1 train.

  • Updated dependencies [06cc669]

  • Updated dependencies [d423b98]

  • Updated dependencies [e26045b]

  • Updated dependencies

  • Updated dependencies [403805a]

    • @baukit/data-contracts@0.5.1

data-contracts-expo-sqlite

Patch Changes

  • 89a2d0a: SqliteTransaction.transaction(work) now nests instead of rejecting. It runs work in a SAVEPOINT on the same handle without waiting on the per-file queue, releases it when work resolves, and rolls back to it when work rejects, so only the nested statements and schema changes are undone and the same error reaches the caller. The enclosing transaction rolls back too unless the caller catches that error. While a nested transaction is open, statements and transaction() on an enclosing context reject with a TypeError.

    Break: SqliteTransaction.transaction changes from (work) => Promise<never> to <TResult>(work: (transaction: SqliteTransaction) => Promise<TResult> | TResult) => Promise<TResult>. Code that relied on a nested call rejecting, or that typed its forwarding method as returning Promise<never>, must change.

  • e26045b: Add KeyValueStore.clearPrefix(prefix), which deletes every key that starts with prefix. Matching is exact and case-sensitive, no character is a wildcard, and an empty prefix clears the store. InMemoryKeyValueStore, DexieKeyValueStore (one IndexedDB key range), and the Expo SQLite key-value store (a UTF-8 byte prefix match inside the store's namespace) implement it, and describeKeyValueContract checks it, including SQL LIKE wildcards, case, emoji, and U+FFFF. The Dexie real-browser suite now runs the key-value contract too.

    Breaking: KeyValueStore gains a required method, so a product's own KeyValueStore implementation must add clearPrefix.

  • Release the coordinated baukit 0.5.1 train.

  • Updated dependencies [06cc669]

  • Updated dependencies [d423b98]

  • Updated dependencies [e26045b]

  • Updated dependencies

  • Updated dependencies [403805a]

    • @baukit/data-contracts@0.5.1

data-contracts

Patch Changes

  • 06cc669: Add two encodeCsv options that keep a null cell apart from empty text. quoteAllCells: true quotes every text and numeric cell and leaves null cells unquoted. nullMarker writes a marker such as \N unquoted for a null cell and quotes a text cell with the same content; an empty marker or one holding a double quote, comma, CR, or LF throws a RangeError. baukit_core::export::CsvOptions gains with_all_cells_quoted and with_null_marker, and both pass five new shared vectors.

    Default output is unchanged. No breaking changes.

  • d423b98: Add DurableDraft.move(scope), which hands an open draft to another scope, for example from a "new" draft to the record the server created. It writes a dirty or stored value under the new key, deletes the old key, keeps the value, revision, and submission state, and sends later saves to the new key. It resolves moved, blocked, or stale and rejects with DraftPersistenceError on a storage failure, leaving the draft on the old scope.

    Add the isScopeActive option to createDurableDraft. The helper calls it before every write or delete; when it returns false, save, clear, and move resolve stale without touching storage. Reads are not checked.

    Breaking: the DurableDraft interface gains the required move method, so a product's own implementation of that interface must add it. Code that only calls createDurableDraft is unaffected.

  • e26045b: Add KeyValueStore.clearPrefix(prefix), which deletes every key that starts with prefix. Matching is exact and case-sensitive, no character is a wildcard, and an empty prefix clears the store. InMemoryKeyValueStore, DexieKeyValueStore (one IndexedDB key range), and the Expo SQLite key-value store (a UTF-8 byte prefix match inside the store's namespace) implement it, and describeKeyValueContract checks it, including SQL LIKE wildcards, case, emoji, and U+FFFF. The Dexie real-browser suite now runs the key-value contract too.

    Breaking: KeyValueStore gains a required method, so a product's own KeyValueStore implementation must add clearPrefix.

  • Release the coordinated baukit 0.5.1 train.

  • 403805a: Add WebStorageKeyValueStore, a KeyValueStore over sessionStorage or localStorage. It keeps every key under a required non-empty namespace, stores JSON text, rejects instead of throwing on storage failures, and maps a full storage to StorageError storage_quota_exceeded. It passes describeKeyValueContract. No breaking changes.

events

Patch Changes

  • Release the coordinated baukit 0.5.1 train.

integrations-client

Patch Changes

  • Release the coordinated baukit 0.5.1 train.

localization-core

Patch Changes

  • Release the coordinated baukit 0.5.1 train.

notifications-core

Patch Changes

  • Release the coordinated baukit 0.5.1 train.
  • Updated dependencies
    • @baukit/localization-core@0.5.1

notifications-expo

Patch Changes

  • Release the coordinated baukit 0.5.1 train.
  • Updated dependencies
    • @baukit/notifications-core@0.5.1

preferences-core

Patch Changes

  • Release the coordinated baukit 0.5.1 train.

pwa-web

Patch Changes

  • Release the coordinated baukit 0.5.1 train.

sync-client

Patch Changes

  • Release the coordinated baukit 0.5.1 train.

  • a39abd7: SyncScheduler retries failed runs when given retry: { maxRetries, delayMs, onRetryScheduled? }. delayMs(error, retryIndex) returns the wait in milliseconds or null for no retry; fullJitterBackoffMs from @baukit/api-runtime/backoff is the Baukit policy to pass in, so the root entry keeps no runtime dependencies. The retry index restarts after a success. Any trigger during the wait ends it and restarts the index: trigger(), the interval, foreground, or connectivity. stop() ends the wait without another run, and a wait that ends while backgrounded runs nothing. A bad delay or a throwing delayMs reaches onError and ends the retries. The constructor throws a RangeError for a maxRetries that is not a non-negative integer. New types: SyncSchedulerRetryOptions and SyncSchedulerRetry. Without retry, behavior is unchanged.

    trigger() during a run no longer only joins it: when the run is waiting to retry, the wait ends. This applies only with retry set.

ui-tokens

Patch Changes

  • Release the coordinated baukit 0.5.1 train.

Commits

  • chore: prepare baukit v0.5.1 (b3583bb)
  • docs(plan): record the P1 CSV and durable draft fixes (fce34a0)
  • docs(evidence): record the 0.5.1 CSV and durable draft follow-ups (a2b2138)
  • feat(data-contracts): move durable drafts between scopes and fence writes (d423b98)
  • feat(data-contracts): add a Web Storage key-value store (403805a)
  • feat(data-contracts): add KeyValueStore.clearPrefix (e26045b)
  • feat(data-contracts): keep null CSV cells apart from empty text (06cc669)
  • docs(plan): record the P2 runtime, reduced motion, and sync retry fixes (16ba3cb)
  • docs(evidence): record 0.5.1 follow-ups for idempotency, reduced motion, and sync retry (b6f291c)
  • feat(sync-client): retry failed scheduler runs with a product-chosen delay (a39abd7)
  • feat(a11y-core): export reduced-motion hooks from the web entry (5a29c63)
  • feat(api-runtime): accept interface bodies, product error classifiers, and overlapping sends (fa3e0c3)
  • docs(plan): record the P4 claim purge and Keycloak helpers (c6da0d4)
  • feat(templates): import Keycloak stack helpers from @baukit/auth-node (a7a88c0)
  • feat(auth-node): add Keycloak test helpers on a keycloak-testing subpath (306a46f)
  • feat(push): add purge to the delivery claim store port (eee8757)
  • docs(plan): record the P3 nested savepoint fix (2ed92ef)
  • docs(evidence): record nested savepoints for the SQLite connection (db596ab)
  • fix(data-contracts-expo-sqlite): nest connection transactions as savepoints (89a2d0a)
  • docs(plan): track the 0.5.1 fix wave (c0b207a)
  • docs(plan): record Redemut adoption of 0.5.0 (28db260)
  • docs(plan): record Hebkit adoption of 0.5.0 (e8aee59)
  • docs(plan): record Leitbild adoption of 0.5.0 (d7f0032)
  • docs(plan): track product adoption of 0.5.0 (7a865d2)