Repository navigation
connect: mdbase-next SDK backend for Reader (opt-in) - #60
callumalpass wants to merge 5 commits into
Conversation
The SDK is not published yet. The packed build is vendored and referenced with a file: spec until a published version replaces it.
nextReaderClient maps read to get, query/queryPages to cursor-paged query (offsets emulated), and create/update/delete to field-level intents. Writes with ifRevision wait for confirmation so a CAS refusal reaches Reader as concurrent_modification; others return optimistically. The 15 replica error codes map onto Connect problems, including a waiting-for-device message for unavailable/no_device_online. nextReaderFiles implements stat/list/upload/download over FilesApi. Tests run against MemoryReplica.
ReaderNextApplicationSession produces the same snapshots as the Connect session over a relay connection, keyed by loadOrCreateClientKey (a non-extractable WebCrypto key in IndexedDB). A private collection with no device online shows as blocked with a waiting-for-device message. The control plane (grants registered at consent, routes) sits behind ReaderNextControlPlane; ProposedHttpControlPlane targets the proposed GET /v1/next/collections/:id/route endpoint. nextReaderCollection assembles Reader's existing repositories on the new seams. Migration, interrupted-write recovery, direct access and collection setup are stubbed; saved views read listViews/executeView defensively. ReaderSession and ReaderPortableSession describe what the apps need, and the backend is exported as @mdbase-reader/connect/next. Core may not import the new SDK.
The web app reads ?sdk=next or VITE_MDBASE_SDK=next, beside its existing ?server= and VITE_MDBASE_CONNECT_URL settings. The extension reads MDBASE_SDK=next at build time, beside MDBASE_ENV. The Connect SDK stays the default.
|
D2 import stub replaced in this opt-in backend (commit a new head). NextMigrationTarget uses collection-scoped stable record/file/transfer IDs, immutable-payload mutation IDs and singleton submit allow_partial/mutation_ids via existing SDK; waits for confirmed writes/files; never updates existing records. Native legacy journal stays daemon-owned (migration confirmed browser must not read it). Connect package typecheck, 163 tests, touched-file ESLint/Prettier, and architecture check pass. Six new tests cover pending-to-confirmed, lost confirmation response retry, reopened-session retry, normalized field order, changed payload/path collisions, duplicate identities, cancellation, and file retry. Full-repo build not run. No deploy/publish. Coordinator merges this port PR; security review requested on new adapter. |
|
security: no blocking findings — security-2, on head 6aaa6e5, for the three-file migration adapter delta from 150b185. Scoped deterministic identities and normalized-payload mutation IDs retain confirmed-only create semantics; collisions/changed records are never updated. Existing importer supplies a content-digest-qualified transfer key and checks the resulting file digest; files wait for confirmed. Duplicate record identities and malformed provenance stop import. Static delta/caller review; reported 163 tests (six new) were not rerun. This does not establish real relay/replica authorization conformance or remove earlier opt-in port integration gates. |
Adds an opt-in backend for Reader on the mdbase-next TS SDK (
@mdbase-dev/sdk). The current Connect SDK is still the default, and nothing changes unless the switch below is set.Only
packages/connectimports the new SDK. ESLint now also stopspackages/corefrom importing it.How to switch
?sdk=nextto the URL, or build withVITE_MDBASE_SDK=next. These sit next to the existing?server=andVITE_MDBASE_CONNECT_URLsettings.MDBASE_SDK=next, next toMDBASE_ENV.@mdbase-reader/connect/next(ReaderNextApplicationSession,nextReaderClient,nextReaderFiles,nextReaderCollection).SDK dependency (temporary)
The SDK is not published yet. The packed build is vendored at
packages/connect/vendor/mdbase-dev-sdk-c573c96.tgzand referenced with afile:spec. This will switch to a published version once one exists.What's ported
ReaderConnectClientoverMdbaseClient(next/client.ts)readmaps toget.readManyruns concurrentgets and returns revision-qualified rows, soread-many-documents-v1is reported.queryandqueryPagesmap to cursor-pagedquery. Page sizes are kept. Offsets are emulated for the library list.create,updateanddeletemap to field-level intents. The SDK fills in the base values and the body edits from the view that was just read.ifRevisionis passed through. A write that carriesifRevisionwaits forconfirmed, so a refusal at head still reaches Reader. Other writes return the optimisticWrite.records. A later rejection goes toonRejected.ConnectRepositoryErrorandconnectProblemMessagepath, using Reader's own text for each code. A CAS conflict becomesconcurrent_modification, so record sessions and editors behave as before.nextErrorOf(problem)returns the originalMdbaseError.unavailablewith reasonno_device_onlineshows "Waiting for one of your devices…". The session setswaitForDevice: true, the snapshot showsblockedwith that message, and the session exposeswaitingForDevice.next/files.ts):stat,list,uploadanddownloadoverFilesApi. They feed the existing document, collection-file, asset and source-import repositories. Reader's retry keys are turned into stable transfer and mutation UUIDs, so retries are idempotent.include: {body: true}, used only for that search.ReaderNextApplicationSessionimplements a newReaderSessioninterface, and alsoReaderPortableSessionfor the extension.loadOrCreateClientKey, which keeps a non-extractable WebCrypto key in IndexedDB.MdbaseRecordSessionadapters work unchanged, because they sit on the repositories.What's stubbed (no equivalent in the client API yet)
unsupported_operation.dryRundelete, but it reports nobrokenLinks, andcheckBacklinksis ignored.selectvalues: the annotation→source link resolution projection is dropped. Path-form source links that don't fall back to the legacy bare-ID form won't resolve.query-metadata-v1is reported as unsupported.listViews/executeView. Because their payload shape isn't defined in the contract, they are read defensively. An unrecognised list is treated as empty, and an unrecognised execution is refused. Saving works through create/update.pending()is always false, and receipts replace it;applyCollectionSetup: a no-op, because type packs aren't provisioned;modifiedAton file descriptors: empty, becauseFileViewhas no mtime.reader-source/reader-annotation), because the new API has no contract views.Dependency on the control workstream
How the app obtains a grant (registering
client_pkat consent) and a route is isolated behindReaderNextControlPlane(next/control-plane.ts).ProposedHttpControlPlanecurrently does the following:resolveRoutecalls the proposedGET {serverUrl}/v1/next/collections/:id/route, which returns{targets: [{url, device, noise_pk}]}. A 404 or an emptytargetsmeans no device is online.grantsreads grants that were stored out of band (mdbase-reader:next-grants, viarememberGrant).authorizemakes no grant yet, so "Connect another collection" reports that the consent flow is unavailable.The endpoint and the consent flow need to match whatever the control workstream ships.
Tests
MemoryReplica(26 tests). They cover:concurrent_modification/ConnectRepositoryError;packages/connect: 157 tests pass;apps/reader: 401 tests pass;apps/extension: 169 tests pass;eslint . --max-warnings 0,prettier --check .and the architecture check and tests all pass.