Jumentix
Frontend blueprint

Build installable products that keep working offline

Pair generated SDK clients with a SPA or PWA architecture, IndexedDB persistence, and the same contract vocabulary as the backend.

Jumentix open-source mascot

What this blueprint gives your team

  • Offline-first data workflows
  • REST, Socket.IO, and gRPC client packages
  • Shared governance across frontend and backend
bun install
bun run cli
bun run dev:express

From zero to first MVP

A practical launch sequence for this blueprint

Use this path when the first MVP must run in the browser, keep local state, and later synchronize with backend contracts.

Model local records

Start with Category and Task tables, one filter, one create action, and one event the UI can listen to.

  • Define the Cana schema: categories and tasks stores with id keyPaths.
  • Add the byCategory and byCompleted indexes the UI filters will use.
  • Freeze schema version 1 and write the upgrade rule for the next version.
  • Write acceptance criteria: records survive a browser reload.

MVP output: browser data model with a visible workflow.

const schema = {
  version: 1,
  stores: [
    { name: 'categories', keyPath: 'id', indexes: [{ name: 'byName', keyPath: 'name', unique: true }] },
    {
      name: 'tasks',
      keyPath: 'id',
      indexes: [
        { name: 'byCategory', keyPath: 'categoryId' },
        { name: 'byCompleted', keyPath: 'completed' }
      ]
    }
  ]
} as const;

Definition of done for the first MVP

  • The create/list workflow works with the network disabled.
  • Components re-render from committed Cana change events.
  • The sync assumption is documented: local-only, sync-later, or API-backed.
  • Records survive a browser reload — durable IndexedDB, not localStorage.

Deliberately out of scope

  • Advanced conflict resolution for concurrent edits.
  • Push notifications and background sync.
  • Multi-device identity and session management.
  • IndexedDB schema migrations beyond version 1.

MVP scope

Keep the first release small enough to prove

Two local tables

Category and Task prove relationship, filtering, and event-driven UI updates.

Data slice

One state library

Use Context, Redux, or Pinia as the first integration surface.

UI slice

One sync assumption

Document whether the MVP is local-only, sync-later, or API-backed.

Product slice
Proof areaQuestion to answerJumentix mechanismMVP evidence
Local dataCan the app create and read records with no server?Cana/browser in-memory playground and local persistence checks.The first workflow works offline.
State updatesDo components refresh from Cana events?React/Vue state-management examples and event listener assertions.UI stays consistent with local data.
Future backendCan the same vocabulary map to API contracts later?Generated SDK names and Category/Task contract parity.The frontend MVP does not invent a separate domain.

Practical implementation

Complete code for the first working slice

These examples keep Category and Task as the product vocabulary and show the controller, contract, client, worker, or state layer needed to reach a runnable MVP.

type Category = { id: string; name: string };
type Task = { id: string; title: string; categoryId: string; completed: boolean };

const categories = new Map<string, Category>([
  ['work', { id: 'work', name: 'Work' }]
]);
const tasks = new Map<string, Task>();
const listeners = new Set<() => void>();

export function subscribe(listener: () => void) {
  listeners.add(listener);
  return () => listeners.delete(listener);
}

export function createTask(input: { title: string; categoryId: string }) {
  if (!categories.has(input.categoryId)) throw new Error('category not found');
  const task = { id: crypto.randomUUID(), completed: false, ...input };
  tasks.set(task.id, task);
  listeners.forEach((listener) => listener());
  return task;
}

export function listTaskCards() {
  return [...tasks.values()].map((task) => ({
    ...task,
    category: categories.get(task.categoryId)
  }));
}

Build the product. Keep the architecture.

Explore the source, run the factory locally, and turn your next Node.js service into a repeatable platform capability.