@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.
- package/CHANGELOG.md +554 -530
- package/dist/auth.cjs +60 -39
- package/dist/auth.cjs.map +1 -1
- package/dist/auth.d.cts +1 -2
- package/dist/auth.d.ts +1 -2
- package/dist/auth.mjs +60 -39
- package/dist/auth.mjs.map +1 -1
- package/dist/catalog.d.cts +1 -2
- package/dist/catalog.d.ts +1 -2
- package/dist/checkout.d.cts +1 -2
- package/dist/checkout.d.ts +1 -2
- package/dist/db-react.cjs +15 -5
- package/dist/db-react.cjs.map +1 -1
- package/dist/db-react.d.cts +35 -13
- package/dist/db-react.d.ts +35 -13
- package/dist/db-react.mjs +15 -5
- package/dist/db-react.mjs.map +1 -1
- package/dist/db.cjs +71 -50
- package/dist/db.cjs.map +1 -1
- package/dist/db.d.cts +1 -2
- package/dist/db.d.ts +1 -2
- package/dist/db.mjs +71 -50
- package/dist/db.mjs.map +1 -1
- package/dist/entitlements.d.cts +1 -2
- package/dist/entitlements.d.ts +1 -2
- package/dist/firestore-compat.cjs +399 -0
- package/dist/firestore-compat.cjs.map +1 -0
- package/dist/firestore-compat.d.cts +294 -0
- package/dist/firestore-compat.d.ts +294 -0
- package/dist/firestore-compat.mjs +371 -0
- package/dist/firestore-compat.mjs.map +1 -0
- package/dist/index.cjs +72 -51
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -3
- package/dist/index.d.ts +2 -3
- package/dist/index.mjs +72 -51
- package/dist/index.mjs.map +1 -1
- package/dist/mocks.d.cts +1 -2
- package/dist/mocks.d.ts +1 -2
- package/dist/notifications.d.cts +1 -2
- package/dist/notifications.d.ts +1 -2
- package/dist/react.cjs +267 -0
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +117 -3
- package/dist/react.d.ts +117 -3
- package/dist/react.mjs +265 -1
- package/dist/react.mjs.map +1 -1
- package/dist/support.d.cts +1 -2
- package/dist/support.d.ts +1 -2
- package/dist/telemetry.d.cts +1 -2
- package/dist/telemetry.d.ts +1 -2
- package/dist/{types-D7zVkO3n.d.ts → types-BRv8wBxX.d.ts} +581 -2
- package/dist/{types-Dc4bjrrt.d.cts → types-nwErcRX8.d.cts} +581 -2
- package/dist/usage.d.cts +1 -2
- package/dist/usage.d.ts +1 -2
- package/dist/webhooks.d.cts +1 -2
- package/dist/webhooks.d.ts +1 -2
- package/package.json +156 -151
- package/dist/collection-ref-BDdfD87k.d.cts +0 -581
- package/dist/collection-ref-BDdfD87k.d.ts +0 -581
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { c as DbCollectionRef$1, k as SyncState, U as Unsubscribe, C as ConflictRecord, R as RealtimeTransport } from './collection-ref-BDdfD87k.cjs';
|
|
2
1
|
import { NodePgDatabase } from 'drizzle-orm/node-postgres';
|
|
3
2
|
import { NeetruError } from './errors.cjs';
|
|
4
3
|
|
|
@@ -266,6 +265,328 @@ declare class MockWebhooks implements WebhooksNamespace {
|
|
|
266
265
|
test(id: string, _options?: WebhookScopeOptions): Promise<WebhookTestResult>;
|
|
267
266
|
}
|
|
268
267
|
|
|
268
|
+
/**
|
|
269
|
+
* Tipos internos da camada offline do @neetru/sdk.
|
|
270
|
+
*
|
|
271
|
+
* Estes tipos SÃO internos ao módulo `db/offline/` — não fazem parte da
|
|
272
|
+
* superfície pública do SDK. A superfície pública é o `DbCollectionRef` e
|
|
273
|
+
* o `DbSnapshot` de `src/types.ts`.
|
|
274
|
+
*
|
|
275
|
+
* Derivados de:
|
|
276
|
+
* - I3-sdk-offline.md §3 (store schemas)
|
|
277
|
+
* - I3-sdk-offline.md §4 (fila de escritas)
|
|
278
|
+
* - I3-sdk-offline.md §5 (query descriptor)
|
|
279
|
+
* - I3-sdk-offline.md §7 (conflito LWW)
|
|
280
|
+
* - 02-sdk.md §3.2 (DbSnapshot, DbWhereFilter, DbQuery)
|
|
281
|
+
*/
|
|
282
|
+
/** Identificador de um documento (string opaca). */
|
|
283
|
+
type DocId = string;
|
|
284
|
+
/** Identificador de uma mutação (UUID v4 client-generated). */
|
|
285
|
+
type MutationId = string;
|
|
286
|
+
/** Operações possíveis na fila de escritas. */
|
|
287
|
+
type MutationKind = 'add' | 'set' | 'update' | 'remove';
|
|
288
|
+
/**
|
|
289
|
+
* Uma mutação enfileirada (escrita durável pendente).
|
|
290
|
+
* Equivalente a `QueuedMutation` do I3 §3.3 (`mutations` store).
|
|
291
|
+
*/
|
|
292
|
+
interface Mutation {
|
|
293
|
+
/** Número de sequência autoincrement — define a ordem total da fila. */
|
|
294
|
+
seq: number;
|
|
295
|
+
/** UUID v4 client-generated — chave de idempotência. */
|
|
296
|
+
mutationId: MutationId;
|
|
297
|
+
/** Nome da coleção. */
|
|
298
|
+
collection: string;
|
|
299
|
+
/** ID do documento alvo (client-gen para `add`, existente para os demais). */
|
|
300
|
+
docId: DocId;
|
|
301
|
+
/** Operação. */
|
|
302
|
+
op: MutationKind;
|
|
303
|
+
/** Dados da operação. `null` para `remove`. */
|
|
304
|
+
payload: Record<string, unknown> | null;
|
|
305
|
+
/**
|
|
306
|
+
* `serverVersion` que o cliente viu ao escrever.
|
|
307
|
+
* `null` para `add` (doc não existia) ou quando o cliente nunca sincronizou.
|
|
308
|
+
* Usado pelo servidor para detectar conflito (I3 §7.4).
|
|
309
|
+
*/
|
|
310
|
+
baseVersion: string | null;
|
|
311
|
+
/** Epoch ms do relógio do CLIENTE — só para diagnóstico e ordenação local. */
|
|
312
|
+
enqueuedAt: number;
|
|
313
|
+
/** Número de tentativas de replay. */
|
|
314
|
+
attempts: number;
|
|
315
|
+
/** Último erro de replay (string). `null` se nenhuma tentativa ainda. */
|
|
316
|
+
lastError: string | null;
|
|
317
|
+
/** Estado de envio desta mutação. */
|
|
318
|
+
status: 'queued' | 'inflight' | 'failed';
|
|
319
|
+
/** Agrupa mutações de um `batch()` atômico. `null` se mutação individual. */
|
|
320
|
+
batchId: string | null;
|
|
321
|
+
}
|
|
322
|
+
/**
|
|
323
|
+
* Razão pela qual uma escrita foi descartada no LWW (I3 §3.3 `conflict_log`).
|
|
324
|
+
*/
|
|
325
|
+
type ConflictReason = 'lww_server_newer' | 'rejected_permission' | 'rejected_validation';
|
|
326
|
+
/**
|
|
327
|
+
* Registro de uma escrita perdedora — gravado no `conflict_log` (I3 §3.3).
|
|
328
|
+
* Entregue ao produto via `onWriteResult` (status: `'superseded'`).
|
|
329
|
+
*/
|
|
330
|
+
interface ConflictRecord {
|
|
331
|
+
id?: number;
|
|
332
|
+
collection: string;
|
|
333
|
+
docId: DocId;
|
|
334
|
+
mutationId: MutationId;
|
|
335
|
+
losingData: Record<string, unknown>;
|
|
336
|
+
winningData: Record<string, unknown>;
|
|
337
|
+
reason: ConflictReason;
|
|
338
|
+
detectedAt: number;
|
|
339
|
+
delivered: boolean;
|
|
340
|
+
}
|
|
341
|
+
/**
|
|
342
|
+
* Estado de sincronização da camada offline (I3 §10 / 02-sdk §3.4).
|
|
343
|
+
* Exposto em `client.db.syncState`.
|
|
344
|
+
*
|
|
345
|
+
* - `idle` — online e em repouso (sem sync ativo).
|
|
346
|
+
* - `syncing` — sync em progresso (push + pull + realtime).
|
|
347
|
+
* - `offline` — conexão perdida ou inexistente.
|
|
348
|
+
* - `error` — erro persistente após retries (reservado para uso futuro).
|
|
349
|
+
*/
|
|
350
|
+
type SyncStatus = 'idle' | 'offline' | 'syncing' | 'error';
|
|
351
|
+
interface SyncState {
|
|
352
|
+
status: SyncStatus;
|
|
353
|
+
pendingWrites: number;
|
|
354
|
+
lastSyncedAt: number | null;
|
|
355
|
+
isLeaderTab: boolean;
|
|
356
|
+
}
|
|
357
|
+
/** Função para cancelar uma subscrição (onSnapshot, onDoc, onSyncStateChanged). */
|
|
358
|
+
type Unsubscribe = () => void;
|
|
359
|
+
|
|
360
|
+
/**
|
|
361
|
+
* SyncEngine — orquestrador da camada offline do @neetru/sdk.
|
|
362
|
+
*
|
|
363
|
+
* Implementa as 3 fases de sincronização definidas em I3 §6:
|
|
364
|
+
*
|
|
365
|
+
* FASE 1 — DRENAR A FILA (push)
|
|
366
|
+
* Envia mutações pendentes ao Core via SyncTransport.
|
|
367
|
+
* Cada mutação é marcada inflight, enviada e depois:
|
|
368
|
+
* - CONFIRMADA → removida da fila (markApplied)
|
|
369
|
+
* - SUPERADA (LWW)→ ConflictRecord gravado, cache atualizado
|
|
370
|
+
* - REJEITADA → resolveRejected + ConflictRecord + removida
|
|
371
|
+
* - FALHA TRANSIENTE → markRetry + PARA a drenagem (ordem preservada)
|
|
372
|
+
*
|
|
373
|
+
* FASE 2 — RECONCILIAR O CACHE (pull)
|
|
374
|
+
* Busca mudanças do servidor desde o watermark ou resume token.
|
|
375
|
+
* Cada documento passa pelo ConflictResolver (LWW) e é gravado no LocalStore.
|
|
376
|
+
* Changes são emitidos pelo ChangeBus.
|
|
377
|
+
* Se `resyncRequired` → aciona full resync em vez de pull incremental.
|
|
378
|
+
*
|
|
379
|
+
* FASE 3 — REABRIR LISTENERS (realtime)
|
|
380
|
+
* Atualiza SyncState para refletir que o cache está reconciliado.
|
|
381
|
+
* O ChangeBus já foi alimentado nas fases anteriores — os listeners
|
|
382
|
+
* ativos na camada superior (onSnapshot/onDoc) recebem snapshots frescos.
|
|
383
|
+
*
|
|
384
|
+
* Garantias:
|
|
385
|
+
* - SOMENTE a aba LÍDER executa o sync (TabCoordinator.isLeader()).
|
|
386
|
+
* - Re-entrante-safe: um sync em progresso bloqueia novos disparos.
|
|
387
|
+
* - Fail-closed: falha transiente retém a mutação; falha de pull não
|
|
388
|
+
* corrompe o cache nem o watermark.
|
|
389
|
+
* - Idempotente: o SyncTransport usa mutationId como chave de idempotência.
|
|
390
|
+
*
|
|
391
|
+
* O SyncTransport é INJETADO — a ligação real com o Core acontece na camada
|
|
392
|
+
* de adaptação, não aqui. Isso torna o SyncEngine 100% testável com fake.
|
|
393
|
+
*/
|
|
394
|
+
|
|
395
|
+
/**
|
|
396
|
+
* Documento retornado pelo servidor em push/pull/resync.
|
|
397
|
+
*/
|
|
398
|
+
interface ServerDoc {
|
|
399
|
+
collection: string;
|
|
400
|
+
id: string;
|
|
401
|
+
data: Record<string, unknown>;
|
|
402
|
+
serverVersion: string;
|
|
403
|
+
serverTimestamp: number;
|
|
404
|
+
/** `true` se o doc foi deletado no servidor. */
|
|
405
|
+
deleted: boolean;
|
|
406
|
+
}
|
|
407
|
+
/**
|
|
408
|
+
* Resultado individual de uma mutação enviada ao Core (FASE 1).
|
|
409
|
+
*/
|
|
410
|
+
type MutationResult = {
|
|
411
|
+
mutationId: string;
|
|
412
|
+
outcome: 'confirmed';
|
|
413
|
+
serverVersion: string;
|
|
414
|
+
serverTimestamp: number;
|
|
415
|
+
} | {
|
|
416
|
+
mutationId: string;
|
|
417
|
+
outcome: 'superseded';
|
|
418
|
+
serverVersion: string;
|
|
419
|
+
serverTimestamp: number;
|
|
420
|
+
serverData: Record<string, unknown>;
|
|
421
|
+
} | {
|
|
422
|
+
mutationId: string;
|
|
423
|
+
outcome: 'rejected';
|
|
424
|
+
reason: 'rejected_permission' | 'rejected_validation';
|
|
425
|
+
serverVersion: string | null;
|
|
426
|
+
serverData: Record<string, unknown> | null;
|
|
427
|
+
};
|
|
428
|
+
/** Retorno de pushMutations. */
|
|
429
|
+
interface PushMutationsResult {
|
|
430
|
+
results: MutationResult[];
|
|
431
|
+
}
|
|
432
|
+
/** Retorno de pullChanges. */
|
|
433
|
+
interface PullChangesResult {
|
|
434
|
+
docs: ServerDoc[];
|
|
435
|
+
newWatermark: number | null;
|
|
436
|
+
/** `true` quando o resume token / watermark ficou inválido (gap grande). */
|
|
437
|
+
resyncRequired: boolean;
|
|
438
|
+
}
|
|
439
|
+
/** Retorno de fullResync. */
|
|
440
|
+
interface FullResyncResult {
|
|
441
|
+
docs: ServerDoc[];
|
|
442
|
+
newWatermark: number | null;
|
|
443
|
+
}
|
|
444
|
+
/**
|
|
445
|
+
* Contrato do transporte de sync — injetado no SyncEngine.
|
|
446
|
+
*
|
|
447
|
+
* A implementação real usa os endpoints `/api/sdk/v1/db/*` do Core.
|
|
448
|
+
* Em testes, usa-se um FakeSyncTransport.
|
|
449
|
+
*
|
|
450
|
+
* Per I3 §6:
|
|
451
|
+
* - `pushMutations` → FASE 1: envia mutações em ordem de seq.
|
|
452
|
+
* - `pullChanges` → FASE 2: busca delta desde o watermark/resumeToken.
|
|
453
|
+
* - `fullResync` → FASE 2 (fallback): lista completa das coleções ativas.
|
|
454
|
+
*/
|
|
455
|
+
interface SyncTransport {
|
|
456
|
+
/**
|
|
457
|
+
* Envia um lote de mutações ao Core.
|
|
458
|
+
* O Core aplica, verifica conflito LWW e retorna um resultado por mutação.
|
|
459
|
+
* A ordem do array deve ser preservada (seq crescente).
|
|
460
|
+
*/
|
|
461
|
+
pushMutations(mutations: Mutation[]): Promise<PushMutationsResult>;
|
|
462
|
+
/**
|
|
463
|
+
* Busca documentos modificados no servidor desde `sinceWatermark`.
|
|
464
|
+
* `sinceWatermark === null` indica primeiro sync (sem base conhecida).
|
|
465
|
+
* `resumeToken` é o token de change stream (nosql-vm); pode ser null.
|
|
466
|
+
*
|
|
467
|
+
* Retorna `resyncRequired: true` se o gap é grande demais para pull incremental.
|
|
468
|
+
*/
|
|
469
|
+
pullChanges(sinceWatermark: number | null, resumeToken: string | null): Promise<PullChangesResult>;
|
|
470
|
+
/**
|
|
471
|
+
* Faz um full resync das coleções informadas.
|
|
472
|
+
* Retorna TODOS os documentos atuais (paginado internamente pelo transporte).
|
|
473
|
+
* Docs ausentes na resposta foram deletados durante o gap.
|
|
474
|
+
*/
|
|
475
|
+
fullResync(collections: string[]): Promise<FullResyncResult>;
|
|
476
|
+
/**
|
|
477
|
+
* (Opcional) Registra uma subscription realtime para uma coleção específica.
|
|
478
|
+
*
|
|
479
|
+
* Implementado pelo WebSocket transport (nosql-vm): chama
|
|
480
|
+
* `NeetruRealtimeClient.subscribe()` e alimenta os frames delta/resync
|
|
481
|
+
* no `ChangeBus` da camada offline.
|
|
482
|
+
*
|
|
483
|
+
* Transportes REST e Firestore não implementam este método (undefined).
|
|
484
|
+
* O chamador (`DbCollectionRefImpl.onSnapshot`) verifica antes de invocar.
|
|
485
|
+
*
|
|
486
|
+
* @param collection - Nome da coleção a subscrever.
|
|
487
|
+
* @param onChange - Callback que recebe changes quando um delta/resync chega.
|
|
488
|
+
* `null` payload = resync full necessário.
|
|
489
|
+
* @returns Função de unsubscribe.
|
|
490
|
+
*/
|
|
491
|
+
subscribeCollection?: (collection: string, onChange: (changes: Array<{
|
|
492
|
+
type: 'added' | 'modified' | 'removed';
|
|
493
|
+
docId: string;
|
|
494
|
+
data: Record<string, unknown> | null;
|
|
495
|
+
}>, needsResync: boolean) => void) => () => void;
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
/**
|
|
499
|
+
* field-value.ts — Sentinels de escrita server-side para o Mundo Documentos.
|
|
500
|
+
*
|
|
501
|
+
* Resolve duas lacunas confirmadas (bug_05aedb8b) da superfície `client.db`:
|
|
502
|
+
*
|
|
503
|
+
* 1. **Timestamp resolvido no servidor** — antes deste módulo, o produto que
|
|
504
|
+
* quisesse `createdAt`/`updatedAt` confiáveis tinha que usar `Date.now()`
|
|
505
|
+
* no CLIENTE, que é spoofável e sofre clock-skew. Agora:
|
|
506
|
+
*
|
|
507
|
+
* await orders.add({ total: 99, createdAt: serverTimestamp() });
|
|
508
|
+
*
|
|
509
|
+
* O marcador atravessa o transporte REST como um objeto JSON namespaceado
|
|
510
|
+
* e o Core o substitui por `FieldValue.serverTimestamp()` no momento da
|
|
511
|
+
* escrita — relógio do SERVIDOR, não do cliente.
|
|
512
|
+
*
|
|
513
|
+
* 2. **Increment atômico** — antes, `update(id, { n: x+1 })` era read-modify-write
|
|
514
|
+
* no cliente, sujeito a lost-update sob concorrência. Agora:
|
|
515
|
+
*
|
|
516
|
+
* await counters.update('global', { hits: increment(1) });
|
|
517
|
+
*
|
|
518
|
+
* O Core aplica `FieldValue.increment(1)` atomicamente no documento.
|
|
519
|
+
*
|
|
520
|
+
* ## Contrato de fio (wire format)
|
|
521
|
+
*
|
|
522
|
+
* Os sentinels são objetos JSON simples — sobrevivem `JSON.stringify` no
|
|
523
|
+
* transporte REST e ao `IndexedDB structured clone` da camada offline:
|
|
524
|
+
*
|
|
525
|
+
* serverTimestamp() → `{ "__neetru__": "serverTimestamp" }`
|
|
526
|
+
* increment(n) → `{ "__neetru__": "increment", "operand": n }`
|
|
527
|
+
*
|
|
528
|
+
* A chave-âncora `__neetru__` é deliberadamente improvável de colidir com
|
|
529
|
+
* dados de produto. O Core valida e resolve esses marcadores; qualquer objeto
|
|
530
|
+
* com `__neetru__` desconhecido é rejeitado server-side (fail-closed).
|
|
531
|
+
*
|
|
532
|
+
* ## Aplicação otimista (offline-first)
|
|
533
|
+
*
|
|
534
|
+
* A camada offline aplica uma APROXIMAÇÃO local imediata (para a UI não piscar):
|
|
535
|
+
* - `serverTimestamp()` → `Date.now()` local (substituído pelo valor real no sync).
|
|
536
|
+
* - `increment(n)` → soma `n` ao valor numérico atual do campo (0 se ausente).
|
|
537
|
+
* O valor canônico vem sempre do servidor no pull/confirm subsequente.
|
|
538
|
+
*
|
|
539
|
+
* @module
|
|
540
|
+
*/
|
|
541
|
+
/** Chave-âncora dos marcadores de sentinel no wire format. */
|
|
542
|
+
declare const NEETRU_SENTINEL_KEY: "__neetru__";
|
|
543
|
+
/** Marcador de timestamp resolvido pelo servidor. */
|
|
544
|
+
interface ServerTimestampSentinel {
|
|
545
|
+
readonly __neetru__: 'serverTimestamp';
|
|
546
|
+
}
|
|
547
|
+
/** Marcador de incremento atômico aplicado pelo servidor. */
|
|
548
|
+
interface IncrementSentinel {
|
|
549
|
+
readonly __neetru__: 'increment';
|
|
550
|
+
/** Delta a somar (pode ser negativo para decremento). */
|
|
551
|
+
readonly operand: number;
|
|
552
|
+
}
|
|
553
|
+
/**
|
|
554
|
+
* União de todos os sentinels de campo. Use `serverTimestamp()` / `increment()`
|
|
555
|
+
* para construir — nunca monte o objeto à mão.
|
|
556
|
+
*/
|
|
557
|
+
type FieldSentinel = ServerTimestampSentinel | IncrementSentinel;
|
|
558
|
+
/**
|
|
559
|
+
* Sentinel que instrui o servidor a gravar o timestamp do MOMENTO da escrita
|
|
560
|
+
* usando o relógio do servidor (não do cliente).
|
|
561
|
+
*
|
|
562
|
+
* @example
|
|
563
|
+
* ```ts
|
|
564
|
+
* import { serverTimestamp } from '@neetru/sdk/db';
|
|
565
|
+
* await orders.add({ total: 99, createdAt: serverTimestamp() });
|
|
566
|
+
* ```
|
|
567
|
+
*/
|
|
568
|
+
declare function serverTimestamp(): ServerTimestampSentinel;
|
|
569
|
+
/**
|
|
570
|
+
* Sentinel que instrui o servidor a incrementar atomicamente o campo numérico
|
|
571
|
+
* por `n` (negativo decrementa). Se o campo não existir/não for numérico, o
|
|
572
|
+
* servidor o trata como `0` antes de somar (semântica `FieldValue.increment`).
|
|
573
|
+
*
|
|
574
|
+
* @param n — delta inteiro ou fracionário a somar.
|
|
575
|
+
* @example
|
|
576
|
+
* ```ts
|
|
577
|
+
* import { increment } from '@neetru/sdk/db';
|
|
578
|
+
* await counters.update('global', { hits: increment(1) });
|
|
579
|
+
* await wallet.update(uid, { balance: increment(-50) });
|
|
580
|
+
* ```
|
|
581
|
+
*/
|
|
582
|
+
declare function increment(n: number): IncrementSentinel;
|
|
583
|
+
/** `true` se `v` é qualquer sentinel de campo Neetru. */
|
|
584
|
+
declare function isFieldSentinel(v: unknown): v is FieldSentinel;
|
|
585
|
+
/** `true` se `v` é o sentinel `serverTimestamp()`. */
|
|
586
|
+
declare function isServerTimestampSentinel(v: unknown): v is ServerTimestampSentinel;
|
|
587
|
+
/** `true` se `v` é o sentinel `increment(n)` com `operand` numérico válido. */
|
|
588
|
+
declare function isIncrementSentinel(v: unknown): v is IncrementSentinel;
|
|
589
|
+
|
|
269
590
|
/**
|
|
270
591
|
* NeetruDbError — classe de erro especializada para o módulo `db`.
|
|
271
592
|
*
|
|
@@ -323,6 +644,264 @@ declare class NeetruDbError extends NeetruError {
|
|
|
323
644
|
constructor(code: NeetruDbErrorCode, message: string, dbId?: string);
|
|
324
645
|
}
|
|
325
646
|
|
|
647
|
+
/**
|
|
648
|
+
* DbCollectionRef — superfície de Documentos offline-first do @neetru/sdk (M2).
|
|
649
|
+
*
|
|
650
|
+
* Implementa a API pública conforme especificada em:
|
|
651
|
+
* - 02-sdk.md §3.2 (contrato TypeScript DbCollectionRef)
|
|
652
|
+
* - I2-mundo-documentos.md §3.2 (superfície dupla CRUD + realtime)
|
|
653
|
+
* - I3-sdk-offline.md §10 (camada offline, lifecycle, SyncState)
|
|
654
|
+
*
|
|
655
|
+
* ### Semântica de Promise de escrita (02-sdk.md §3.2 nota negrito)
|
|
656
|
+
* `add/set/update/remove` resolvem quando a escrita está **durável localmente**
|
|
657
|
+
* (cache + fila), NÃO quando o servidor confirma. Isso é offline-first por design:
|
|
658
|
+
* sem rede, `add()` resolve imediatamente. Para saber quando sincronizou, o produto
|
|
659
|
+
* observa `onWriteResult` (future / SyncEngine). A Promise só rejeita por:
|
|
660
|
+
* - validação síncrona (nome de coleção inválido, id vazio)
|
|
661
|
+
* - `offline_quota_exceeded` (IndexedDB cheio)
|
|
662
|
+
*
|
|
663
|
+
* ### Injeção do transporte realtime
|
|
664
|
+
* O `RealtimeTransport` é injetado via `createOfflineDocumentsNamespace({ transport })`.
|
|
665
|
+
* A implementação atual usa o `SyncEngine` para push/pull (sync offline ↔ servidor).
|
|
666
|
+
* Transportes futuros (Firestore Web SDK / WebSocket gateway) são injetados aqui —
|
|
667
|
+
* sem alterar a superfície pública.
|
|
668
|
+
*/
|
|
669
|
+
|
|
670
|
+
/**
|
|
671
|
+
* Permite que cada campo de `T` aceite, além do seu tipo declarado, um sentinel
|
|
672
|
+
* de escrita server-side (`serverTimestamp()` / `increment()`).
|
|
673
|
+
*
|
|
674
|
+
* Sem isso, um produto que tipa `createdAt: number` veria erro de tipo ao passar
|
|
675
|
+
* `serverTimestamp()`. Esta projeção mantém o autocomplete dos campos do produto
|
|
676
|
+
* e ainda autoriza os sentinels onde fizer sentido.
|
|
677
|
+
*
|
|
678
|
+
* @example
|
|
679
|
+
* ```ts
|
|
680
|
+
* interface Order { total: number; createdAt: number; hits: number }
|
|
681
|
+
* orders.add({ total: 10, createdAt: serverTimestamp(), hits: increment(1) });
|
|
682
|
+
* ```
|
|
683
|
+
*/
|
|
684
|
+
type WithFieldSentinels<T> = {
|
|
685
|
+
[K in keyof T]: T[K] | FieldSentinel;
|
|
686
|
+
};
|
|
687
|
+
interface DbWhereFilter$1 {
|
|
688
|
+
field: string;
|
|
689
|
+
op: '==' | '!=' | '<' | '<=' | '>' | '>=' | 'array-contains' | 'in' | 'not-in';
|
|
690
|
+
value: unknown;
|
|
691
|
+
}
|
|
692
|
+
interface DbQuery$1 {
|
|
693
|
+
where?: DbWhereFilter$1[];
|
|
694
|
+
orderBy?: {
|
|
695
|
+
field: string;
|
|
696
|
+
direction?: 'asc' | 'desc';
|
|
697
|
+
};
|
|
698
|
+
/** Máximo de documentos. Default 20, max 500. */
|
|
699
|
+
limit?: number;
|
|
700
|
+
/** Cursor opaco — devolvido em `DbListResult.nextCursor`. */
|
|
701
|
+
cursor?: string;
|
|
702
|
+
}
|
|
703
|
+
interface DbDoc<T = Record<string, unknown>> {
|
|
704
|
+
id: string;
|
|
705
|
+
data: T;
|
|
706
|
+
}
|
|
707
|
+
interface DbListResult<T = Record<string, unknown>> {
|
|
708
|
+
docs: DbDoc<T>[];
|
|
709
|
+
nextCursor: string | null;
|
|
710
|
+
fromCache: boolean;
|
|
711
|
+
stale: boolean;
|
|
712
|
+
hasPendingWrites: boolean;
|
|
713
|
+
changes: Array<{
|
|
714
|
+
type: 'added' | 'modified' | 'removed';
|
|
715
|
+
doc: DbDoc<T>;
|
|
716
|
+
}>;
|
|
717
|
+
}
|
|
718
|
+
interface DbGetResult<T = Record<string, unknown>> {
|
|
719
|
+
docs: Array<DbDoc<T>>;
|
|
720
|
+
fromCache: boolean;
|
|
721
|
+
stale: boolean;
|
|
722
|
+
hasPendingWrites: boolean;
|
|
723
|
+
changes: Array<{
|
|
724
|
+
type: 'added' | 'modified' | 'removed';
|
|
725
|
+
doc: DbDoc<T>;
|
|
726
|
+
}>;
|
|
727
|
+
}
|
|
728
|
+
type DbChangeType = 'added' | 'modified' | 'removed';
|
|
729
|
+
/**
|
|
730
|
+
* Operação atômica para uso em `DbCollectionRef.batch()`.
|
|
731
|
+
*
|
|
732
|
+
* Discriminated union pelo campo `kind` — cada variante deixa claro
|
|
733
|
+
* quais campos são obrigatórios:
|
|
734
|
+
*
|
|
735
|
+
* - `add` — insere com id gerado pelo cliente; `data` obrigatório.
|
|
736
|
+
* - `set` — cria ou sobrescreve pelo `id`; `data` obrigatório.
|
|
737
|
+
* - `update` — mescla campos em doc existente pelo `id`; `data` obrigatório.
|
|
738
|
+
* - `remove` — tombstone pelo `id`; sem `data`.
|
|
739
|
+
*
|
|
740
|
+
* @example
|
|
741
|
+
* ```ts
|
|
742
|
+
* const ops: DbBatchOp[] = [
|
|
743
|
+
* { kind: 'add', data: { name: 'produto A', price: 10 } },
|
|
744
|
+
* { kind: 'set', id: 'sku-1', data: { name: 'produto B', price: 20 } },
|
|
745
|
+
* { kind: 'update', id: 'sku-2', data: { price: 25 } },
|
|
746
|
+
* { kind: 'remove', id: 'sku-3' },
|
|
747
|
+
* ];
|
|
748
|
+
* await col.batch(ops);
|
|
749
|
+
* ```
|
|
750
|
+
*/
|
|
751
|
+
type DbBatchOp<T = Record<string, unknown>> = {
|
|
752
|
+
kind: 'add';
|
|
753
|
+
data: WithFieldSentinels<Omit<T, 'id'>>;
|
|
754
|
+
} | {
|
|
755
|
+
kind: 'set';
|
|
756
|
+
id: string;
|
|
757
|
+
data: WithFieldSentinels<Omit<T, 'id'>>;
|
|
758
|
+
} | {
|
|
759
|
+
kind: 'update';
|
|
760
|
+
id: string;
|
|
761
|
+
data: Partial<WithFieldSentinels<Omit<T, 'id'>>>;
|
|
762
|
+
} | {
|
|
763
|
+
kind: 'remove';
|
|
764
|
+
id: string;
|
|
765
|
+
};
|
|
766
|
+
/**
|
|
767
|
+
* Contrato do transporte de realtime — injetado no namespace.
|
|
768
|
+
*
|
|
769
|
+
* A implementação real usa os endpoints `/api/sdk/v1/db/*` do Core.
|
|
770
|
+
* Em testes, usa-se um fake in-memory.
|
|
771
|
+
*
|
|
772
|
+
* Este é o **seam de injeção** que conecta a camada offline ao servidor real.
|
|
773
|
+
* Transportes futuros (Firestore Web SDK direto / WebSocket gateway) são
|
|
774
|
+
* implementados aqui, sem modificar a superfície pública `DbCollectionRef`.
|
|
775
|
+
*/
|
|
776
|
+
type RealtimeTransport = SyncTransport;
|
|
777
|
+
/**
|
|
778
|
+
* Referência a um documento específico.
|
|
779
|
+
* Parte da superfície `DbCollectionRef.doc(id)`.
|
|
780
|
+
*
|
|
781
|
+
* Expõe as propriedades de identidade do documento de forma análoga ao
|
|
782
|
+
* `DocumentReference` do Firestore SDK, evitando a necessidade de adaptadores
|
|
783
|
+
* externos que reattachavam `collection` + `id` manualmente.
|
|
784
|
+
*
|
|
785
|
+
* @example
|
|
786
|
+
* ```ts
|
|
787
|
+
* const ref = orders.doc('order-123');
|
|
788
|
+
* console.log(ref.id); // "order-123"
|
|
789
|
+
* console.log(ref.collection); // "orders"
|
|
790
|
+
* console.log(ref.path); // "orders/order-123"
|
|
791
|
+
* ```
|
|
792
|
+
*/
|
|
793
|
+
interface DbDocRef<T = Record<string, unknown>> {
|
|
794
|
+
/** ID do documento dentro da coleção. */
|
|
795
|
+
readonly id: string;
|
|
796
|
+
/** Caminho completo no formato "collection/docId". */
|
|
797
|
+
readonly path: string;
|
|
798
|
+
/** Nome da coleção parent. */
|
|
799
|
+
readonly collection: string;
|
|
800
|
+
get(): Promise<DbGetResult<T> | null>;
|
|
801
|
+
set(data: WithFieldSentinels<Omit<T, 'id'>>): Promise<{
|
|
802
|
+
ok: true;
|
|
803
|
+
}>;
|
|
804
|
+
update(data: Partial<WithFieldSentinels<Omit<T, 'id'>>>): Promise<{
|
|
805
|
+
ok: true;
|
|
806
|
+
}>;
|
|
807
|
+
remove(): Promise<{
|
|
808
|
+
ok: true;
|
|
809
|
+
}>;
|
|
810
|
+
onSnapshot(cb: (snap: DbGetResult<T> | null) => void): Unsubscribe;
|
|
811
|
+
}
|
|
812
|
+
/**
|
|
813
|
+
* Referência a uma coleção — superfície dupla CRUD + realtime.
|
|
814
|
+
*
|
|
815
|
+
* Conforme 02-sdk.md §3.2 / I2 §3.2.
|
|
816
|
+
*/
|
|
817
|
+
interface DbCollectionRef$1<T = Record<string, unknown>> {
|
|
818
|
+
/** Retorna snapshot de um documento. `null` se não existir (nem no cache). */
|
|
819
|
+
get(id: string): Promise<DbGetResult<T> | null>;
|
|
820
|
+
/** Lista documentos com query opcional. */
|
|
821
|
+
list(q?: DbQuery$1): Promise<DbListResult<T>>;
|
|
822
|
+
/** Adiciona doc com id gerado pelo cliente (UUID v4). Durável localmente. */
|
|
823
|
+
add(data: WithFieldSentinels<Omit<T, 'id'>>): Promise<{
|
|
824
|
+
ok: true;
|
|
825
|
+
id: string;
|
|
826
|
+
}>;
|
|
827
|
+
/** Cria ou sobrescreve um doc pelo id. Durável localmente. */
|
|
828
|
+
set(id: string, data: WithFieldSentinels<Omit<T, 'id'>>): Promise<{
|
|
829
|
+
ok: true;
|
|
830
|
+
}>;
|
|
831
|
+
/** Atualiza campos de um doc (merge). Durável localmente. */
|
|
832
|
+
update(id: string, data: Partial<WithFieldSentinels<Omit<T, 'id'>>>): Promise<{
|
|
833
|
+
ok: true;
|
|
834
|
+
}>;
|
|
835
|
+
/** Remove um doc (tombstone local). Durável localmente. */
|
|
836
|
+
remove(id: string): Promise<{
|
|
837
|
+
ok: true;
|
|
838
|
+
}>;
|
|
839
|
+
/** Aplica múltiplas operações atomicamente na fila. */
|
|
840
|
+
batch(ops: DbBatchOp<T>[]): Promise<{
|
|
841
|
+
ok: true;
|
|
842
|
+
}>;
|
|
843
|
+
/** Escuta um documento específico. */
|
|
844
|
+
onDoc(id: string, cb: (doc: T | null) => void): Unsubscribe;
|
|
845
|
+
/** Escuta a coleção inteira (ou subconjunto via query). */
|
|
846
|
+
onSnapshot(q: DbQuery$1 | undefined, cb: (snap: DbListResult<T>) => void): Unsubscribe;
|
|
847
|
+
/** Retorna uma referência ao documento `id`. */
|
|
848
|
+
doc(id: string): DbDocRef<T>;
|
|
849
|
+
}
|
|
850
|
+
/**
|
|
851
|
+
* Namespace `db.documents` — ponto de entrada para coleções de documentos.
|
|
852
|
+
*
|
|
853
|
+
* Analogia a `NeetruDb` de 02-sdk.md §3.1 mas escoped para o Mundo Documentos.
|
|
854
|
+
*/
|
|
855
|
+
interface NeetruDbDocuments {
|
|
856
|
+
collection<T = Record<string, unknown>>(name: string): DbCollectionRef$1<T>;
|
|
857
|
+
/** Estado atual de sincronização (offline/online/syncing). */
|
|
858
|
+
readonly syncState: SyncState;
|
|
859
|
+
onSyncStateChanged(cb: (s: SyncState) => void): Unsubscribe;
|
|
860
|
+
/** Força flush da fila de mutações pendentes. */
|
|
861
|
+
flush(): Promise<void>;
|
|
862
|
+
/** Limpa o cache local (IndexedDB) — operação destrutiva. */
|
|
863
|
+
clearCache(): Promise<void>;
|
|
864
|
+
/**
|
|
865
|
+
* Retorna os registros do log de conflitos LWW gravados pelo SyncEngine.
|
|
866
|
+
* Um conflito ocorre quando a mutação local é descartada porque o servidor
|
|
867
|
+
* já avançou a versão do documento (Last-Write-Wins).
|
|
868
|
+
*/
|
|
869
|
+
getConflicts(): Promise<ConflictRecord[]>;
|
|
870
|
+
}
|
|
871
|
+
interface CreateOfflineDocumentsOptions {
|
|
872
|
+
/**
|
|
873
|
+
* Nome do banco IndexedDB — deve ser único por produto × dbId × env.
|
|
874
|
+
* Convenção: `neetru-db__{productSlug}__{dbId}__{env}`.
|
|
875
|
+
*/
|
|
876
|
+
dbName: string;
|
|
877
|
+
/**
|
|
878
|
+
* Transporte de sync — implementa `SyncTransport` do SyncEngine.
|
|
879
|
+
* Este é o **seam de injeção** do transporte real.
|
|
880
|
+
*
|
|
881
|
+
* Implementações possíveis:
|
|
882
|
+
* - `RestSyncTransport` — REST → Core (padrão em staging/prod)
|
|
883
|
+
* - `FirestoreRealtimeTransport` — Firestore Web SDK direto (engine=firestore)
|
|
884
|
+
* - `WebSocketTransport` — WebSocket → gateway (engine=nosql-vm)
|
|
885
|
+
* - `FakeRealtimeTransport` — in-memory (testes)
|
|
886
|
+
*/
|
|
887
|
+
transport: RealtimeTransport;
|
|
888
|
+
/**
|
|
889
|
+
* Estado inicial de conectividade.
|
|
890
|
+
* Default: `navigator.onLine` se disponível, senão `true`.
|
|
891
|
+
*/
|
|
892
|
+
startOnline?: boolean;
|
|
893
|
+
/**
|
|
894
|
+
* `true` = sem contenção multi-aba (sempre líder).
|
|
895
|
+
* Padrão em testes; em produção usa Web Locks.
|
|
896
|
+
*/
|
|
897
|
+
singleTab?: boolean;
|
|
898
|
+
/**
|
|
899
|
+
* Intervalo do sync periódico em ms. `0` desativa.
|
|
900
|
+
* Default: 5 * 60 * 1000 (5min).
|
|
901
|
+
*/
|
|
902
|
+
periodicSyncIntervalMs?: number;
|
|
903
|
+
}
|
|
904
|
+
|
|
326
905
|
declare const DELTA_OPS: readonly ["delta", "resync", "stale", "error", "pong", "drain"];
|
|
327
906
|
type DeltaOp = (typeof DELTA_OPS)[number];
|
|
328
907
|
interface DbQuery {
|
|
@@ -1515,4 +2094,4 @@ interface CatalogListOptions {
|
|
|
1515
2094
|
includeDrafts?: boolean;
|
|
1516
2095
|
}
|
|
1517
2096
|
|
|
1518
|
-
export { type
|
|
2097
|
+
export { type RealtimeTransport as $, type AuthNamespace as A, type NeetruClientConfig as B, type CatalogListOptions as C, DEFAULT_BASE_URL as D, type EntitlementCheck as E, type FieldSentinel as F, type NeetruDb as G, type NeetruDbDocuments as H, type IncrementSentinel as I, type NeetruDbEngine as J, NeetruDbError as K, type ListNotificationsOptions as L, MockCheckout as M, NEETRU_SENTINEL_KEY as N, type NeetruDbErrorCode as O, type NeetruDbOptions as P, type NeetruDbTransport as Q, type NeetruEnv as R, type NeetruSqlClient as S, type NeetruUser as T, type NotificationScopeOptions as U, type NotificationsNamespace as V, type Product as W, type ProductNotification as X, type ProductNotificationSeverity as Y, type ProductPlan as Z, type ProductStatus as _, type AuthStateListener as a, type RegisterWebhookInput as a0, type ResolvedConfig as a1, type SendNotificationInput as a2, type ServerTimestampSentinel as a3, type SignInOptions as a4, type SqlOptions as a5, type SupportNamespace as a6, type SupportSeverity as a7, type SupportStatus as a8, type SupportTicket as a9, serverTimestamp as aA, verifyWebhookSignature as aB, type SyncState as aa, type TelemetryEventAck as ab, type TelemetryEventInput as ac, type TelemetryLogAck as ad, type TelemetryLogInput as ae, type Unsubscribe as af, type UsageEventInput as ag, type UsageNamespace as ah, type UsageQuota as ai, type VerifyTokenOptions as aj, type WebhookEndpoint as ak, type WebhookEvent as al, type WebhookScopeOptions as am, type WebhookTestResult as an, type WebhooksNamespace as ao, type WithFieldSentinels as ap, createCheckoutNamespace as aq, createNeetruDb as ar, createNotificationsNamespace as as, createRestSyncTransport as at, createWebhooksNamespace as au, getWebSocketRealtimeClient as av, increment as aw, isFieldSentinel as ax, isIncrementSentinel as ay, isServerTimestampSentinel as az, type CatalogListResponse as b, type CheckoutIntentInfo as c, type CheckoutIntentStatus as d, type CheckoutNamespace as e, type CheckoutStartInput as f, type CheckoutStartResult as g, type CheckoutTenantType as h, type ConflictRecord as i, type CreateOfflineDocumentsOptions as j, type CreateTicketInput as k, type DbBatchOp as l, type DbChangeType as m, type DbCollectionRef$1 as n, type DbDoc as o, type DbDocRef as p, type DbGetResult as q, type DbListResult as r, type DbNamespace as s, type DbQuery$1 as t, type DbSqlLease as u, type DbWhereFilter as v, type DbWhereFilter$1 as w, MockNotifications as x, MockWebhooks as y, type NeetruClient as z };
|
package/dist/usage.d.cts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import './collection-ref-BDdfD87k.cjs';
|
|
1
|
+
import { a1 as ResolvedConfig, ah as UsageNamespace } from './types-nwErcRX8.cjs';
|
|
3
2
|
import 'drizzle-orm/node-postgres';
|
|
4
3
|
import './errors.cjs';
|
|
5
4
|
|
package/dist/usage.d.ts
CHANGED
package/dist/webhooks.d.cts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
export {
|
|
2
|
-
import './collection-ref-BDdfD87k.cjs';
|
|
1
|
+
export { y as MockWebhooks, a0 as RegisterWebhookInput, ak as WebhookEndpoint, al as WebhookEvent, am as WebhookScopeOptions, an as WebhookTestResult, ao as WebhooksNamespace, au as createWebhooksNamespace, aB as verifyWebhookSignature } from './types-nwErcRX8.cjs';
|
|
3
2
|
import 'drizzle-orm/node-postgres';
|
|
4
3
|
import './errors.cjs';
|
package/dist/webhooks.d.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
export {
|
|
2
|
-
import './collection-ref-BDdfD87k.js';
|
|
1
|
+
export { y as MockWebhooks, a0 as RegisterWebhookInput, ak as WebhookEndpoint, al as WebhookEvent, am as WebhookScopeOptions, an as WebhookTestResult, ao as WebhooksNamespace, au as createWebhooksNamespace, aB as verifyWebhookSignature } from './types-BRv8wBxX.js';
|
|
3
2
|
import 'drizzle-orm/node-postgres';
|
|
4
3
|
import './errors.js';
|