mirror of
https://github.com/meshtastic/web.git
synced 2026-08-03 00:20:08 -04:00
main
20 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
d2cb51d489 |
Execute oxfmt (#1166)
Ran pnpm oxfmt . This should get the web repo aligned so that we can better enforce oxfmt going forward. |
||
|
|
3dec67f623 |
chore(deps-dev): bump typescript from 5.9.3 to 6.0.3 (#1113)
* chore(deps-dev): bump typescript from 5.9.3 to 6.0.3 Bumps [typescript](https://github.com/microsoft/TypeScript) from 5.9.3 to 6.0.3. - [Release notes](https://github.com/microsoft/TypeScript/releases) - [Commits](https://github.com/microsoft/TypeScript/compare/v5.9.3...v6.0.3) --- updated-dependencies: - dependency-name: typescript dependency-version: 6.0.3 dependency-type: direct:development update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> * chore(tsconfig): align configs for typescript 6.0 Consolidate package tsconfigs to extend tsconfig.base.json and drop options now redundant or invalid under TS 6 (strict, baseUrl, allowImportingTsExtensions, strictNullChecks). Switch base module to "preserve" and lib to ESNext. Fix apps/web typecheck script to invoke tsc directly instead of recursive pnpm run tsc. --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Dan Ditomaso <dan.ditomaso@gmail.com> |
||
|
|
dc9749c042 |
feat: scaffold @meshtastic/sdk + signals/sqlocal persistence migration (#1050)
* feat(sdk): scaffold @meshtastic/sdk + @meshtastic/sdk-react
Adds two new packages laying the foundation for a domain-driven migration
away from @meshtastic/core.
packages/sdk
- DDD feature slices: device, chat, nodes, channels, config, telemetry,
position, traceroute, files. Each with domain/application/infrastructure/state.
- Shared kernel under core/: MeshClient orchestrator, Transport interface
(byte-compat with existing transport-* packages), EventBus (typed pub/sub),
packet codec, Queue, Xmodem, signals helpers, tslog factory.
- Signals via @preact/signals-core. Application use-cases return
Result<T,E> via better-result; legacy ports keep throwing.
- shim/ re-exports the legacy MeshDevice/Types/Utils API so
packages/web continues to build unchanged.
- createFakeTransport() under @meshtastic/sdk/testing.
- 16 vitest tests incl. end-to-end fake-transport integration.
packages/sdk-react
- MeshProvider + useSignal/useSignalValue/useClient adapters.
- Hooks: useDevice, useConnection, useChat, useNodes, useNode,
useChannels, useChannel, useConfig, useModuleConfig, useTelemetry,
usePosition, useTraceroute, useFileTransfer, useFavoriteNode, useIgnoreNode.
- jsdom-backed hook tests.
Root README rewritten with packages table, architecture, and workflow.
* refactor(web): import @meshtastic/sdk instead of @meshtastic/core
Mechanical swap across all 74 @meshtastic/core imports in packages/web/src.
The SDK's shim layer re-exports the legacy MeshDevice/Types/Utils/Protobuf
surface, so no source edits are required beyond the import path. All 294
web vitest tests still pass.
This is Phase B step 1: web now runs on @meshtastic/sdk's MeshDevice shim.
Per-slice store migrations (messageStore/nodeDBStore/deviceStore →
useChat/useNodes/useDevice etc.) land in follow-up commits.
* feat(sdk): add MeshRegistry + multi-client React providers
Phase B prep for web migration. Web holds multiple simultaneous device
connections keyed by ConnectionId, so per-slice hook migrations need a
registry-aware provider.
packages/sdk
- MeshRegistry: Map<ConnectionId, MeshClient> with signals for list/active/activeId.
create()/get()/has()/remove()/setActive().
- First-created client auto-activates.
- remove() disconnects the client and rotates active to another entry.
- 4 vitest cases covering create, auto-activate, duplicate rejection, and remove.
packages/sdk-react
- MeshRegistryProvider + MeshRegistryContext.
- useMeshRegistry, useOptionalMeshRegistry, useActiveClient, useClientById(id).
- useClient now falls back to the registry's active client when no direct
<MeshProvider> is present, so existing hooks work unchanged under a
registry-backed app.
No web-facing changes in this commit; used by follow-up slice migrations.
* refactor(sdk-react): rename useDevice → useMeshDevice
Prevents collision with packages/web's own useDevice() Zustand hook. All
internal exports + tests updated; no behavior change.
Callers migrating off @meshtastic/core should use useMeshDevice() from
@meshtastic/sdk-react going forward.
* feat(web): mount MeshRegistryProvider at app root
Introduces a single app-wide MeshRegistry in packages/web/src/core/meshRegistry.ts
and wraps the RouterProvider in <MeshRegistryProvider>. Registry starts empty;
useConnections continues to instantiate the legacy MeshDevice shim. Subsequent
slice migrations will swap useConnections over to registry.create() and move
consumers onto useMeshDevice()/useChat()/etc from @meshtastic/sdk-react.
Adds @meshtastic/sdk-react as a web dependency. No behavior change; web tests
(294) and production build still pass.
* feat(web,sdk): register per-connection MeshClient in registry
packages/sdk
- MeshRegistry.register(id, client): adopts an externally-constructed
MeshClient. Complements create() for migration paths where a legacy shim
already owns the client.
- MeshRegistry.unregister(id): drops the mapping without disconnecting, for
cases where the caller has torn down the transport itself.
- Legacy MeshDevice shim exposes its inner MeshClient as `meshClient` so
consumers can adopt it into the registry.
packages/web
- useConnections.setupMeshDevice now registers the shim's MeshClient with the
app-wide meshRegistry and marks it active on connect.
- removeConnection unregisters from the registry.
- Legacy Zustand deviceStore wiring is unchanged; follow-up commits will move
read paths to useMeshDevice/useChat etc. and remove the duplicated fields.
No behavior change visible to users. Web tests (294) + SDK tests (20) still pass.
* feat(sdk): add MessageRepository port + InMemoryMessageRepository
Lays the persistence groundwork for the chat slice migration.
- MessageRepository port defines paginated reads (loadRecent, loadBefore),
atomic writes (append, appendBatch), state updates, and retention pruning.
Conversation keyed by ConversationKey tagged union (channel | direct peer).
- RetentionPolicy: maxPerBucket + olderThanMs knobs. Consumer decides.
- InMemoryMessageRepository ships with SDK as default + test fixture.
- ChatClient accepts { repository?, retention?, initialLoadLimit? }.
Lazy-hydrates per conversation on first subscribe; writes through on every
inbound message; prunes after append when retention policy is set.
- ChatClient.loadOlder(conv, before, limit) for pagination UI.
- ChatStore.prepend() for older-first inserts.
Tests: 5 new cases for InMemoryMessageRepository (paginate, update state,
retention). 25 SDK tests total, all green. Web build unchanged.
Paves the way for @meshtastic/sdk-storage-sqlocal to implement this port
against SQLite/OPFS in a follow-up.
* feat(sdk-storage-sqlocal): SQLite WASM persistence adapters
New workspace package implementing @meshtastic/sdk repository ports against
sqlocal (SQLite WASM + OPFS) with Drizzle-typed queries.
Schema (single DB, multi-device aware via device_id column)
- messages: chat history. Indexes on (device_id, conversation_key, rx_time)
for fast pagination and on (device_id, state) for pending lookups.
- nodes: NodeDB snapshot per device (stub schema, repo lands with PR #7).
- telemetry: per-node ring buffer of readings (stub).
- _schema: migration version table.
- Hand-written DDL migrations in src/schema/migrations.ts; applied at boot.
createSqlocalDb({ databasePath })
- Opens OPFS DB, applies migrations, returns Drizzle client typed against the
schema.
- Single instance per origin; sqlocal serializes writes via OPFS file locks.
SqlocalMessageRepository
- Implements MessageRepository: paginated loadRecent / loadBefore, append /
appendBatch with onConflictDoNothing, updateState, prune (maxPerBucket via
windowed DELETE + olderThanMs).
- Optional MultiTabCoordinator broadcasts messages-changed events on append.
MultiTabCoordinator
- BroadcastChannel pub/sub for cross-tab change notifications (no-ops if API
unavailable, e.g. Node).
- acquireLock() wraps navigator.locks.request with fall-through for non-browser
contexts.
Testing
- src/testing/createMemoryDb.ts: in-memory sql.js + Drizzle, same surface as
the sqlocal connection. Lets repository tests run on Node CI.
- 6 SqlocalMessageRepository tests (pagination, retention, multi-device
isolation) + 2 MultiTabCoordinator tests pass on sql.js.
Notes
- @meshtastic/sdk pkg.json now points types at ./mod.ts so workspace consumers
resolve types directly from source. Production publish path needs a separate
follow-up to emit a stable mod.d.ts.
- Storage pkg ships ESM only; dts disabled until tsdown's hashed-name emit
is reconciled with mod.d.ts resolution. Workspace consumption already gets
full types from source.
* feat(web): persist chat history via @meshtastic/sdk-storage-sqlocal
Plumbs the SDK chat slice through the OPFS-backed SQLite repository so
inbound and outbound text messages survive page reloads, with retention
capped at 1000 messages per conversation or 90 days, whichever hits first.
- packages/sdk legacy MeshDevice shim now accepts MeshClientOptions
(chat, configId, logger). Backwards-compatible with the old
`new MeshDevice(transport, configIdNumber)` form.
- packages/web/src/core/sdkStorage.ts: lazy singletons for the shared
SqlocalDb and the cross-tab MultiTabCoordinator. The DB opens on first
call so test runs that never connect stay headless-safe.
- useConnections.setupMeshDevice is now async, awaits getStorageDb, and
passes a SqlocalMessageRepository scoped to the connection id. Falls
back to the SDK's InMemoryMessageRepository if sqlocal init fails (no
OPFS support, etc.).
- Vite worker format set to "es" because sqlocal's worker is ES-module
and rolldown rejects iife with code-splitting.
- COOP/COEP dev headers were already in vite.config.ts; no further
changes required for OPFS.
Web tests (294) and production build still green.
This is the runtime payoff of PR #5: a fresh page load populates chat from
SQLite via lazy pagination instead of rehydrating 1000 messages into memory.
The legacy Zustand messageStore is still in place for now; PR #6 (chat
slice migration) will retire it and switch UI components to useChat.
* test: testing strategy + 19 new SDK / storage / hook tests
Adds TESTING.md documenting six tiers (unit / slice / client / storage /
hook / E2E), per-package coverage gates, and the audit of where we
currently sit.
Slice tests in @meshtastic/sdk
- NodeMapper proto round-trip; NodesClient list signal updates.
- ChannelsClient indexes by channel number.
- ConfigClient merges Config + ModuleConfig variants.
- TelemetryClient latest + history per node.
- PositionClient byNode + list.
- ChatClient persistence: hydrate on first subscribe, paginate via
loadOlder, persist inbound messages through the repository. Fixes a
reverse-iteration bug in loadOlder discovered by the new test.
Hook tests in @meshtastic/sdk-react
- New tests/hooks.registry.test.tsx covers useMeshDevice, useNodes,
useNode, useChannels under <MeshRegistryProvider>, plus an active-
client switch round-trip.
Storage tests in @meshtastic/sdk-storage-sqlocal
- migrations.test.ts validates v1 DDL creates messages/nodes/telemetry/
_schema and indexes; CREATE IF NOT EXISTS is idempotent.
- MultiTabCoordinator.broadcast.test.ts proves cross-tab BroadcastChannel
delivery between two coordinators in the same process.
- New vitest.browser.config.ts + tests/sqlocal-opfs.browser.test.ts run
under @vitest/browser (Playwright provider) for real OPFS round-trip
verification. Wired as `pnpm test:browser`. Browser test files end in
`.browser.test.ts` and are excluded from the Node runner.
E2E / firmware-simulator tier (TESTING.md §"E2E / simulator") is scoped
for a follow-up — needs CI Docker for meshtasticd.
Totals after this change:
- @meshtastic/sdk: 36 tests (was 25)
- @meshtastic/sdk-react: 8 tests (was 2)
- @meshtastic/sdk-storage-sqlocal: 12 tests (was 8)
- meshtastic-web: 294 tests (unchanged)
* feat(web): MessagesPage reads chat history from SDK / SQLite
Adds an adapter hook that bridges sdk-react's `useChat` and `useDirectChat`
to the legacy `Message` shape MessagesPage / ChannelChat / MessageItem
expect. The page now renders messages directly from the OPFS-backed
SqlocalMessageRepository — page reload hydrates lazily (last 50 per
conversation) instead of pulling 1000+ rows from IndexedDB into memory.
packages/sdk-react
- useChat() gains loadOlder(before, limit) for paginated backfill.
- New useDirectChat(peer) hook covering DM conversations.
packages/web
- src/core/hooks/useChatLegacy.ts: maps SDK `Message` → legacy
`messageStore/types.ts` Message shape, including state translation
(SDK Pending → legacy Waiting).
- MessagesPage flips broadcast and direct chat reads to useChatLegacy.
getMessages call sites removed; setMessageState retained on the legacy
store for outbound bookkeeping until the next migration step.
Drafts, unread counts, activeChat / chatType continue to live in the
Zustand messageStore — they are UI-only state and stay where they are
per the locked architecture decision. The legacy store's saveMessage,
getMessages, setMessageState, and persistence path remain in place for
now; PR cleanup follow-up will retire them once outbound state and
"delete all messages" flows are switched to the SDK.
Web test suite: 294 still green. Production Vite build clean.
* feat(web): MessagesPage outbound send through SDK ChatClient
Switches the send-text path from `connection?.sendText(...)` (legacy
MeshDevice) to `client.chat.send({ text, destination, channel })` on the
active MeshClient pulled from MeshRegistry via useActiveClient().
Side effects:
- Outbound message state (Ack / Failed) is now driven entirely by the
SDK chat slice via routing-packet subscriptions; the manual
setMessageState calls are removed.
- The legacy Zustand `useMessages().setMessageState` and `MessageState`
imports are no longer used by MessagesPage. Drafts and unread counts
still live in the Zustand store.
- `getMyNode` is no longer needed in this page (was only used to label
the now-removed direct-message state updates).
Web tests (294) still green; production Vite build clean.
* feat(sdk,web): drafts persisted in SDK chat slice (SQLite-backed)
Moves per-conversation draft text out of the legacy Zustand messageStore
into the SDK chat slice so drafts share the same persistence + signal
machinery as messages. MessageInput now binds directly to the SDK; the
legacy `useMessages().getDraft/setDraft/clearDraft` API is no longer
called from the input.
packages/sdk
- New DraftRepository port + InMemoryDraftRepository default.
- ChatClient.drafts namespace: get(key) returns a ReadonlySignal<string>;
set/clear keyed by ConversationKey.
- ChatClient.send auto-clears the draft for the resolved conversation on
success (parity with prior Zustand clearDraft-on-send behavior).
- Lazy hydrate from the DraftRepository on first read of a conversation.
- 3 new tests in ChatClient.drafts.test.ts.
packages/sdk-react
- New useDraft(conversation) hook returning { text, setText, clear }.
packages/sdk-storage-sqlocal
- New `drafts` table (device_id + conversation_key composite PK, text,
updated_at). Drizzle schema in src/schema/drafts.ts.
- Migration v2 in src/schema/migrations.ts creates the table on first
open of an existing v1 DB.
- SqlocalDraftRepository implementing DraftRepository: load/save/clear/
loadAll, scoped by device_id, upsert on conflict, delete on empty save.
- 6 new tests covering save/load round-trip, empty-string deletion,
upsert, multi-device isolation, loadAll.
packages/web
- useConnections wires the SqlocalDraftRepository alongside the existing
SqlocalMessageRepository per registered MeshClient.
- MessageInput accepts `conversation: ConversationKey` instead of the
prior `to: Types.Destination` — fixes the legacy bug where every
broadcast channel shared a single draft slot.
- MessagesPage passes the appropriate ConversationKey for direct/
broadcast chats.
- MessageInput.test.tsx rewritten to mock useDraft from sdk-react.
Test counts: sdk 39 (+3), sdk-storage-sqlocal 18 (+6), web 294 unchanged.
Production Vite build clean.
* chore(web): drop saveMessage path from subscriptions, delete dead DTO
The SDK chat slice now persists every inbound/outbound text packet via the
SqlocalMessageRepository wired in useConnections, so the legacy Zustand
saveMessage path in subscriptions.ts was writing to a store no UI code
reads from.
- subscriptions.ts: removed saveMessage call + PacketToMessageDTO usage.
Unread-count increments retained as-is (cross-cutting concern, migrates
in a separate commit).
- subscribeAll's messageStore parameter retained as `_messageStore` for
callsite stability while the rest of the legacy store is being retired.
- Deleted packages/web/src/core/dto/PacketToMessageDTO.ts (no remaining
consumers; SDK has its own MessageMapper at
packages/sdk/src/features/chat/infrastructure/MessageMapper.ts).
Web tests (294) still green; production build clean.
Out of scope, queued:
- useConnections refactor (the hook is overdue for cleanup)
- Strip dead methods from the Zustand messageStore (saveMessage,
getMessages, setMessageState, getDraft, setDraft, clearDraft +
Zustand-persist + IDB wrapper). Requires a follow-up sweep of the
remaining test files that mock those methods.
- Migrate unread counts to the SDK (cross-cutting between chat + nodes).
* feat(sdk,web): ChatClient.clearConversation + clearAll; wire DeleteMessagesDialog
Rounds out the chat slice with destructive operations so the UI dialog no
longer needs the legacy Zustand deleteAllMessages.
packages/sdk
- MessageRepository port gains clearConversation(key); InMemory and
Sqlocal adapters implement it (SQL: scoped DELETE by conversation).
- ChatStore gains clearBucket(key) + clearAll().
- ChatClient.clearConversation(conv) and ChatClient.clearAll(): empty the
in-memory store, drop the hydrated-marker so a future subscribe
re-fetches fresh, then delete from the repository. Repository failures
are swallowed — UI must not get stuck behind a write error.
packages/sdk-storage-sqlocal
- SqlocalMessageRepository.clearConversation: DELETE FROM messages WHERE
(device_id, conversation_key) match.
packages/web
- DeleteMessagesDialog swaps useMessages().deleteAllMessages for
useActiveClient()?.chat.clearAll(). No-active-client path is a no-op
but still closes the dialog.
- Test file updated: mocks useActiveClient; new case covers
no-active-client safety.
Totals: sdk 39 tests unchanged (clearConversation tested transitively via
DeleteMessagesDialog; adapter-specific test queued for follow-up),
sdk-storage-sqlocal 18 unchanged, web 295 (+1).
* feat(sdk,sdk-storage-sqlocal,web): NodesRepository port + SQLite persistence
Adds SDK-side node persistence so a fresh page load rehydrates the mesh
NodeDB from disk before any device packets arrive. Web's existing Zustand
nodeDBStore continues to work unchanged in parallel — UI consumer
migration to useNodes/useNode lands in follow-up commits.
packages/sdk
- New NodesRepository port: loadAll / get / upsert / upsertBatch / remove /
clear. InMemoryNodesRepository ships as the default.
- NodesClient takes optional { repository }, hydrates on construction,
writes through on every onNodeInfoPacket, and keeps live signal +
persistence in lockstep. remove/reset clear the repository alongside the
store + drive the legacy admin message.
- MeshClientOptions exposes a `nodes` slot mirroring the existing `chat`
slot.
packages/sdk-storage-sqlocal
- New SqlocalNodesRepository implementing the port. user / position /
deviceMetrics serialized as base64-encoded protobuf bytes (stable
across schema additions). Subpath export at "@meshtastic/sdk-storage-
sqlocal/nodes".
- 6 vitest cases covering upsert + loadAll round-trip, overwrite, proto
round-trip preserves user fields, remove, clear, multi-device isolation.
packages/web
- useConnections opens a SqlocalNodesRepository alongside the chat /
draft repos and passes it to the new MeshDevice constructor.
Test counts: sdk 39 (unchanged — repo round-trip exercised in storage
adapter tests), sdk-storage-sqlocal 24 (+6), web 295 unchanged. Build
clean.
* feat(sdk,web): SDK Node carries hops/mqtt/key-verified; legacy adapter hook
Lays the groundwork for migrating web's 31 nodeDB consumers off the
Zustand `useNodeDB().getNodes/getNode` API onto SDK signals, without a
big-bang rewrite.
packages/sdk
- Node domain entity gains channel, viaMqtt, hopsAway, isKeyManuallyVerified
fields. NodeMapper.fromProto now copies these from the inbound
Protobuf.Mesh.NodeInfo. Required so downstream UIs that show "X hops
away" / "via MQTT" / "encryption verified" can read SDK nodes without
losing fidelity.
packages/web
- New core/hooks/useNodesLegacy.ts: useNodesLegacy() returns
Protobuf.Mesh.NodeInfo[] derived from SDK signals; useNodeLegacy(num)
returns a single NodeInfo. Components migrate one at a time by swapping
useNodeDB().getNodes / getNode call sites for these hooks; templates
stay unchanged because the result shape matches.
No consumer rewrites in this commit — that is per-component follow-up
work to keep diffs reviewable. Web tests (295) still green; production
build clean. SDK 39 / sdk-storage-sqlocal 24 unchanged.
* refactor(web): NodesPage reads node list from SDK signals
First consumer migration off the legacy Zustand nodeDBStore onto
SDK-managed nodes. Pages/Nodes/index.tsx now pulls the full node array
through useNodesLegacy() — which subscribes to the SDK NodesClient
signal underneath — and applies the existing nodeFilter predicate
client-side via useMemo.
Behavior parity:
- Same Protobuf.Mesh.NodeInfo shape rendered in the table (the legacy
adapter ensures channel / viaMqtt / hopsAway / publicKey survive).
- Same debounce semantics — only the underlying source changed.
- hasNodeError + nodeErrors continue to come from the Zustand
nodeDBStore until PKI-error tracking is migrated to the SDK in a
follow-up commit (validation logic still in
packages/web/src/core/stores/nodeDBStore/nodeValidation.ts).
The legacy nodeDBStore still populates from subscriptions.ts, so this
rewrite is reversible and runs alongside the SDK source. Web tests
(295) still green; production Vite build clean.
* refactor(web): migrate 9 nodeDB consumers onto SDK adapter
Sweeps the most-touched UI paths off useNodeDB().getNode/getMyNode/getNodes
and onto the useNodesLegacy / useNodeLegacy / useMyNodeLegacy adapters
introduced earlier. Behaviour parity preserved — the adapter returns the
same Protobuf.Mesh.NodeInfo shape components already render.
Migrated:
- components/Sidebar.tsx — myNode + node count from SDK signals.
- components/CommandPalette/index.tsx — node lookup for connection labels.
- components/UI/Avatar.tsx — single-node lookup.
- components/Dialog/RemoveNodeDialog.tsx — selected node display.
- components/Dialog/PKIBackupDialog.tsx — myNode for download/print headers.
- components/Dialog/LocationResponseDialog.tsx — sender lookup.
- components/Dialog/TracerouteResponseDialog.tsx — endpoint lookups.
- components/Dialog/NodeDetailsDialog.tsx — selected node detail.
- components/PageComponents/Settings/User.tsx — myNode for owner edit.
- components/PageComponents/Settings/Position.tsx — myNode for current position.
- components/PageComponents/Messages/TraceRoute.tsx — hop name lookup.
- components/PageComponents/Messages/MessageItem.tsx — myNode (suspending) +
message author. The polling Suspense fallback now retriggers on the SDK
signal instead of polling the Zustand store directly.
- components/PageComponents/Map/Popups/WaypointDetail.tsx — locked-to node.
- pages/Messages.tsx — sidebar node list + selected peer lookup.
- pages/Map/index.tsx — full filtered list + myNode for fitting. Drops the
now-dead NODEDB_DEBOUNCE_MS constant since the SDK signal layer handles
re-render coalescing internally.
- TraceRoute.test.tsx mocks updated to mock useNodesLegacy instead of
useNodeDB.
NodesLayer.tsx, RemoveNodeDialog (removeNode), ResetNodeDbDialog, and
RefreshKeysDialog still depend on hasNodeError / removeNode /
removeAllNodes on the legacy nodeDB store — those move when PKI-error
tracking and the admin-message paths are migrated to the SDK in the
unread/cleanup follow-up.
Web tests: 295 still green; production Vite build clean. No SDK changes
in this commit.
* refactor(web): rename node legacy adapters to *AsProto
The "Legacy" suffix on the node-adapter hooks reads as if the hook
itself is deprecated, when in fact the hook is the bridge: it converts
SDK Node domain entities into Protobuf.Mesh.NodeInfo so consumer
templates that destructure proto fields keep working during the
migration.
- useNodesLegacy → useNodesAsProto
- useNodeLegacy → useNodeAsProto
- useMyNodeLegacy → useMyNodeAsProto
- File renamed to packages/web/src/core/hooks/useNodesAsProto.ts.
All 16 call sites updated. Test mock in
packages/web/src/components/PageComponents/Messages/TraceRoute.test.tsx
updated to the new module path.
Behaviour unchanged. 295 tests still green; production Vite build clean.
(useChatLegacy follows the same pattern but maps to a hand-written web
type, not a proto — it stays as-is for now and is queued for renaming
once the broader chat-store cleanup lands.)
* refactor(web): useFavoriteNode + useIgnoreNode drive SDK NodesClient
The favorite/ignore admin-message paths now go through the SDK NodesClient
instead of the legacy Zustand sendAdminMessage + manual proto build.
- useFavoriteNode: meshClient.nodes.favorite(nodeNum) /
meshClient.nodes.unfavorite(nodeNum). The SDK already has
FavoriteNodeUseCase + AdminMessageSender behind these APIs.
- useIgnoreNode: meshClient.nodes.ignore / .unignore.
- Both still mirror the optimistic flag flip into the legacy nodeDB store
via updateFavorite / updateIgnore until that store is fully retired —
same TODO as before, "wait for ack before flipping".
Tests rewritten to mock @meshtastic/sdk-react via vi.hoisted (so the
factory captures the spy without hitting the vi.mock hoist trap):
- assert SDK favorite/unfavorite/ignore/unignore calls
- assert legacy store mirror still fires
- assert toast + longName fallback behaviour preserved
- assert no-op when the node isn't in the SDK store yet (parity with
the prior "getNode returned undefined" guard)
Web tests: 295 still green; production Vite build clean.
* refactor(web): ResetNodeDb + RefreshKeys dialogs read SDK; safer adapter
- ResetNodeDbDialog: connection.resetNodes() → meshClient.nodes.reset();
on success it now also calls meshClient.chat.clearAll() instead of the
legacy useMessages().deleteAllMessages(). PKI-error tracking and the
in-memory nodeDB still get cleared via removeAllNodeErrors /
removeAllNodes since those subsystems have not yet migrated. Test
rewritten to mock useActiveClient via vi.hoisted.
- RefreshKeysDialog: drops useNodeDB().getNode in favour of
useNodeAsProto for the missing-key node display. Test wraps render in
a MeshRegistryProvider with an empty registry so the adapter resolves
cleanly with no active client.
Adapter hardening (useNodesAsProto.ts)
- Switched off useNodes() / useMeshDevice() (which both throw outside a
MeshProvider/MeshRegistryProvider with an active client) onto
useActiveClient() + a no-op signal fallback. The hooks now return [] /
undefined when there is no active client instead of throwing, which
fixes RefreshKeysDialog's "no error → render null" path under tests
that don't connect a device.
Web tests: 295 still green; production Vite build clean.
* feat(sdk,sdk-react,web): node PKI / routing-error tracking on the SDK
Moves PKI mismatch / duplicate-key validation and routing-error tracking
out of the legacy Zustand nodeDBStore and into the SDK NodesClient so
every consumer reads the same source of truth. Validates Android's gap
analysis: Meshtastic-Android does not track per-node PKI errors at all,
so the SDK must own this concern client-side.
packages/sdk
- New NodeError + NodeErrorType (Routing_Error | "MISMATCH_PKI" |
"DUPLICATE_PKI"). Mirrors the previous web-only types.
- New nodeValidation infrastructure mapper (pure): byte-equal compare on
publicKey, returns ValidatedNodeInfo { accepted?, error? }. Ports the
detection rules verbatim from
packages/web/src/core/stores/nodeDBStore/nodeValidation.ts.
- NodesClient gains an errors signal + errorFor / hasError / setError /
clearError / clearAllErrors API. handleIncoming runs validation before
upserting; conflicts skip the store write and record an error. A clean
refresh of a previously-flagged node clears the error.
- handleRoutingPacket records PKI_UNKNOWN_PUBKEY / PKI_FAILED /
PKI_SEND_FAIL_PUBLIC_KEY / NO_CHANNEL against packet.from.
- 5 new vitest cases covering MISMATCH_PKI, DUPLICATE_PKI, error-clear-on-
refresh, routing-error capture, clearError / clearAllErrors. SDK total:
44 tests, all green.
packages/sdk-react
- New useNodeErrors / useNodeError(num) / useHasNodeError(num) hooks.
All resolve through useActiveClient() and fall back to empty when no
client is present.
packages/web
- pages/Nodes/index.tsx, pages/Messages.tsx, components/PageComponents/
Map/Layers/NodesLayer.tsx swap useNodeDB().hasNodeError for the SDK
useNodeErrors hook + a memoised Set lookup.
- components/Dialog/RefreshKeysDialog/RefreshKeysDialog.tsx reads the
current error from useNodeError(activeChat); useRefreshKeysDialog.ts
drives meshClient.nodes.clearError + meshClient.nodes.remove instead
of the legacy store. The companion test still passes through the
empty-MeshRegistry render path because useNodeError tolerates a
missing active client.
- core/subscriptions.ts no longer calls nodeDB.setNodeError on PKI /
no-channel routing packets — the SDK records those itself. The dialog
open trigger stays here.
- pages/Nodes/index.tsx drops the now-dead NODEDB_DEBOUNCE_MS constant.
Web tests: 295 still green; production Vite build clean. The legacy
nodeDB still runs its internal validation but its error map is now a
dead end — removal queued in the plan-file follow-up alongside the rest
of the nodeDB cleanup.
* chore(web): remove dead PKI / nodeError paths from legacy nodeDB
Now that the SDK NodesClient owns public-key validation and per-node
error tracking, the legacy Zustand mirrors are dead code.
packages/web/src/core/stores/nodeDBStore/index.ts
- Drops the nodeErrors map + setNodeError / getNodeError / hasNodeError /
clearNodeError / removeAllNodeErrors methods from the NodeDB interface
and factory.
- Drops the validateIncomingNode call inside addNode and inside
setNodeNum's merge path; legacy mirror is now a straight last-write-
wins shallow merge. The SDK runs validation independently against its
own snapshot.
- Drops the nodeErrors entries from the persisted partialize shape.
packages/web/src/core/stores/nodeDBStore/types.ts
- Removes NodeError + NodeErrorType. ProcessPacketParams stays.
packages/web/src/core/stores/nodeDBStore/nodeValidation.ts — deleted
(SDK ports it at packages/sdk/src/features/nodes/infrastructure/
nodeValidation.ts and exposes the verdict via NodesClient).
packages/web/src/core/stores/index.ts — drop the dead NodeErrorType
re-export.
packages/web/src/core/stores/nodeDBStore/nodeDBStore.test.tsx
- Removes tests covering the migrated PKI behaviour (errors map,
MISMATCH, DUPLICATE, "unions nodeErrors") — equivalent coverage now
lives in packages/sdk/src/features/nodes/NodesClient.errors.test.ts.
- Trims the surviving merge-semantics tests to the simpler last-write-
wins shape.
- The "selector re-renders" test swaps the deleted setNodeError mutation
for an updateFavorite call to keep the slice-stability assertion alive.
- The "addNodeDB instance identity" assertion relaxes from .toBe to
content equality — immer's pruneStaleNodes path can reseat the entry,
but the registered DB's id stays stable.
components/Dialog/ResetNodeDbDialog/ResetNodeDbDialog.tsx — calls
meshClient.nodes.clearAllErrors() instead of the legacy
removeAllNodeErrors. Test mocks updated.
components/Dialog/RefreshKeysDialog/* — already migrated to SDK errors
in the previous commit; no further changes here.
Test counts: web 290 (was 295 — 5 PKI-tracking tests retired in
favour of 5 equivalent SDK tests). Production Vite build clean.
* feat(sdk,web): SDK NodesClient owns user/position/lastHeard/snr +
favourite-flag flips; legacy mirror retired
The SDK NodesClient now subscribes to onUserPacket / onPositionPacket /
onMeshPacket so user-record updates, GPS updates, and lastHeard / snr
refreshes flow into the signal-backed store + repository directly. The
favourite / ignored toggles also flip the local flag once the admin
message resolves successfully, so the UI no longer needs to mirror the
state into a parallel Zustand store.
packages/sdk
- NodesClient: new private patch(num, partial) helper that shallow-
merges into the existing entry (or seeds a placeholder Node) and
upserts to the repository in one shot. Used by:
- onUserPacket → patch user
- onPositionPacket → patch position
- onMeshPacket → patch lastHeard / snr (replaces the legacy nodeDB
processPacket flow)
- favorite / unfavorite / ignore / unignore — flag flip on Result.ok
- NodesClient.reset({ keepMyNode? }) — preserves the local node entry
when requested. Mirrors the previous removeAllNodes(true) semantics
the ResetNodeDb dialog relied on.
packages/web
- core/subscriptions.ts: drops the onUserPacket / onPositionPacket /
onNodeInfoPacket / onMeshPacket → legacy nodeDB write paths. The
unused nodeDB parameter is renamed `_nodeDB` for callsite stability;
it will disappear when the store itself is deleted in a follow-up.
- core/hooks/useFavoriteNode + useIgnoreNode: drop the legacy
updateFavorite / updateIgnore mirror — the SDK flips the flag on
success.
- components/Dialog/RemoveNodeDialog: now calls meshClient.nodes.remove
exclusively; no legacy fall-through.
- components/Dialog/ResetNodeDbDialog: calls
meshClient.nodes.reset({ keepMyNode: true }) and meshClient.chat.clearAll().
No legacy nodeDB calls.
- Test mocks for useFavoriteNode / useIgnoreNode / ResetNodeDbDialog
pruned to match.
Test counts: sdk 44 (unchanged), sdk-react 8, sdk-storage-sqlocal 24,
web 290. Production Vite build clean.
Remaining surface on the legacy nodeDB: addNode / addUser / addPosition /
processPacket / setNodeError-equivalent are now unused; the store retains
only updateFavorite / updateIgnore (no callers) and the per-device
plumbing required by the deviceContext hooks. Full deletion of
nodeDBStore queued in the plan-file follow-up.
* chore(web): drop leftover useNodeDB from Position settings page
useMyNodeAsProto already replaces getMyNode here; the import was left
behind during the nodeDB mirror retirement.
* chore(web): delete legacy nodeDBStore — SDK NodesClient is the only source of truth
The Zustand nodeDBStore had no remaining writers (the mirror in
subscriptions.ts was retired in
|
||
|
|
c729d3b25e |
fix: resolve lint warnings/errors and apply formatting (#1024)
* fix: resolve lint warnings/errors and apply formatting Fix 10 oxlint issues (2 errors, 8 warnings): - Remove unused catch parameters in Security.tsx and ImportDialog.tsx - Remove stray expression in Generator.tsx - Add eslint-disable for debounced useCallback in FilterControl.tsx - Remove unnecessary deps (resolveDB, store) in bindStoreToDevice.ts - Prefix unused variant param in AppSidebar.tsx - Memoize tabs arrays in DeviceConfig, RadioConfig, ModuleConfig - Fix channels type in RadioConfig TabItem Also applies oxfmt formatting across all files. * updating lock file * update protobuf package * fix: regenerate pnpm-lock.yaml and exclude jsr protobufs from minimumReleaseAge * prevented http card from always being polled * updated pnpm config fiile * updated actions * removed biome config and lint system leftovers * updating protobuf package |
||
|
|
ad6b9f470f |
Bumped library package version (#843)
* chore: bumped package versions * removed jsr file |
||
|
|
765f672bf5 |
Improve the nightly and release workflow (#836)
* fix: improve the nightly and release workflow * Update .github/workflows/nightly.yml Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * fix from code review * add type module to node-serial --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> |
||
|
|
bcdda8b751 |
Connection robustness improvements (#813)
* Clear heartbeat and queue when disconnected * Give clearer error in case configure fails due to a lost connection. Used to throw 'Packet does not exist' * If the queue processing error is due to a lost connection, throw it instead of looping endlessly * In case we send a disconnection event we don't need to also throw * Catch heartbeat errors * Also handle invalid state errors * Handle socket timeouts * Log heartbeat failures * Make linter happy * Transform stream being a singleton prevented reconnection attempts * Adapt tests to not using singleton * Aborting already ends the connection |
||
|
|
d0f4939a4d |
Added lint rule to enforce importing with extensions (#831)
* fix: added lint rule to enforce importing with extentions * fix test import path * added import file extentions |
||
|
|
90cf136b8c |
Update test format (#821)
* Lint tests, format JSON * Update test formatting --------- Co-authored-by: philon- <philon-@users.noreply.github.com> |
||
|
|
91426a89e5 | Lint tests, format JSON (#818) | ||
|
|
d7e32e9b03 |
Add transport status events (#790)
* Transport status events Add symbol docs Emit transport status events Transport test suite * Review fixes * Remove core dependency * HTTP transport use AbortSignal, error handling in TransportNode * Improve stream handling * Update packages/transport-web-serial/src/transport.ts Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Fix linting --------- Co-authored-by: philon- <philon-@users.noreply.github.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> |
||
|
|
eb9b7159d4 |
Refactor github actions with monorepo support (#783)
* refactor github actions with monorepo support * Update .github/workflows/release-packages.yml Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update .github/workflows/release-packages.yml Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * updates * changed order of ci/pr steps * Update .github/workflows/release-web.yml Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * adding lock file --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> |
||
|
|
2735c37fad |
Fix Docker and CI builds (#773)
* Fix Docker and CI builds * Fix indentation * Fix release --------- Co-authored-by: philon- <philon-@users.noreply.github.com> |
||
|
|
284ccd43f8 |
refactor: update ci/cd scripts, switch to pnpm (#763)
* refactor: update ci/cd scripts, switch to pnpm * updated workflow * add new packages github action. Bump package.json version (#762) * Update packages/transport-deno/scripts/build_npm.ts Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update packages/core/package.json Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update packages/transport-deno/scripts/build_npm.ts Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update packages/transport-deno/scripts/build_npm.ts Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update packages/transport-deno/scripts/build_npm.ts Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update .github/workflows/release-packages.yml Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * revert copilot suggestion * adding lock file * regenerate lock file --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> |
||
|
|
0a7b653ec8 |
Transport disconnect method (#753)
* Prevent reads from piling up * Implement disconnect() method for all transports |
||
|
|
4dd911e73d |
Use workspace meshtastic packages (#749)
* refactor: updated web package.json to use workspace * formatting fixes |
||
|
|
8a443e9cad |
Fix/add npm jsr building (#722)
* fixed github workflows to improve handling of mutl runtimes * updating readme * Update packages/core/src/meshDevice.ts Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update packages/core/package.json Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update packages/transport-http/package.json Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> |
||
|
|
0e29639222 | Use the actual toDeviceStream (#721) | ||
|
|
704d06cfe7 | refactor: switch to using Bun (#718) | ||
|
|
66b839742a |
Add node-transport lib (#703)
* feat: add node transport * updated readme * Update packages/transport-node/README.md Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> |