@neetru/sdk 3.0.2 → 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/CHANGELOG.md +554 -530
  2. package/dist/auth.cjs +60 -39
  3. package/dist/auth.cjs.map +1 -1
  4. package/dist/auth.d.cts +1 -2
  5. package/dist/auth.d.ts +1 -2
  6. package/dist/auth.mjs +60 -39
  7. package/dist/auth.mjs.map +1 -1
  8. package/dist/catalog.d.cts +1 -2
  9. package/dist/catalog.d.ts +1 -2
  10. package/dist/checkout.d.cts +1 -2
  11. package/dist/checkout.d.ts +1 -2
  12. package/dist/db-react.cjs +15 -5
  13. package/dist/db-react.cjs.map +1 -1
  14. package/dist/db-react.d.cts +35 -13
  15. package/dist/db-react.d.ts +35 -13
  16. package/dist/db-react.mjs +15 -5
  17. package/dist/db-react.mjs.map +1 -1
  18. package/dist/db.cjs +71 -50
  19. package/dist/db.cjs.map +1 -1
  20. package/dist/db.d.cts +1 -2
  21. package/dist/db.d.ts +1 -2
  22. package/dist/db.mjs +71 -50
  23. package/dist/db.mjs.map +1 -1
  24. package/dist/entitlements.d.cts +1 -2
  25. package/dist/entitlements.d.ts +1 -2
  26. package/dist/firestore-compat.cjs +399 -0
  27. package/dist/firestore-compat.cjs.map +1 -0
  28. package/dist/firestore-compat.d.cts +294 -0
  29. package/dist/firestore-compat.d.ts +294 -0
  30. package/dist/firestore-compat.mjs +371 -0
  31. package/dist/firestore-compat.mjs.map +1 -0
  32. package/dist/index.cjs +72 -51
  33. package/dist/index.cjs.map +1 -1
  34. package/dist/index.d.cts +2 -3
  35. package/dist/index.d.ts +2 -3
  36. package/dist/index.mjs +72 -51
  37. package/dist/index.mjs.map +1 -1
  38. package/dist/mocks.d.cts +1 -2
  39. package/dist/mocks.d.ts +1 -2
  40. package/dist/notifications.d.cts +1 -2
  41. package/dist/notifications.d.ts +1 -2
  42. package/dist/react.cjs +267 -0
  43. package/dist/react.cjs.map +1 -1
  44. package/dist/react.d.cts +117 -3
  45. package/dist/react.d.ts +117 -3
  46. package/dist/react.mjs +265 -1
  47. package/dist/react.mjs.map +1 -1
  48. package/dist/support.d.cts +1 -2
  49. package/dist/support.d.ts +1 -2
  50. package/dist/telemetry.d.cts +1 -2
  51. package/dist/telemetry.d.ts +1 -2
  52. package/dist/{types-D7zVkO3n.d.ts → types-BRv8wBxX.d.ts} +581 -2
  53. package/dist/{types-Dc4bjrrt.d.cts → types-nwErcRX8.d.cts} +581 -2
  54. package/dist/usage.d.cts +1 -2
  55. package/dist/usage.d.ts +1 -2
  56. package/dist/webhooks.d.cts +1 -2
  57. package/dist/webhooks.d.ts +1 -2
  58. package/package.json +156 -151
  59. package/dist/collection-ref-BDdfD87k.d.cts +0 -581
  60. package/dist/collection-ref-BDdfD87k.d.ts +0 -581
@@ -1,581 +0,0 @@
1
- /**
2
- * Tipos internos da camada offline do @neetru/sdk.
3
- *
4
- * Estes tipos SÃO internos ao módulo `db/offline/` — não fazem parte da
5
- * superfície pública do SDK. A superfície pública é o `DbCollectionRef` e
6
- * o `DbSnapshot` de `src/types.ts`.
7
- *
8
- * Derivados de:
9
- * - I3-sdk-offline.md §3 (store schemas)
10
- * - I3-sdk-offline.md §4 (fila de escritas)
11
- * - I3-sdk-offline.md §5 (query descriptor)
12
- * - I3-sdk-offline.md §7 (conflito LWW)
13
- * - 02-sdk.md §3.2 (DbSnapshot, DbWhereFilter, DbQuery)
14
- */
15
- /** Identificador de um documento (string opaca). */
16
- type DocId = string;
17
- /** Identificador de uma mutação (UUID v4 client-generated). */
18
- type MutationId = string;
19
- /** Operações possíveis na fila de escritas. */
20
- type MutationKind = 'add' | 'set' | 'update' | 'remove';
21
- /**
22
- * Uma mutação enfileirada (escrita durável pendente).
23
- * Equivalente a `QueuedMutation` do I3 §3.3 (`mutations` store).
24
- */
25
- interface Mutation {
26
- /** Número de sequência autoincrement — define a ordem total da fila. */
27
- seq: number;
28
- /** UUID v4 client-generated — chave de idempotência. */
29
- mutationId: MutationId;
30
- /** Nome da coleção. */
31
- collection: string;
32
- /** ID do documento alvo (client-gen para `add`, existente para os demais). */
33
- docId: DocId;
34
- /** Operação. */
35
- op: MutationKind;
36
- /** Dados da operação. `null` para `remove`. */
37
- payload: Record<string, unknown> | null;
38
- /**
39
- * `serverVersion` que o cliente viu ao escrever.
40
- * `null` para `add` (doc não existia) ou quando o cliente nunca sincronizou.
41
- * Usado pelo servidor para detectar conflito (I3 §7.4).
42
- */
43
- baseVersion: string | null;
44
- /** Epoch ms do relógio do CLIENTE — só para diagnóstico e ordenação local. */
45
- enqueuedAt: number;
46
- /** Número de tentativas de replay. */
47
- attempts: number;
48
- /** Último erro de replay (string). `null` se nenhuma tentativa ainda. */
49
- lastError: string | null;
50
- /** Estado de envio desta mutação. */
51
- status: 'queued' | 'inflight' | 'failed';
52
- /** Agrupa mutações de um `batch()` atômico. `null` se mutação individual. */
53
- batchId: string | null;
54
- }
55
- /**
56
- * Razão pela qual uma escrita foi descartada no LWW (I3 §3.3 `conflict_log`).
57
- */
58
- type ConflictReason = 'lww_server_newer' | 'rejected_permission' | 'rejected_validation';
59
- /**
60
- * Registro de uma escrita perdedora — gravado no `conflict_log` (I3 §3.3).
61
- * Entregue ao produto via `onWriteResult` (status: `'superseded'`).
62
- */
63
- interface ConflictRecord {
64
- id?: number;
65
- collection: string;
66
- docId: DocId;
67
- mutationId: MutationId;
68
- losingData: Record<string, unknown>;
69
- winningData: Record<string, unknown>;
70
- reason: ConflictReason;
71
- detectedAt: number;
72
- delivered: boolean;
73
- }
74
- /**
75
- * Estado de sincronização da camada offline (I3 §10 / 02-sdk §3.4).
76
- * Exposto em `client.db.syncState`.
77
- *
78
- * - `idle` — online e em repouso (sem sync ativo).
79
- * - `syncing` — sync em progresso (push + pull + realtime).
80
- * - `offline` — conexão perdida ou inexistente.
81
- * - `error` — erro persistente após retries (reservado para uso futuro).
82
- */
83
- type SyncStatus = 'idle' | 'offline' | 'syncing' | 'error';
84
- interface SyncState {
85
- status: SyncStatus;
86
- pendingWrites: number;
87
- lastSyncedAt: number | null;
88
- isLeaderTab: boolean;
89
- }
90
- /** Função para cancelar uma subscrição (onSnapshot, onDoc, onSyncStateChanged). */
91
- type Unsubscribe = () => void;
92
-
93
- /**
94
- * SyncEngine — orquestrador da camada offline do @neetru/sdk.
95
- *
96
- * Implementa as 3 fases de sincronização definidas em I3 §6:
97
- *
98
- * FASE 1 — DRENAR A FILA (push)
99
- * Envia mutações pendentes ao Core via SyncTransport.
100
- * Cada mutação é marcada inflight, enviada e depois:
101
- * - CONFIRMADA → removida da fila (markApplied)
102
- * - SUPERADA (LWW)→ ConflictRecord gravado, cache atualizado
103
- * - REJEITADA → resolveRejected + ConflictRecord + removida
104
- * - FALHA TRANSIENTE → markRetry + PARA a drenagem (ordem preservada)
105
- *
106
- * FASE 2 — RECONCILIAR O CACHE (pull)
107
- * Busca mudanças do servidor desde o watermark ou resume token.
108
- * Cada documento passa pelo ConflictResolver (LWW) e é gravado no LocalStore.
109
- * Changes são emitidos pelo ChangeBus.
110
- * Se `resyncRequired` → aciona full resync em vez de pull incremental.
111
- *
112
- * FASE 3 — REABRIR LISTENERS (realtime)
113
- * Atualiza SyncState para refletir que o cache está reconciliado.
114
- * O ChangeBus já foi alimentado nas fases anteriores — os listeners
115
- * ativos na camada superior (onSnapshot/onDoc) recebem snapshots frescos.
116
- *
117
- * Garantias:
118
- * - SOMENTE a aba LÍDER executa o sync (TabCoordinator.isLeader()).
119
- * - Re-entrante-safe: um sync em progresso bloqueia novos disparos.
120
- * - Fail-closed: falha transiente retém a mutação; falha de pull não
121
- * corrompe o cache nem o watermark.
122
- * - Idempotente: o SyncTransport usa mutationId como chave de idempotência.
123
- *
124
- * O SyncTransport é INJETADO — a ligação real com o Core acontece na camada
125
- * de adaptação, não aqui. Isso torna o SyncEngine 100% testável com fake.
126
- */
127
-
128
- /**
129
- * Documento retornado pelo servidor em push/pull/resync.
130
- */
131
- interface ServerDoc {
132
- collection: string;
133
- id: string;
134
- data: Record<string, unknown>;
135
- serverVersion: string;
136
- serverTimestamp: number;
137
- /** `true` se o doc foi deletado no servidor. */
138
- deleted: boolean;
139
- }
140
- /**
141
- * Resultado individual de uma mutação enviada ao Core (FASE 1).
142
- */
143
- type MutationResult = {
144
- mutationId: string;
145
- outcome: 'confirmed';
146
- serverVersion: string;
147
- serverTimestamp: number;
148
- } | {
149
- mutationId: string;
150
- outcome: 'superseded';
151
- serverVersion: string;
152
- serverTimestamp: number;
153
- serverData: Record<string, unknown>;
154
- } | {
155
- mutationId: string;
156
- outcome: 'rejected';
157
- reason: 'rejected_permission' | 'rejected_validation';
158
- serverVersion: string | null;
159
- serverData: Record<string, unknown> | null;
160
- };
161
- /** Retorno de pushMutations. */
162
- interface PushMutationsResult {
163
- results: MutationResult[];
164
- }
165
- /** Retorno de pullChanges. */
166
- interface PullChangesResult {
167
- docs: ServerDoc[];
168
- newWatermark: number | null;
169
- /** `true` quando o resume token / watermark ficou inválido (gap grande). */
170
- resyncRequired: boolean;
171
- }
172
- /** Retorno de fullResync. */
173
- interface FullResyncResult {
174
- docs: ServerDoc[];
175
- newWatermark: number | null;
176
- }
177
- /**
178
- * Contrato do transporte de sync — injetado no SyncEngine.
179
- *
180
- * A implementação real usa os endpoints `/api/sdk/v1/db/*` do Core.
181
- * Em testes, usa-se um FakeSyncTransport.
182
- *
183
- * Per I3 §6:
184
- * - `pushMutations` → FASE 1: envia mutações em ordem de seq.
185
- * - `pullChanges` → FASE 2: busca delta desde o watermark/resumeToken.
186
- * - `fullResync` → FASE 2 (fallback): lista completa das coleções ativas.
187
- */
188
- interface SyncTransport {
189
- /**
190
- * Envia um lote de mutações ao Core.
191
- * O Core aplica, verifica conflito LWW e retorna um resultado por mutação.
192
- * A ordem do array deve ser preservada (seq crescente).
193
- */
194
- pushMutations(mutations: Mutation[]): Promise<PushMutationsResult>;
195
- /**
196
- * Busca documentos modificados no servidor desde `sinceWatermark`.
197
- * `sinceWatermark === null` indica primeiro sync (sem base conhecida).
198
- * `resumeToken` é o token de change stream (nosql-vm); pode ser null.
199
- *
200
- * Retorna `resyncRequired: true` se o gap é grande demais para pull incremental.
201
- */
202
- pullChanges(sinceWatermark: number | null, resumeToken: string | null): Promise<PullChangesResult>;
203
- /**
204
- * Faz um full resync das coleções informadas.
205
- * Retorna TODOS os documentos atuais (paginado internamente pelo transporte).
206
- * Docs ausentes na resposta foram deletados durante o gap.
207
- */
208
- fullResync(collections: string[]): Promise<FullResyncResult>;
209
- /**
210
- * (Opcional) Registra uma subscription realtime para uma coleção específica.
211
- *
212
- * Implementado pelo WebSocket transport (nosql-vm): chama
213
- * `NeetruRealtimeClient.subscribe()` e alimenta os frames delta/resync
214
- * no `ChangeBus` da camada offline.
215
- *
216
- * Transportes REST e Firestore não implementam este método (undefined).
217
- * O chamador (`DbCollectionRefImpl.onSnapshot`) verifica antes de invocar.
218
- *
219
- * @param collection - Nome da coleção a subscrever.
220
- * @param onChange - Callback que recebe changes quando um delta/resync chega.
221
- * `null` payload = resync full necessário.
222
- * @returns Função de unsubscribe.
223
- */
224
- subscribeCollection?: (collection: string, onChange: (changes: Array<{
225
- type: 'added' | 'modified' | 'removed';
226
- docId: string;
227
- data: Record<string, unknown> | null;
228
- }>, needsResync: boolean) => void) => () => void;
229
- }
230
-
231
- /**
232
- * field-value.ts — Sentinels de escrita server-side para o Mundo Documentos.
233
- *
234
- * Resolve duas lacunas confirmadas (bug_05aedb8b) da superfície `client.db`:
235
- *
236
- * 1. **Timestamp resolvido no servidor** — antes deste módulo, o produto que
237
- * quisesse `createdAt`/`updatedAt` confiáveis tinha que usar `Date.now()`
238
- * no CLIENTE, que é spoofável e sofre clock-skew. Agora:
239
- *
240
- * await orders.add({ total: 99, createdAt: serverTimestamp() });
241
- *
242
- * O marcador atravessa o transporte REST como um objeto JSON namespaceado
243
- * e o Core o substitui por `FieldValue.serverTimestamp()` no momento da
244
- * escrita — relógio do SERVIDOR, não do cliente.
245
- *
246
- * 2. **Increment atômico** — antes, `update(id, { n: x+1 })` era read-modify-write
247
- * no cliente, sujeito a lost-update sob concorrência. Agora:
248
- *
249
- * await counters.update('global', { hits: increment(1) });
250
- *
251
- * O Core aplica `FieldValue.increment(1)` atomicamente no documento.
252
- *
253
- * ## Contrato de fio (wire format)
254
- *
255
- * Os sentinels são objetos JSON simples — sobrevivem `JSON.stringify` no
256
- * transporte REST e ao `IndexedDB structured clone` da camada offline:
257
- *
258
- * serverTimestamp() → `{ "__neetru__": "serverTimestamp" }`
259
- * increment(n) → `{ "__neetru__": "increment", "operand": n }`
260
- *
261
- * A chave-âncora `__neetru__` é deliberadamente improvável de colidir com
262
- * dados de produto. O Core valida e resolve esses marcadores; qualquer objeto
263
- * com `__neetru__` desconhecido é rejeitado server-side (fail-closed).
264
- *
265
- * ## Aplicação otimista (offline-first)
266
- *
267
- * A camada offline aplica uma APROXIMAÇÃO local imediata (para a UI não piscar):
268
- * - `serverTimestamp()` → `Date.now()` local (substituído pelo valor real no sync).
269
- * - `increment(n)` → soma `n` ao valor numérico atual do campo (0 se ausente).
270
- * O valor canônico vem sempre do servidor no pull/confirm subsequente.
271
- *
272
- * @module
273
- */
274
- /** Chave-âncora dos marcadores de sentinel no wire format. */
275
- declare const NEETRU_SENTINEL_KEY: "__neetru__";
276
- /** Marcador de timestamp resolvido pelo servidor. */
277
- interface ServerTimestampSentinel {
278
- readonly __neetru__: 'serverTimestamp';
279
- }
280
- /** Marcador de incremento atômico aplicado pelo servidor. */
281
- interface IncrementSentinel {
282
- readonly __neetru__: 'increment';
283
- /** Delta a somar (pode ser negativo para decremento). */
284
- readonly operand: number;
285
- }
286
- /**
287
- * União de todos os sentinels de campo. Use `serverTimestamp()` / `increment()`
288
- * para construir — nunca monte o objeto à mão.
289
- */
290
- type FieldSentinel = ServerTimestampSentinel | IncrementSentinel;
291
- /**
292
- * Sentinel que instrui o servidor a gravar o timestamp do MOMENTO da escrita
293
- * usando o relógio do servidor (não do cliente).
294
- *
295
- * @example
296
- * ```ts
297
- * import { serverTimestamp } from '@neetru/sdk/db';
298
- * await orders.add({ total: 99, createdAt: serverTimestamp() });
299
- * ```
300
- */
301
- declare function serverTimestamp(): ServerTimestampSentinel;
302
- /**
303
- * Sentinel que instrui o servidor a incrementar atomicamente o campo numérico
304
- * por `n` (negativo decrementa). Se o campo não existir/não for numérico, o
305
- * servidor o trata como `0` antes de somar (semântica `FieldValue.increment`).
306
- *
307
- * @param n — delta inteiro ou fracionário a somar.
308
- * @example
309
- * ```ts
310
- * import { increment } from '@neetru/sdk/db';
311
- * await counters.update('global', { hits: increment(1) });
312
- * await wallet.update(uid, { balance: increment(-50) });
313
- * ```
314
- */
315
- declare function increment(n: number): IncrementSentinel;
316
- /** `true` se `v` é qualquer sentinel de campo Neetru. */
317
- declare function isFieldSentinel(v: unknown): v is FieldSentinel;
318
- /** `true` se `v` é o sentinel `serverTimestamp()`. */
319
- declare function isServerTimestampSentinel(v: unknown): v is ServerTimestampSentinel;
320
- /** `true` se `v` é o sentinel `increment(n)` com `operand` numérico válido. */
321
- declare function isIncrementSentinel(v: unknown): v is IncrementSentinel;
322
-
323
- /**
324
- * DbCollectionRef — superfície de Documentos offline-first do @neetru/sdk (M2).
325
- *
326
- * Implementa a API pública conforme especificada em:
327
- * - 02-sdk.md §3.2 (contrato TypeScript DbCollectionRef)
328
- * - I2-mundo-documentos.md §3.2 (superfície dupla CRUD + realtime)
329
- * - I3-sdk-offline.md §10 (camada offline, lifecycle, SyncState)
330
- *
331
- * ### Semântica de Promise de escrita (02-sdk.md §3.2 nota negrito)
332
- * `add/set/update/remove` resolvem quando a escrita está **durável localmente**
333
- * (cache + fila), NÃO quando o servidor confirma. Isso é offline-first por design:
334
- * sem rede, `add()` resolve imediatamente. Para saber quando sincronizou, o produto
335
- * observa `onWriteResult` (future / SyncEngine). A Promise só rejeita por:
336
- * - validação síncrona (nome de coleção inválido, id vazio)
337
- * - `offline_quota_exceeded` (IndexedDB cheio)
338
- *
339
- * ### Injeção do transporte realtime
340
- * O `RealtimeTransport` é injetado via `createOfflineDocumentsNamespace({ transport })`.
341
- * A implementação atual usa o `SyncEngine` para push/pull (sync offline ↔ servidor).
342
- * Transportes futuros (Firestore Web SDK / WebSocket gateway) são injetados aqui —
343
- * sem alterar a superfície pública.
344
- */
345
-
346
- /**
347
- * Permite que cada campo de `T` aceite, além do seu tipo declarado, um sentinel
348
- * de escrita server-side (`serverTimestamp()` / `increment()`).
349
- *
350
- * Sem isso, um produto que tipa `createdAt: number` veria erro de tipo ao passar
351
- * `serverTimestamp()`. Esta projeção mantém o autocomplete dos campos do produto
352
- * e ainda autoriza os sentinels onde fizer sentido.
353
- *
354
- * @example
355
- * ```ts
356
- * interface Order { total: number; createdAt: number; hits: number }
357
- * orders.add({ total: 10, createdAt: serverTimestamp(), hits: increment(1) });
358
- * ```
359
- */
360
- type WithFieldSentinels<T> = {
361
- [K in keyof T]: T[K] | FieldSentinel;
362
- };
363
- interface DbWhereFilter {
364
- field: string;
365
- op: '==' | '!=' | '<' | '<=' | '>' | '>=' | 'array-contains' | 'in' | 'not-in';
366
- value: unknown;
367
- }
368
- interface DbQuery {
369
- where?: DbWhereFilter[];
370
- orderBy?: {
371
- field: string;
372
- direction?: 'asc' | 'desc';
373
- };
374
- /** Máximo de documentos. Default 20, max 500. */
375
- limit?: number;
376
- /** Cursor opaco — devolvido em `DbListResult.nextCursor`. */
377
- cursor?: string;
378
- }
379
- interface DbDoc<T = Record<string, unknown>> {
380
- id: string;
381
- data: T;
382
- }
383
- interface DbListResult<T = Record<string, unknown>> {
384
- docs: DbDoc<T>[];
385
- nextCursor: string | null;
386
- fromCache: boolean;
387
- stale: boolean;
388
- hasPendingWrites: boolean;
389
- changes: Array<{
390
- type: 'added' | 'modified' | 'removed';
391
- doc: DbDoc<T>;
392
- }>;
393
- }
394
- interface DbGetResult<T = Record<string, unknown>> {
395
- docs: Array<DbDoc<T>>;
396
- fromCache: boolean;
397
- stale: boolean;
398
- hasPendingWrites: boolean;
399
- changes: Array<{
400
- type: 'added' | 'modified' | 'removed';
401
- doc: DbDoc<T>;
402
- }>;
403
- }
404
- type DbChangeType = 'added' | 'modified' | 'removed';
405
- /**
406
- * Operação atômica para uso em `DbCollectionRef.batch()`.
407
- *
408
- * Discriminated union pelo campo `kind` — cada variante deixa claro
409
- * quais campos são obrigatórios:
410
- *
411
- * - `add` — insere com id gerado pelo cliente; `data` obrigatório.
412
- * - `set` — cria ou sobrescreve pelo `id`; `data` obrigatório.
413
- * - `update` — mescla campos em doc existente pelo `id`; `data` obrigatório.
414
- * - `remove` — tombstone pelo `id`; sem `data`.
415
- *
416
- * @example
417
- * ```ts
418
- * const ops: DbBatchOp[] = [
419
- * { kind: 'add', data: { name: 'produto A', price: 10 } },
420
- * { kind: 'set', id: 'sku-1', data: { name: 'produto B', price: 20 } },
421
- * { kind: 'update', id: 'sku-2', data: { price: 25 } },
422
- * { kind: 'remove', id: 'sku-3' },
423
- * ];
424
- * await col.batch(ops);
425
- * ```
426
- */
427
- type DbBatchOp<T = Record<string, unknown>> = {
428
- kind: 'add';
429
- data: WithFieldSentinels<Omit<T, 'id'>>;
430
- } | {
431
- kind: 'set';
432
- id: string;
433
- data: WithFieldSentinels<Omit<T, 'id'>>;
434
- } | {
435
- kind: 'update';
436
- id: string;
437
- data: Partial<WithFieldSentinels<Omit<T, 'id'>>>;
438
- } | {
439
- kind: 'remove';
440
- id: string;
441
- };
442
- /**
443
- * Contrato do transporte de realtime — injetado no namespace.
444
- *
445
- * A implementação real usa os endpoints `/api/sdk/v1/db/*` do Core.
446
- * Em testes, usa-se um fake in-memory.
447
- *
448
- * Este é o **seam de injeção** que conecta a camada offline ao servidor real.
449
- * Transportes futuros (Firestore Web SDK direto / WebSocket gateway) são
450
- * implementados aqui, sem modificar a superfície pública `DbCollectionRef`.
451
- */
452
- type RealtimeTransport = SyncTransport;
453
- /**
454
- * Referência a um documento específico.
455
- * Parte da superfície `DbCollectionRef.doc(id)`.
456
- *
457
- * Expõe as propriedades de identidade do documento de forma análoga ao
458
- * `DocumentReference` do Firestore SDK, evitando a necessidade de adaptadores
459
- * externos que reattachavam `collection` + `id` manualmente.
460
- *
461
- * @example
462
- * ```ts
463
- * const ref = orders.doc('order-123');
464
- * console.log(ref.id); // "order-123"
465
- * console.log(ref.collection); // "orders"
466
- * console.log(ref.path); // "orders/order-123"
467
- * ```
468
- */
469
- interface DbDocRef<T = Record<string, unknown>> {
470
- /** ID do documento dentro da coleção. */
471
- readonly id: string;
472
- /** Caminho completo no formato "collection/docId". */
473
- readonly path: string;
474
- /** Nome da coleção parent. */
475
- readonly collection: string;
476
- get(): Promise<DbGetResult<T> | null>;
477
- set(data: WithFieldSentinels<Omit<T, 'id'>>): Promise<{
478
- ok: true;
479
- }>;
480
- update(data: Partial<WithFieldSentinels<Omit<T, 'id'>>>): Promise<{
481
- ok: true;
482
- }>;
483
- remove(): Promise<{
484
- ok: true;
485
- }>;
486
- onSnapshot(cb: (snap: DbGetResult<T> | null) => void): Unsubscribe;
487
- }
488
- /**
489
- * Referência a uma coleção — superfície dupla CRUD + realtime.
490
- *
491
- * Conforme 02-sdk.md §3.2 / I2 §3.2.
492
- */
493
- interface DbCollectionRef<T = Record<string, unknown>> {
494
- /** Retorna snapshot de um documento. `null` se não existir (nem no cache). */
495
- get(id: string): Promise<DbGetResult<T> | null>;
496
- /** Lista documentos com query opcional. */
497
- list(q?: DbQuery): Promise<DbListResult<T>>;
498
- /** Adiciona doc com id gerado pelo cliente (UUID v4). Durável localmente. */
499
- add(data: WithFieldSentinels<Omit<T, 'id'>>): Promise<{
500
- ok: true;
501
- id: string;
502
- }>;
503
- /** Cria ou sobrescreve um doc pelo id. Durável localmente. */
504
- set(id: string, data: WithFieldSentinels<Omit<T, 'id'>>): Promise<{
505
- ok: true;
506
- }>;
507
- /** Atualiza campos de um doc (merge). Durável localmente. */
508
- update(id: string, data: Partial<WithFieldSentinels<Omit<T, 'id'>>>): Promise<{
509
- ok: true;
510
- }>;
511
- /** Remove um doc (tombstone local). Durável localmente. */
512
- remove(id: string): Promise<{
513
- ok: true;
514
- }>;
515
- /** Aplica múltiplas operações atomicamente na fila. */
516
- batch(ops: DbBatchOp<T>[]): Promise<{
517
- ok: true;
518
- }>;
519
- /** Escuta um documento específico. */
520
- onDoc(id: string, cb: (doc: T | null) => void): Unsubscribe;
521
- /** Escuta a coleção inteira (ou subconjunto via query). */
522
- onSnapshot(q: DbQuery | undefined, cb: (snap: DbListResult<T>) => void): Unsubscribe;
523
- /** Retorna uma referência ao documento `id`. */
524
- doc(id: string): DbDocRef<T>;
525
- }
526
- /**
527
- * Namespace `db.documents` — ponto de entrada para coleções de documentos.
528
- *
529
- * Analogia a `NeetruDb` de 02-sdk.md §3.1 mas escoped para o Mundo Documentos.
530
- */
531
- interface NeetruDbDocuments {
532
- collection<T = Record<string, unknown>>(name: string): DbCollectionRef<T>;
533
- /** Estado atual de sincronização (offline/online/syncing). */
534
- readonly syncState: SyncState;
535
- onSyncStateChanged(cb: (s: SyncState) => void): Unsubscribe;
536
- /** Força flush da fila de mutações pendentes. */
537
- flush(): Promise<void>;
538
- /** Limpa o cache local (IndexedDB) — operação destrutiva. */
539
- clearCache(): Promise<void>;
540
- /**
541
- * Retorna os registros do log de conflitos LWW gravados pelo SyncEngine.
542
- * Um conflito ocorre quando a mutação local é descartada porque o servidor
543
- * já avançou a versão do documento (Last-Write-Wins).
544
- */
545
- getConflicts(): Promise<ConflictRecord[]>;
546
- }
547
- interface CreateOfflineDocumentsOptions {
548
- /**
549
- * Nome do banco IndexedDB — deve ser único por produto × dbId × env.
550
- * Convenção: `neetru-db__{productSlug}__{dbId}__{env}`.
551
- */
552
- dbName: string;
553
- /**
554
- * Transporte de sync — implementa `SyncTransport` do SyncEngine.
555
- * Este é o **seam de injeção** do transporte real.
556
- *
557
- * Implementações possíveis:
558
- * - `RestSyncTransport` — REST → Core (padrão em staging/prod)
559
- * - `FirestoreRealtimeTransport` — Firestore Web SDK direto (engine=firestore)
560
- * - `WebSocketTransport` — WebSocket → gateway (engine=nosql-vm)
561
- * - `FakeRealtimeTransport` — in-memory (testes)
562
- */
563
- transport: RealtimeTransport;
564
- /**
565
- * Estado inicial de conectividade.
566
- * Default: `navigator.onLine` se disponível, senão `true`.
567
- */
568
- startOnline?: boolean;
569
- /**
570
- * `true` = sem contenção multi-aba (sempre líder).
571
- * Padrão em testes; em produção usa Web Locks.
572
- */
573
- singleTab?: boolean;
574
- /**
575
- * Intervalo do sync periódico em ms. `0` desativa.
576
- * Default: 5 * 60 * 1000 (5min).
577
- */
578
- periodicSyncIntervalMs?: number;
579
- }
580
-
581
- export { type ConflictRecord as C, type DbBatchOp as D, type FieldSentinel as F, type IncrementSentinel as I, NEETRU_SENTINEL_KEY as N, type RealtimeTransport as R, type ServerTimestampSentinel as S, type Unsubscribe as U, type WithFieldSentinels as W, type CreateOfflineDocumentsOptions as a, type DbChangeType as b, type DbCollectionRef as c, type DbDoc as d, type DbDocRef as e, type DbGetResult as f, type DbListResult as g, type DbQuery as h, type DbWhereFilter as i, type NeetruDbDocuments as j, type SyncState as k, increment as l, isFieldSentinel as m, isIncrementSentinel as n, isServerTimestampSentinel as o, serverTimestamp as s };