Skip to Content
PortuguêsDocumentação JumentixPacotes@jumentix/canaGuia de usoStorage e recuperação de crash

Storage e recuperação de crash

O Cana armazena dados duráveis no navegador e expõe sinais suficientes para a aplicação decidir quando oferecer backup, limpeza ou reconciliação de dados locais.

Avaliação de storage

import { createClient, type CanaSchema } from '@jumentix/cana';

const schema: CanaSchema = {
  version: 1,
  stores: [
    { name: 'categories', keyPath: 'id' },
    { name: 'tasks', keyPath: 'id', indexes: [{ name: 'byCategory', keyPath: 'categoryId' }] }
  ]
};

const client = createClient({
  name: 'tasks-storage-demo',
  schema
});

await client.open();

const storage = await client.storageState();
const durability = await client.durabilityAssessment();

console.log({
  backend: client.backend,
  storage,
  durability
});

Use os sinais de storage e durabilidade para decidir quando a UI deve oferecer export, limpeza ou nova tentativa.

Export e import

const exported = await client.exportAll();

const restored = createClient({
  name: 'tasks-restored-demo',
  schema
});

await restored.open();
await restored.importAll(exported);

console.log({
  categories: await restored.table('categories').query(),
  tasks: await restored.table('tasks').query()
});

Use export/import para backup controlado pelo usuário, migração entre versões da aplicação ou diagnóstico.

Resolver uma escrita incerta

const ledgerClient = createClient({
  name: 'tasks-ledger-demo',
  schema,
  operationLedger: true
});

await ledgerClient.open();

const tx = await ledgerClient.transaction('readwrite', ['categories', 'tasks'], async (scope) => {
  const now = Date.now();
  await scope.table('categories').put({
    id: 'support',
    name: 'Support',
    color: '#0f766e',
    createdAt: now,
    updatedAt: now
  });
  await scope.table('tasks').put({
    id: 'support-1',
    title: 'Verify uncertain writes',
    categoryId: 'support',
    completed: false,
    priority: 'high',
    createdAt: now,
    updatedAt: now
  });
  return 'done';
});

const resolved = await ledgerClient.resolveWrite(tx.correlationId, tx.attemptedAt);

console.log({
  outcome: tx.outcome,
  resolved
});

resolveWrite() serve para escritas reportadas como unknown, normalmente depois que um worker, aba ou conexão morre antes de o caller receber o resultado.

Execute aqui

Avaliacao de storage

Leia storageState e durabilityAssessment depois de gravar Task.

const client = cana.createClient({
  name: dbName,
  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' },
        { name: 'byUpdatedAt', keyPath: 'updatedAt' }
      ]
    }
  ]
}
});
await client.open();
await client.table('categories').add({ id: 'docs', name: 'Docs', color: '#0f766e' });
await client.table('tasks').add({
  id: 'docs-1',
  title: 'Durable data',
  categoryId: 'docs',
  completed: false,
  priority: 'high',
  createdAt: 1,
  updatedAt: 1
});
const storage = await client.storageState();
const durability = await client.durabilityAssessment();
return { backend: client.backend, storage, durability };

Operation ledger

Resolva uma escrita de Task commitada com operation ledger ligado.

const client = cana.createClient({
  name: dbName,
  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' },
        { name: 'byUpdatedAt', keyPath: 'updatedAt' }
      ]
    }
  ]
},
  operationLedger: true
});
await client.open();
const tx = await client.transaction('readwrite', ['categories', 'tasks'], async (scope) => {
  const now = Date.now();
  await scope.table('categories').put({ id: 'docs', name: 'Docs', color: '#0f766e' });
  await scope.table('tasks').put({
    id: 'docs-1',
    title: 'Reconcile uncertain write',
    categoryId: 'docs',
    completed: false,
    priority: 'high',
    createdAt: now,
    updatedAt: now
  });
  return 'wrote';
});
const resolved = await client.resolveWrite(tx.correlationId, tx.attemptedAt);
return { outcome: tx.outcome, resolved, task: await client.table('tasks').get('docs-1') };

Export

Exporte as stores Category e Task como dados puros.

const client = cana.createClient({
  name: dbName,
  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' },
        { name: 'byUpdatedAt', keyPath: 'updatedAt' }
      ]
    }
  ]
}
});
await client.open();
const now = Date.now();
await client.table('categories').bulkAdd([
  { id: 'work', name: 'Work', color: '#2563eb', createdAt: now, updatedAt: now },
  { id: 'home', name: 'Home', color: '#16a34a', createdAt: now, updatedAt: now }
]);
await client.table('tasks').bulkAdd([
  {
    id: 'task-1',
    title: 'Write the Cana tutorial',
    categoryId: 'work',
    completed: false,
    priority: 'high',
    createdAt: now,
    updatedAt: now
  },
  {
    id: 'task-2',
    title: 'Review category filters',
    categoryId: 'home',
    completed: true,
    priority: 'medium',
    createdAt: now,
    updatedAt: now + 1
  }
]);
const dump = await client.exportAll();
return dump;

Próximo

Continue em workers e testes.