Skip to content

Monorepo Layout

memrynote is a pnpm + Turborepo monorepo. Apps live under apps/, reusable domain and storage code lives under packages/, and contracts remain the source of truth for app boundaries.

Apps

PathPurposeStack
apps/climemry CLI used by desktop headless mode and standalone runsNode 24, TypeScript
apps/desktopElectron desktop appElectron 39, React 19, Vite, BlockNote, Yjs
apps/sync-serverCloudflare Workers sync APIWorkers + Hono, D1, R2
apps/docsThis documentation siteVitePress 1.6

Packages

PathPurpose
packages/app-coreShared vault-opening and command services used by non-renderer clients such as the CLI.
packages/contractsZod-typed IPC and HTTP API contracts. The single source of truth for renderer↔main and client↔server boundaries.
packages/db-schemaDrizzle ORM schemas for the data and index databases plus migration files.
packages/storage-*Shared persistence adapters for local vault files and local data database access.
packages/domain-*UI-agnostic domain command/query logic.
packages/sharedTiny set of shared utilities. Kept intentionally small to avoid coupling.

Shared Assets

Reusable brand files live in assets/brand/memry. The desktop icon generator reads assets/brand/memry/icon-color.png and writes the packaged app icons under apps/desktop/build/; pnpm --dir apps/desktop generate:icons writes the default light icons, and pnpm --dir apps/desktop generate:icons --dark overwrites those same Electron icon files with the dark icon treatment. It also writes the depth-treated social profile PNGs under assets/brand/memry/social/profile-image.png, assets/brand/memry/social/profile-square.png, and assets/brand/memry/social/profile-rectangle.png, plus dark-theme variants at assets/brand/memry/social/profile-image-dark.png, assets/brand/memry/social/profile-square-dark.png, and assets/brand/memry/social/profile-rectangle-dark.png, while landing and social surfaces should reuse the shared logo and icon sources instead of keeping app-local copies.

Tooling

  • Package manager: pnpm 10.30+ (workspace-aware)
  • Task runner: Turborepo for orchestration and caching
  • Node: pinned via .nvmrc (24.x)
  • TypeScript: strict mode across every package

Cross-Cutting Scripts

bash
# Dev
pnpm dev                # desktop dev (electron-vite)
pnpm staging            # desktop dev pointed at staging sync
pnpm dev:desktop        # desktop dev (alias)
pnpm dev:sync-server    # cloudflare worker dev
pnpm test:cli           # app-core + CLI node tests

# Deploy
pnpm run deploy:sync:staging     # deploy staging sync worker
pnpm run deploy:sync:production  # deploy production sync worker

# Verify
pnpm lint               # ESLint flat config
pnpm typecheck          # TypeScript across apps/packages
pnpm test               # app-core, CLI, desktop, and sync-server tests
pnpm test:e2e           # Playwright E2E (Electron)
pnpm ipc:check          # validate renderer/main contract types
pnpm ipc:generate       # regenerate IPC invoke map

# Database
pnpm db:generate        # Drizzle schema → migration SQL
pnpm db:push            # apply migrations
pnpm db:studio          # Drizzle Studio GUI

Runtime Environments

Desktop runtime config is selected with MEMRY_ENV:

  • development is the local desktop app talking to the local Wrangler sync server at http://localhost:8787.
  • staging is the desktop staging command talking to Cloudflare staging at https://sync-staging.memrynote.com.
  • production is reserved for packaged release builds and talks to https://sync.memrynote.com.

Production desktop packaging must use the production sync URL only; release builds fail if the packaged runtime config is missing or points at localhost or staging.

Why Turborepo

Most actions can be cached and parallelized:

  • lint, typecheck, test per package
  • Builds can fan out
  • The cache is keyed on inputs, so unchanged packages are skipped

Why pnpm

  • Strict module resolution (no phantom dependencies)
  • Workspace protocol (workspace:*) makes cross-package imports explicit
  • Faster CI installs vs npm/yarn

Boundaries

  • Renderer never imports from apps/desktop/src/main/* and vice versa.
  • Both sides import shared types from @memry/contracts.
  • packages/app-core owns non-UI vault operations for standalone clients. Keep Electron-only concerns in apps/desktop; desktop --cli mode should call the CLI/app-core path and inject desktop vault selection state rather than duplicate command logic.
  • packages/db-schema is consumed by desktop main, app-core, and tests.
  • apps/sync-server and apps/desktop only share types via packages/contracts — never code.

Released under the GNU GPL v3.0.