# Estado compartilhado

Leituras separadas, escritas copy-on-write, inscrições por caminho, batch e sincronização entre réplicas.

<span id="un-dato-varios-renderers"></span>

<span class="section-anchor" id="section-1"></span>

## Um dado, vários renderizadores

O store permite que React, Vue e o shell projetem o mesmo estado. Seus dados são endereçados por caminhos separados por pontos. O dado compartilhado deve descrever o domínio ou a interação; nós DOM, raízes React e instâncias Vue pertencem ao ciclo de vida de cada app.

Para implementar isso em um workspace real, siga [Um contador entre React e Vue](/pt/docs/guides/shared-counter/). Você encontrará os componentes completos, leitura inicial, inscrição, limpeza e os dois lados das permissões: manifesto e shell.

Os próximos fragmentos ficam no módulo cliente do **shell**, onde `wu` é importado de `wu-framework`. Dentro do ciclo de vida de uma **microapp**, use `context.wu` e solicite as permissões `store.read` e `store.write` para os caminhos necessários; o shell deve concedê-las. [Configuração e permissões](/pt/docs/framework/security/) mostra as duas partes. Para observar uma escrita compartilhada, abra [React e Vue](/pt/labs/composition/) e clique em **+1 a partir de React**.

```js
wu.store.set('catalog.selection', { id: 'p-42', quantity: 1 });

const stop = wu.store.on('catalog.selection', (selection) => {
  console.log(selection.id, selection.quantity);
});

wu.store.update('catalog.selection', (selection) => ({
  ...selection,
  quantity: selection.quantity + 1,
}));

await Promise.resolve(); // Permite entregar las notificaciones de la microtask.
stop();
```

`set` e `update` retornam números de sequência. `update` entrega um snapshot separado ao updater e exige um resultado síncrono; uma promise é rejeitada. Para ler dados assíncronos, faça fetch fora e depois escreva o resultado.

<span id="copias-identidad-y-reactividad"></span>

<span class="section-anchor" id="section-2"></span>

## Cópias, identidade e reatividade

`get(path)` e `getSnapshot(path)` retornam dados separados do estado interno. Modificar o objeto recebido não altera o store. As escritas fazem copy-on-write e o estado interno é congelado, de modo que uma escrita descendente não modifica retroativamente entradas anteriores do histórico.

```js
const selection = wu.store.get('catalog.selection');
selection.quantity = 99;
console.log(wu.store.get('catalog.selection').quantity); // Sigue en el store
```

Cada leitura de um objeto produz um novo snapshot. **Não conecte `store.get()` diretamente como um `getSnapshot` do React `useSyncExternalStore` sem manter a identidade entre notificações.** Para uma ponte manual, guarde um snapshot local e substitua-o apenas quando chegar uma notificação; você também pode usar o estado do framework e atualizá-lo por `on`.

As notificações são agendadas em microtasks. Imediatamente após `set`, uma leitura vê o novo estado, mas o callback não necessariamente foi executado. As inscrições não entregam um valor inicial automaticamente: leia uma vez ao conectar.

<span id="suscripción-exacta-y-wildcard"></span>

<span class="section-anchor" id="section-3"></span>

## Inscrição exata e wildcard

| Inscrição | O que observa | Callback |
| --- | --- | --- |
| `on('catalog', fn)` | Escrita em catalog e em caminhos descendentes | `(valorDeCatalog, 'catalog')` |
| `on('catalog.selection', fn)` | Esse caminho e seus descendentes | `(valorDelPath, pathSuscrito)` |
| `on('catalog.*', fn)` | Um segmento imediatamente abaixo de catalog | `({ path, value })` |
| `on('*', fn)` | Todas as escritas | `({ path, value })` |

`catalog.*` não corresponde a `catalog` nem a `catalog.selection.quantity`: o wildcard substitui um segmento, não a subárvore inteira. Ao substituir um pai, todas as inscrições exatas dos filhos não são notificadas automaticamente como se tivessem sido escritas separadamente. Para um componente que projeta um objeto completo, inscreva-o no caminho do objeto e leia-o novamente.

Selecione o payload conforme a inscrição, mesmo quando uma assinatura genérica TypeScript não distinguir os dois casos:

```js
const stopValue = wu.store.on('catalog.selection', (selection) => {
  console.log('Selección actual', selection);
});
const stopChanges = wu.store.on('catalog.*', ({ path, value }) => {
  console.log('Campo escrito', path, value);
});

wu.store.set('catalog.selection', { id: 'p-42', quantity: 2 });
await Promise.resolve();
// Al retirar el consumidor, libera ambas suscripciones.
stopValue();
stopChanges();
```

<span id="valores-admitidos"></span>

<span class="section-anchor" id="section-4"></span>

## Valores aceitos

O codec local aceita `null`, booleanos, strings, números finitos, bigint escalar, arrays densos e objetos simples de dados. Rejeita funções, símbolos, `undefined` como valor escrito, ciclos, accessors, arrays esparsos, instâncias como Date/Map e estruturas que excedem 256 níveis ou 100 000 nós.

Uma leitura de um caminho inexistente pode retornar `undefined`. Para datas compartilhadas, use strings ISO; para IDs grandes que sairão por JSON, use strings. Bigint local não implica compatibilidade com sync, cujas mensagens exigem dados JSON seguros.

Os caminhos rejeitam segmentos vazios e escritas em `__proto__`, `prototype` ou `constructor`. Uma leitura perigosa retorna `undefined` sem expor objetos internos.

<span id="batch-y-límites-de-atomicidad"></span>

<span class="section-anchor" id="section-5"></span>

## Batch e limites de atomicidade

```js
wu.store.batch({
  'catalog.selection': { id: 'p-42', quantity: 2 },
  'checkout.step': 'review',
});
```

`batch` valida e separa todos os caminhos/valores antes de realizar uma escrita. Se essa verificação prévia falhar, não altera estado, sequência ou métricas. Após a validação, cada entrada mantém sua própria escrita e sequência; os taps de baixo nível observam commits individuais. Não é uma transação de banco de dados nem uma transação sobre efeitos externos.

<span id="sincronización-optativa"></span>

<span class="section-anchor" id="section-6"></span>

## Sincronização opcional

```js
const sync = wu.store.sync({ transport: 'broadcast', room: 'docs-demo' });
await sync.ready();

// Escribir después de conectar registra estos paths para otros participantes.
wu.store.set('demo.counter', 1);
console.log(sync.status());

// Al abandonar la sesión:
sync.stop();
```

O módulo é carregado de forma adiada. BroadcastChannel permite comunicação entre contextos compatíveis do navegador; WebSocket e transportes personalizados ampliam o transporte. A resolução de conflitos usa Last Writer Wins por caminho, ordenado por relógio Lamport e identificador do site.

Só são rastreadas escritas feitas após iniciar sync. Paths pai/filho compartilham a ordem Lamport: um pai mais novo oculta filhos anteriores, e um filho mais novo sobrevive a um pai tardio. Snapshots de entrada preservam essa ordem. Substituições da raiz não são sincronizadas, e a persistência não é automática.

O transporte WebSocket incorporado exige `authorizePeer(message)` síncrono, a menos que o host declare `trustTransport: true` porque um relay já autenticou os participantes. Compartilhar um nome de sala não autoriza dados.

<span id="referencia-de-implementación"></span>

<span class="section-anchor" id="section-7"></span>

## Referência de implementação

Codec: `src/core/wu-store.js`; leitura/escrita: `src/core/wu-store.js`; atualização: `src/core/wu-store.js`; batch: `src/core/wu-store.js`; notificações: `src/core/wu-store.js`; sync: `src/core/wu-store-sync.js`. Testes: `tests/unit/store.test.js`, `tests/unit/store-snapshot-identity.test.js`, `tests/unit/store-subscription-semantics.test.js`, `tests/unit/store-sync.test.js`.

<span id="alcance-de-las-réplicas"></span>

<span class="section-anchor" id="section-8"></span>

## Escopo das réplicas

`paths` limita operações de entrada e saída por prefixos de segmentos: `cart` inclui esse path e seus descendentes. `authorizeOp(op, message)` deve retornar `true` de forma síncrona para aceitar uma operação de entrada. Escritas remotas vão diretamente ao store; grants de apps não as autorizam. O runtime também limita saltos do relógio Lamport.

```js
const sync = wu.store.sync({
  transport: 'broadcast', room: 'cart-demo', paths: ['cart'],
  authorizeOp: op => op.path === 'cart.count' && Number.isSafeInteger(op.value),
});
await sync.ready();
wu.store.set('cart.count', 1);
// Teardown:
sync.stop();
```