chore: move packages/web → apps/web

Web is a deployable SPA, not a library — no @meshtastic scope, no
exports map, no npm/JSR publish target, no other workspace member
depends on it. Conventional pnpm/Turbo/Nx layout puts deployables
under apps/ and libraries under packages/. Doing the move now (after
PR #9 / #10 / unread / useConnections refactor land but before Phase
C ships) so the lib-only packages/* directory remains stable as we
delete packages/core.

Mechanics:
- pnpm-workspace.yaml: add `apps/*` to packages.
- git mv packages/web apps/web (entire directory tree).
- vite.config.ts uses process.cwd(), so no internal path edits needed.
- pnpm filter commands still resolve by package name (`pnpm --filter
  meshtastic-web run build`) — no script changes.

External-path consumers updated:
- README.md table row.
- tsconfig.json reference.
- 7 GH workflows (pr.yml, nightly.yml, release-web.yml, the three
  crowdin-*.yml, release-packages.yml). The
  packages/release-packages.yml `packages/* | grep -v packages/web`
  filter is now redundant since web isn't in packages/* anymore — it
  collapses to plain `ls -d packages/*`.

Verified: web build clean, web 236 / sdk 65 / sdk-react 8 / storage
30 tests all pass.
This commit is contained in:
Dan Ditomaso
2026-04-26 11:10:05 -04:00
parent d4cd649b6b
commit daf7c22b60
616 changed files with 319 additions and 757 deletions

View File

@@ -17,7 +17,7 @@ jobs:
uses: crowdin/github-action@v2
with:
base_url: "https://meshtastic.crowdin.com/api/v2"
config: "./packages/web/crowdin.yml"
config: "./apps/web/crowdin.yml"
upload_sources: false
upload_translations: false
download_translations: true

View File

@@ -5,7 +5,7 @@ on:
# Monitor all .json files within the /i18n/locales/en/ directory.
# This ensures the workflow triggers if any the English namespace files are modified on the main branch.
paths:
- "/packages/web/public/i18n/locales/en/*.json"
- "/apps/web/public/i18n/locales/en/*.json"
branches: [main]
workflow_dispatch: # Allow manual triggering
@@ -21,7 +21,7 @@ jobs:
uses: crowdin/github-action@v2
with:
base_url: "https://meshtastic.crowdin.com/api/v2"
config: "./packages/web/crowdin.yml"
config: "./apps/web/crowdin.yml"
upload_sources: true
upload_translations: false
download_translations: false

View File

@@ -15,7 +15,7 @@ jobs:
uses: crowdin/github-action@v2
with:
base_url: "https://meshtastic.crowdin.com/api/v2"
config: "./packages/web/crowdin.yml"
config: "./apps/web/crowdin.yml"
upload_sources: false
upload_translations: true
download_translations: false

View File

@@ -39,18 +39,18 @@ jobs:
run: pnpm run test
- name: Build web package
working-directory: packages/web
working-directory: apps/web
run: pnpm run build
- name: Package output
working-directory: packages/web
working-directory: apps/web
run: pnpm run package
- name: Upload compressed build (artifact)
uses: actions/upload-artifact@v4
with:
name: web-build-nightly
path: packages/web/dist/build.tar
path: apps/web/dist/build.tar
if-no-files-found: error
- name: Compute tags and labels
@@ -66,8 +66,8 @@ jobs:
# Try to use latest release tag if it exists; fallback to package version; else date
LATEST_TAG="$(gh release view --json tagName --jq .tagName 2>/dev/null || true)"
if [ -z "$LATEST_TAG" ] && [ -f packages/web/package.json ]; then
LATEST_TAG="v$(jq -r .version packages/web/package.json)"
if [ -z "$LATEST_TAG" ] && [ -f apps/web/package.json ]; then
LATEST_TAG="v$(jq -r .version apps/web/package.json)"
fi
if [ -n "${LATEST_TAG:-}" ] && [ "$LATEST_TAG" != "vnull" ]; then
@@ -91,7 +91,7 @@ jobs:
uses: redhat-actions/buildah-build@v2
with:
containerfiles: |
./packages/web/infra/Containerfile
./apps/web/infra/Containerfile
image: ${{ env.REGISTRY_IMAGE }}
tags: |
${{ steps.meta.outputs.moving_tag }}

View File

@@ -51,4 +51,4 @@ jobs:
run: pnpm run test
- name: Build web package
run: pnpm --filter "./packages/web" run build
run: pnpm --filter "./apps/web" run build

View File

@@ -46,7 +46,7 @@ jobs:
run: |
set -euo pipefail
if [ "${{ github.event.inputs.packages }}" = "all" ] || [ -z "${{ github.event.inputs.packages }}" ]; then
mapfile -t TARGETS < <(ls -d packages/* | grep -v 'packages/web')
mapfile -t TARGETS < <(ls -d packages/*)
else
IFS=',' read -ra TARGETS <<< "${{ github.event.inputs.packages }}"
TARGETS=("${TARGETS[@]/#/packages/}")

View File

@@ -51,25 +51,25 @@ jobs:
run: pnpm install --frozen-lockfile
- name: Build web package
working-directory: packages/web
working-directory: apps/web
run: pnpm run build
- name: Create release archive
working-directory: packages/web
working-directory: apps/web
run: pnpm run package
- name: Upload archive (artifact)
uses: actions/upload-artifact@v4
with:
name: web-build
path: packages/web/dist/build.tar
path: apps/web/dist/build.tar
if-no-files-found: error
- name: Attach archive to GitHub Release
if: ${{ github.event_name == 'release' || inputs.attach_to_release == true }}
uses: softprops/action-gh-release@v2
with:
files: packages/web/dist/build.tar
files: apps/web/dist/build.tar
tag_name: ${{ github.event_name == 'release' && github.event.release.tag_name || inputs.tag_name }}
fail_on_unmatched_files: true
env:
@@ -134,7 +134,7 @@ jobs:
uses: docker/build-push-action@v6
with:
context: .
file: ./packages/web/infra/Containerfile
file: ./apps/web/infra/Containerfile
platforms: linux/amd64,linux/arm64,linux/arm/v7
push: ${{ github.event_name == 'release' || (github.event_name == 'workflow_dispatch' && inputs.tag_name != '') }}
tags: ${{ steps.meta.outputs.tags }}

3
.gitignore vendored
View File

@@ -18,3 +18,6 @@ packages/protobufs/packages/ts/dist
*.pem
*.crt
*.key
# Claude Code session locks
.claude/

View File

@@ -1,3 +1,3 @@
{
"recommendations": ["bradlc.vscode-tailwindcss", "biomejs.biome"]
"recommendations": ["bradlc.vscode-tailwindcss", "oxc.oxc-vscode"]
}

View File

@@ -24,7 +24,7 @@ All projects live under `packages/`.
| --- | --- |
| `packages/sdk` | Framework-agnostic TypeScript SDK. Domain-driven feature slices (device, chat, nodes, channels, config, telemetry, position, traceroute, files) built around a `MeshClient` orchestrator with `@preact/signals-core` reactive state. |
| `packages/sdk-react` | React hooks + `MeshProvider` on top of `@meshtastic/sdk`. Wraps signals in `useSyncExternalStore` for concurrent-safe renders. |
| `packages/web` | Reference React web client. Hosted at [client.meshtastic.org](https://client.meshtastic.org). |
| `apps/web` | Reference React web client. Hosted at [client.meshtastic.org](https://client.meshtastic.org). |
| `packages/ui` | Shared Radix + Tailwind component library. |
| `packages/protobufs` | Generated TypeScript stubs from [`meshtastic/protobufs`](https://github.com/meshtastic/protobufs), produced via `buf generate`. Source of truth for every wire-level type. |
| `packages/transport-http` | HTTP transport for devices exposing a network interface. |

View File

@@ -19,8 +19,8 @@ Tests live in five tiers. Each PR should add coverage at the **lowest level that
| Package | Required floor | Notes |
| --- | --- | --- |
| `packages/sdk` core | every primitive (signals, EventBus, Queue, packet-codec, identifiers) has a unit test; lifecycle covered by `MeshClient.test.ts` | adopt `c8` thresholds once stable |
| `packages/sdk` features/* | each slice ships: 1 domain invariant test, 1 use-case test against fake transport, 1 mapper round-trip (where mappers exist) | Integration covered by `tests/integration/fake-transport.test.ts` |
| `packages/sdk/core` | every primitive (signals, EventBus, Queue, packet-codec, identifiers) has a unit test; lifecycle covered by `MeshClient.test.ts` | adopt `c8` thresholds once stable |
| `packages/sdk/features/*` | each slice ships: 1 domain invariant test, 1 use-case test against fake transport, 1 mapper round-trip (where mappers exist) | Integration covered by `tests/integration/fake-transport.test.ts` |
| `packages/sdk-react` | each public hook has a `renderHook` test that asserts initial render + re-render on signal change | uses jsdom; provider wrapper required |
| `packages/sdk-storage-sqlocal` | every repository method tested against sql.js in-memory; **at least one OPFS-real test per repo** runs in browser mode | sql.js validates SQL correctness; OPFS validates VFS / Worker plumbing |
| `packages/transport-*` | minimum: framing round-trip, disconnect cleans up streams, error path emits status | low-level; ship as is |

View File

View File

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB

View File

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View File

Before

Width:  |  Height:  |  Size: 102 KiB

After

Width:  |  Height:  |  Size: 102 KiB

View File

Before

Width:  |  Height:  |  Size: 77 KiB

After

Width:  |  Height:  |  Size: 77 KiB

View File

Before

Width:  |  Height:  |  Size: 5.3 KiB

After

Width:  |  Height:  |  Size: 5.3 KiB

View File

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

View File

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 4.2 KiB

View File

Before

Width:  |  Height:  |  Size: 40 KiB

After

Width:  |  Height:  |  Size: 40 KiB

View File

Before

Width:  |  Height:  |  Size: 5.0 KiB

After

Width:  |  Height:  |  Size: 5.0 KiB

View File

Before

Width:  |  Height:  |  Size: 7.4 KiB

After

Width:  |  Height:  |  Size: 7.4 KiB

View File

Before

Width:  |  Height:  |  Size: 5.2 KiB

After

Width:  |  Height:  |  Size: 5.2 KiB

View File

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 13 KiB

View File

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 13 KiB

View File

Before

Width:  |  Height:  |  Size: 95 KiB

After

Width:  |  Height:  |  Size: 95 KiB

View File

Before

Width:  |  Height:  |  Size: 95 KiB

After

Width:  |  Height:  |  Size: 95 KiB

View File

Before

Width:  |  Height:  |  Size: 47 KiB

After

Width:  |  Height:  |  Size: 47 KiB

View File

Before

Width:  |  Height:  |  Size: 9.5 KiB

After

Width:  |  Height:  |  Size: 9.5 KiB

View File

Before

Width:  |  Height:  |  Size: 102 KiB

After

Width:  |  Height:  |  Size: 102 KiB

View File

Before

Width:  |  Height:  |  Size: 86 KiB

After

Width:  |  Height:  |  Size: 86 KiB

View File

Before

Width:  |  Height:  |  Size: 6.8 KiB

After

Width:  |  Height:  |  Size: 6.8 KiB

View File

Before

Width:  |  Height:  |  Size: 164 KiB

After

Width:  |  Height:  |  Size: 164 KiB

View File

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 13 KiB

View File

Before

Width:  |  Height:  |  Size: 128 KiB

After

Width:  |  Height:  |  Size: 128 KiB

View File

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

View File

Before

Width:  |  Height:  |  Size: 89 KiB

After

Width:  |  Height:  |  Size: 89 KiB

View File

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

View File

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 34 KiB

View File

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 34 KiB

View File

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 27 KiB

View File

Before

Width:  |  Height:  |  Size: 8.8 KiB

After

Width:  |  Height:  |  Size: 8.8 KiB

View File

Before

Width:  |  Height:  |  Size: 4.5 KiB

After

Width:  |  Height:  |  Size: 4.5 KiB

View File

Before

Width:  |  Height:  |  Size: 85 KiB

After

Width:  |  Height:  |  Size: 85 KiB

View File

Before

Width:  |  Height:  |  Size: 131 KiB

After

Width:  |  Height:  |  Size: 131 KiB

View File

Before

Width:  |  Height:  |  Size: 33 KiB

After

Width:  |  Height:  |  Size: 33 KiB

View File

Before

Width:  |  Height:  |  Size: 7.7 KiB

After

Width:  |  Height:  |  Size: 7.7 KiB

View File

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 38 KiB

View File

Before

Width:  |  Height:  |  Size: 33 KiB

After

Width:  |  Height:  |  Size: 33 KiB

View File

Before

Width:  |  Height:  |  Size: 33 KiB

After

Width:  |  Height:  |  Size: 33 KiB

View File

Before

Width:  |  Height:  |  Size: 9.5 KiB

After

Width:  |  Height:  |  Size: 9.5 KiB

View File

Before

Width:  |  Height:  |  Size: 9.4 KiB

After

Width:  |  Height:  |  Size: 9.4 KiB

View File

Before

Width:  |  Height:  |  Size: 110 KiB

After

Width:  |  Height:  |  Size: 110 KiB

View File

Before

Width:  |  Height:  |  Size: 148 KiB

After

Width:  |  Height:  |  Size: 148 KiB

View File

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Some files were not shown because too many files have changed in this diff Show More