@neetru/sdk 3.0.1 → 3.0.2
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 +38 -0
- package/dist/auth.cjs +61 -13
- package/dist/auth.cjs.map +1 -1
- package/dist/auth.d.cts +2 -2
- package/dist/auth.d.ts +2 -2
- package/dist/auth.mjs +61 -13
- package/dist/auth.mjs.map +1 -1
- package/dist/catalog.d.cts +2 -2
- package/dist/catalog.d.ts +2 -2
- package/dist/checkout.d.cts +2 -2
- package/dist/checkout.d.ts +2 -2
- package/dist/{collection-ref-DqAAhuhX.d.cts → collection-ref-BDdfD87k.d.cts} +118 -9
- package/dist/{collection-ref-DqAAhuhX.d.ts → collection-ref-BDdfD87k.d.ts} +118 -9
- package/dist/db-react.d.cts +1 -1
- package/dist/db-react.d.ts +1 -1
- package/dist/db.cjs +81 -16
- package/dist/db.cjs.map +1 -1
- package/dist/db.d.cts +2 -2
- package/dist/db.d.ts +2 -2
- package/dist/db.mjs +76 -17
- package/dist/db.mjs.map +1 -1
- package/dist/entitlements.d.cts +2 -2
- package/dist/entitlements.d.ts +2 -2
- package/dist/index.cjs +81 -16
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.mjs +76 -17
- package/dist/index.mjs.map +1 -1
- package/dist/mocks.d.cts +2 -2
- package/dist/mocks.d.ts +2 -2
- package/dist/notifications.d.cts +2 -2
- package/dist/notifications.d.ts +2 -2
- package/dist/react.d.cts +2 -2
- package/dist/react.d.ts +2 -2
- package/dist/support.d.cts +2 -2
- package/dist/support.d.ts +2 -2
- package/dist/telemetry.d.cts +2 -2
- package/dist/telemetry.d.ts +2 -2
- package/dist/{types-DALIhcbq.d.ts → types-D7zVkO3n.d.ts} +1 -1
- package/dist/{types-Lfd3LiAF.d.cts → types-Dc4bjrrt.d.cts} +1 -1
- package/dist/usage.d.cts +2 -2
- package/dist/usage.d.ts +2 -2
- package/dist/webhooks.d.cts +2 -2
- package/dist/webhooks.d.ts +2 -2
- package/package.json +1 -1
package/dist/catalog.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { I as ResolvedConfig, C as CatalogListOptions, b as CatalogListResponse, P as Product } from './types-
|
|
2
|
-
import './collection-ref-
|
|
1
|
+
import { I as ResolvedConfig, C as CatalogListOptions, b as CatalogListResponse, P as Product } from './types-Dc4bjrrt.cjs';
|
|
2
|
+
import './collection-ref-BDdfD87k.cjs';
|
|
3
3
|
import 'drizzle-orm/node-postgres';
|
|
4
4
|
import './errors.cjs';
|
|
5
5
|
|
package/dist/catalog.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { I as ResolvedConfig, C as CatalogListOptions, b as CatalogListResponse, P as Product } from './types-
|
|
2
|
-
import './collection-ref-
|
|
1
|
+
import { I as ResolvedConfig, C as CatalogListOptions, b as CatalogListResponse, P as Product } from './types-D7zVkO3n.js';
|
|
2
|
+
import './collection-ref-BDdfD87k.js';
|
|
3
3
|
import 'drizzle-orm/node-postgres';
|
|
4
4
|
import './errors.js';
|
|
5
5
|
|
package/dist/checkout.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { c as CheckoutIntentInfo, d as CheckoutIntentStatus, e as CheckoutNamespace, f as CheckoutStartInput, g as CheckoutStartResult, h as CheckoutTenantType, M as MockCheckout, a6 as createCheckoutNamespace } from './types-
|
|
2
|
-
import './collection-ref-
|
|
1
|
+
export { c as CheckoutIntentInfo, d as CheckoutIntentStatus, e as CheckoutNamespace, f as CheckoutStartInput, g as CheckoutStartResult, h as CheckoutTenantType, M as MockCheckout, a6 as createCheckoutNamespace } from './types-Dc4bjrrt.cjs';
|
|
2
|
+
import './collection-ref-BDdfD87k.cjs';
|
|
3
3
|
import 'drizzle-orm/node-postgres';
|
|
4
4
|
import './errors.cjs';
|
package/dist/checkout.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { c as CheckoutIntentInfo, d as CheckoutIntentStatus, e as CheckoutNamespace, f as CheckoutStartInput, g as CheckoutStartResult, h as CheckoutTenantType, M as MockCheckout, a6 as createCheckoutNamespace } from './types-
|
|
2
|
-
import './collection-ref-
|
|
1
|
+
export { c as CheckoutIntentInfo, d as CheckoutIntentStatus, e as CheckoutNamespace, f as CheckoutStartInput, g as CheckoutStartResult, h as CheckoutTenantType, M as MockCheckout, a6 as createCheckoutNamespace } from './types-D7zVkO3n.js';
|
|
2
|
+
import './collection-ref-BDdfD87k.js';
|
|
3
3
|
import 'drizzle-orm/node-postgres';
|
|
4
4
|
import './errors.js';
|
|
@@ -228,6 +228,98 @@ interface SyncTransport {
|
|
|
228
228
|
}>, needsResync: boolean) => void) => () => void;
|
|
229
229
|
}
|
|
230
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
|
+
|
|
231
323
|
/**
|
|
232
324
|
* DbCollectionRef — superfície de Documentos offline-first do @neetru/sdk (M2).
|
|
233
325
|
*
|
|
@@ -251,6 +343,23 @@ interface SyncTransport {
|
|
|
251
343
|
* sem alterar a superfície pública.
|
|
252
344
|
*/
|
|
253
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
|
+
};
|
|
254
363
|
interface DbWhereFilter {
|
|
255
364
|
field: string;
|
|
256
365
|
op: '==' | '!=' | '<' | '<=' | '>' | '>=' | 'array-contains' | 'in' | 'not-in';
|
|
@@ -317,15 +426,15 @@ type DbChangeType = 'added' | 'modified' | 'removed';
|
|
|
317
426
|
*/
|
|
318
427
|
type DbBatchOp<T = Record<string, unknown>> = {
|
|
319
428
|
kind: 'add';
|
|
320
|
-
data: Omit<T, 'id'
|
|
429
|
+
data: WithFieldSentinels<Omit<T, 'id'>>;
|
|
321
430
|
} | {
|
|
322
431
|
kind: 'set';
|
|
323
432
|
id: string;
|
|
324
|
-
data: Omit<T, 'id'
|
|
433
|
+
data: WithFieldSentinels<Omit<T, 'id'>>;
|
|
325
434
|
} | {
|
|
326
435
|
kind: 'update';
|
|
327
436
|
id: string;
|
|
328
|
-
data: Partial<Omit<T, 'id'
|
|
437
|
+
data: Partial<WithFieldSentinels<Omit<T, 'id'>>>;
|
|
329
438
|
} | {
|
|
330
439
|
kind: 'remove';
|
|
331
440
|
id: string;
|
|
@@ -365,10 +474,10 @@ interface DbDocRef<T = Record<string, unknown>> {
|
|
|
365
474
|
/** Nome da coleção parent. */
|
|
366
475
|
readonly collection: string;
|
|
367
476
|
get(): Promise<DbGetResult<T> | null>;
|
|
368
|
-
set(data: Omit<T, 'id'
|
|
477
|
+
set(data: WithFieldSentinels<Omit<T, 'id'>>): Promise<{
|
|
369
478
|
ok: true;
|
|
370
479
|
}>;
|
|
371
|
-
update(data: Partial<Omit<T, 'id'
|
|
480
|
+
update(data: Partial<WithFieldSentinels<Omit<T, 'id'>>>): Promise<{
|
|
372
481
|
ok: true;
|
|
373
482
|
}>;
|
|
374
483
|
remove(): Promise<{
|
|
@@ -387,16 +496,16 @@ interface DbCollectionRef<T = Record<string, unknown>> {
|
|
|
387
496
|
/** Lista documentos com query opcional. */
|
|
388
497
|
list(q?: DbQuery): Promise<DbListResult<T>>;
|
|
389
498
|
/** Adiciona doc com id gerado pelo cliente (UUID v4). Durável localmente. */
|
|
390
|
-
add(data: Omit<T, 'id'
|
|
499
|
+
add(data: WithFieldSentinels<Omit<T, 'id'>>): Promise<{
|
|
391
500
|
ok: true;
|
|
392
501
|
id: string;
|
|
393
502
|
}>;
|
|
394
503
|
/** Cria ou sobrescreve um doc pelo id. Durável localmente. */
|
|
395
|
-
set(id: string, data: Omit<T, 'id'
|
|
504
|
+
set(id: string, data: WithFieldSentinels<Omit<T, 'id'>>): Promise<{
|
|
396
505
|
ok: true;
|
|
397
506
|
}>;
|
|
398
507
|
/** Atualiza campos de um doc (merge). Durável localmente. */
|
|
399
|
-
update(id: string, data: Partial<Omit<T, 'id'
|
|
508
|
+
update(id: string, data: Partial<WithFieldSentinels<Omit<T, 'id'>>>): Promise<{
|
|
400
509
|
ok: true;
|
|
401
510
|
}>;
|
|
402
511
|
/** Remove um doc (tombstone local). Durável localmente. */
|
|
@@ -469,4 +578,4 @@ interface CreateOfflineDocumentsOptions {
|
|
|
469
578
|
periodicSyncIntervalMs?: number;
|
|
470
579
|
}
|
|
471
580
|
|
|
472
|
-
export type
|
|
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 };
|
|
@@ -228,6 +228,98 @@ interface SyncTransport {
|
|
|
228
228
|
}>, needsResync: boolean) => void) => () => void;
|
|
229
229
|
}
|
|
230
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
|
+
|
|
231
323
|
/**
|
|
232
324
|
* DbCollectionRef — superfície de Documentos offline-first do @neetru/sdk (M2).
|
|
233
325
|
*
|
|
@@ -251,6 +343,23 @@ interface SyncTransport {
|
|
|
251
343
|
* sem alterar a superfície pública.
|
|
252
344
|
*/
|
|
253
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
|
+
};
|
|
254
363
|
interface DbWhereFilter {
|
|
255
364
|
field: string;
|
|
256
365
|
op: '==' | '!=' | '<' | '<=' | '>' | '>=' | 'array-contains' | 'in' | 'not-in';
|
|
@@ -317,15 +426,15 @@ type DbChangeType = 'added' | 'modified' | 'removed';
|
|
|
317
426
|
*/
|
|
318
427
|
type DbBatchOp<T = Record<string, unknown>> = {
|
|
319
428
|
kind: 'add';
|
|
320
|
-
data: Omit<T, 'id'
|
|
429
|
+
data: WithFieldSentinels<Omit<T, 'id'>>;
|
|
321
430
|
} | {
|
|
322
431
|
kind: 'set';
|
|
323
432
|
id: string;
|
|
324
|
-
data: Omit<T, 'id'
|
|
433
|
+
data: WithFieldSentinels<Omit<T, 'id'>>;
|
|
325
434
|
} | {
|
|
326
435
|
kind: 'update';
|
|
327
436
|
id: string;
|
|
328
|
-
data: Partial<Omit<T, 'id'
|
|
437
|
+
data: Partial<WithFieldSentinels<Omit<T, 'id'>>>;
|
|
329
438
|
} | {
|
|
330
439
|
kind: 'remove';
|
|
331
440
|
id: string;
|
|
@@ -365,10 +474,10 @@ interface DbDocRef<T = Record<string, unknown>> {
|
|
|
365
474
|
/** Nome da coleção parent. */
|
|
366
475
|
readonly collection: string;
|
|
367
476
|
get(): Promise<DbGetResult<T> | null>;
|
|
368
|
-
set(data: Omit<T, 'id'
|
|
477
|
+
set(data: WithFieldSentinels<Omit<T, 'id'>>): Promise<{
|
|
369
478
|
ok: true;
|
|
370
479
|
}>;
|
|
371
|
-
update(data: Partial<Omit<T, 'id'
|
|
480
|
+
update(data: Partial<WithFieldSentinels<Omit<T, 'id'>>>): Promise<{
|
|
372
481
|
ok: true;
|
|
373
482
|
}>;
|
|
374
483
|
remove(): Promise<{
|
|
@@ -387,16 +496,16 @@ interface DbCollectionRef<T = Record<string, unknown>> {
|
|
|
387
496
|
/** Lista documentos com query opcional. */
|
|
388
497
|
list(q?: DbQuery): Promise<DbListResult<T>>;
|
|
389
498
|
/** Adiciona doc com id gerado pelo cliente (UUID v4). Durável localmente. */
|
|
390
|
-
add(data: Omit<T, 'id'
|
|
499
|
+
add(data: WithFieldSentinels<Omit<T, 'id'>>): Promise<{
|
|
391
500
|
ok: true;
|
|
392
501
|
id: string;
|
|
393
502
|
}>;
|
|
394
503
|
/** Cria ou sobrescreve um doc pelo id. Durável localmente. */
|
|
395
|
-
set(id: string, data: Omit<T, 'id'
|
|
504
|
+
set(id: string, data: WithFieldSentinels<Omit<T, 'id'>>): Promise<{
|
|
396
505
|
ok: true;
|
|
397
506
|
}>;
|
|
398
507
|
/** Atualiza campos de um doc (merge). Durável localmente. */
|
|
399
|
-
update(id: string, data: Partial<Omit<T, 'id'
|
|
508
|
+
update(id: string, data: Partial<WithFieldSentinels<Omit<T, 'id'>>>): Promise<{
|
|
400
509
|
ok: true;
|
|
401
510
|
}>;
|
|
402
511
|
/** Remove um doc (tombstone local). Durável localmente. */
|
|
@@ -469,4 +578,4 @@ interface CreateOfflineDocumentsOptions {
|
|
|
469
578
|
periodicSyncIntervalMs?: number;
|
|
470
579
|
}
|
|
471
580
|
|
|
472
|
-
export type
|
|
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 };
|
package/dist/db-react.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { k as SyncState, U as Unsubscribe, d as DbDoc, c as DbCollectionRef, h as DbQuery } from './collection-ref-BDdfD87k.cjs';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Resultado de `useDocument`.
|
package/dist/db-react.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { k as SyncState, U as Unsubscribe, d as DbDoc, c as DbCollectionRef, h as DbQuery } from './collection-ref-BDdfD87k.js';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Resultado de `useDocument`.
|
package/dist/db.cjs
CHANGED
|
@@ -1540,7 +1540,7 @@ var SyncEngine = class {
|
|
|
1540
1540
|
return true;
|
|
1541
1541
|
}
|
|
1542
1542
|
/** Atualiza o cache após confirmação de escrita (outcome=confirmed). */
|
|
1543
|
-
async _applySyncedDoc(collection, docId, serverVersion,
|
|
1543
|
+
async _applySyncedDoc(collection, docId, serverVersion, serverTimestamp2, mut, busChanges) {
|
|
1544
1544
|
const existing = await this._store.getDoc(collection, docId);
|
|
1545
1545
|
if (mut.op === "remove") {
|
|
1546
1546
|
if (existing) {
|
|
@@ -1549,7 +1549,7 @@ var SyncEngine = class {
|
|
|
1549
1549
|
meta: {
|
|
1550
1550
|
...existing.meta,
|
|
1551
1551
|
serverVersion,
|
|
1552
|
-
updatedAtServer:
|
|
1552
|
+
updatedAtServer: serverTimestamp2,
|
|
1553
1553
|
updatedAtLocal: Date.now(),
|
|
1554
1554
|
state: "synced",
|
|
1555
1555
|
deleted: true,
|
|
@@ -1567,7 +1567,7 @@ var SyncEngine = class {
|
|
|
1567
1567
|
data: mut.op === "update" ? { ...existing?.data ?? {}, ...newData } : newData,
|
|
1568
1568
|
meta: {
|
|
1569
1569
|
serverVersion,
|
|
1570
|
-
updatedAtServer:
|
|
1570
|
+
updatedAtServer: serverTimestamp2,
|
|
1571
1571
|
updatedAtLocal: Date.now(),
|
|
1572
1572
|
state: "synced",
|
|
1573
1573
|
pendingMutationIds: [],
|
|
@@ -2646,6 +2646,54 @@ var TabCoordinator = class {
|
|
|
2646
2646
|
|
|
2647
2647
|
// src/db/collection-ref.ts
|
|
2648
2648
|
init_db_errors();
|
|
2649
|
+
|
|
2650
|
+
// src/db/field-value.ts
|
|
2651
|
+
var NEETRU_SENTINEL_KEY = "__neetru__";
|
|
2652
|
+
function serverTimestamp() {
|
|
2653
|
+
return { __neetru__: "serverTimestamp" };
|
|
2654
|
+
}
|
|
2655
|
+
function increment(n) {
|
|
2656
|
+
if (typeof n !== "number" || !Number.isFinite(n)) {
|
|
2657
|
+
throw new TypeError(
|
|
2658
|
+
`increment(n): n deve ser um n\xFAmero finito (recebido: ${String(n)}).`
|
|
2659
|
+
);
|
|
2660
|
+
}
|
|
2661
|
+
return { __neetru__: "increment", operand: n };
|
|
2662
|
+
}
|
|
2663
|
+
function isFieldSentinel(v) {
|
|
2664
|
+
return typeof v === "object" && v !== null && NEETRU_SENTINEL_KEY in v && typeof v[NEETRU_SENTINEL_KEY] === "string";
|
|
2665
|
+
}
|
|
2666
|
+
function isServerTimestampSentinel(v) {
|
|
2667
|
+
return isFieldSentinel(v) && v.__neetru__ === "serverTimestamp";
|
|
2668
|
+
}
|
|
2669
|
+
function isIncrementSentinel(v) {
|
|
2670
|
+
return isFieldSentinel(v) && v.__neetru__ === "increment" && typeof v.operand === "number" && Number.isFinite(v.operand);
|
|
2671
|
+
}
|
|
2672
|
+
function resolveSentinelLocally(value, baseValue) {
|
|
2673
|
+
if (isServerTimestampSentinel(value)) {
|
|
2674
|
+
return Date.now();
|
|
2675
|
+
}
|
|
2676
|
+
if (isIncrementSentinel(value)) {
|
|
2677
|
+
const base = typeof baseValue === "number" && Number.isFinite(baseValue) ? baseValue : 0;
|
|
2678
|
+
return base + value.operand;
|
|
2679
|
+
}
|
|
2680
|
+
return value;
|
|
2681
|
+
}
|
|
2682
|
+
function resolveSentinelsLocally(data, existing) {
|
|
2683
|
+
let touched = false;
|
|
2684
|
+
const out = {};
|
|
2685
|
+
for (const [k, v] of Object.entries(data)) {
|
|
2686
|
+
if (isFieldSentinel(v)) {
|
|
2687
|
+
touched = true;
|
|
2688
|
+
out[k] = resolveSentinelLocally(v, existing?.[k]);
|
|
2689
|
+
} else {
|
|
2690
|
+
out[k] = v;
|
|
2691
|
+
}
|
|
2692
|
+
}
|
|
2693
|
+
return touched ? out : data;
|
|
2694
|
+
}
|
|
2695
|
+
|
|
2696
|
+
// src/db/collection-ref.ts
|
|
2649
2697
|
function isIndexedDBUnavailable() {
|
|
2650
2698
|
return typeof indexedDB === "undefined" || typeof window === "undefined";
|
|
2651
2699
|
}
|
|
@@ -2774,10 +2822,12 @@ var DbCollectionRefImpl = class {
|
|
|
2774
2822
|
return this._buildListResult(q);
|
|
2775
2823
|
}
|
|
2776
2824
|
async add(data) {
|
|
2825
|
+
const rawData = data;
|
|
2826
|
+
const localData = resolveSentinelsLocally(rawData, void 0);
|
|
2777
2827
|
const mutation = await this._queue.enqueue({
|
|
2778
2828
|
collection: this._collection,
|
|
2779
2829
|
op: "add",
|
|
2780
|
-
payload:
|
|
2830
|
+
payload: rawData,
|
|
2781
2831
|
baseVersion: null,
|
|
2782
2832
|
batchId: null
|
|
2783
2833
|
});
|
|
@@ -2785,7 +2835,7 @@ var DbCollectionRefImpl = class {
|
|
|
2785
2835
|
await this._store.putDoc({
|
|
2786
2836
|
collection: this._collection,
|
|
2787
2837
|
id: docId,
|
|
2788
|
-
data,
|
|
2838
|
+
data: localData,
|
|
2789
2839
|
meta: {
|
|
2790
2840
|
serverVersion: null,
|
|
2791
2841
|
updatedAtServer: null,
|
|
@@ -2799,25 +2849,27 @@ var DbCollectionRefImpl = class {
|
|
|
2799
2849
|
this._bus.emit([{
|
|
2800
2850
|
type: "added",
|
|
2801
2851
|
collection: this._collection,
|
|
2802
|
-
doc: { id: docId, data }
|
|
2852
|
+
doc: { id: docId, data: localData }
|
|
2803
2853
|
}]);
|
|
2804
2854
|
return { ok: true, id: docId };
|
|
2805
2855
|
}
|
|
2806
2856
|
async set(id, data) {
|
|
2807
2857
|
assertValidId(id);
|
|
2808
2858
|
const existing = await this._store.getDoc(this._collection, id);
|
|
2859
|
+
const rawData = data;
|
|
2860
|
+
const localData = resolveSentinelsLocally(rawData, void 0);
|
|
2809
2861
|
const mutation = await this._queue.enqueue({
|
|
2810
2862
|
collection: this._collection,
|
|
2811
2863
|
docId: id,
|
|
2812
2864
|
op: "set",
|
|
2813
|
-
payload:
|
|
2865
|
+
payload: rawData,
|
|
2814
2866
|
baseVersion: existing?.meta.serverVersion ?? null,
|
|
2815
2867
|
batchId: null
|
|
2816
2868
|
});
|
|
2817
2869
|
await this._store.putDoc({
|
|
2818
2870
|
collection: this._collection,
|
|
2819
2871
|
id,
|
|
2820
|
-
data,
|
|
2872
|
+
data: localData,
|
|
2821
2873
|
meta: {
|
|
2822
2874
|
serverVersion: existing?.meta.serverVersion ?? null,
|
|
2823
2875
|
updatedAtServer: existing?.meta.updatedAtServer ?? null,
|
|
@@ -2831,19 +2883,22 @@ var DbCollectionRefImpl = class {
|
|
|
2831
2883
|
this._bus.emit([{
|
|
2832
2884
|
type: existing && !existing.meta.deleted ? "modified" : "added",
|
|
2833
2885
|
collection: this._collection,
|
|
2834
|
-
doc: { id, data }
|
|
2886
|
+
doc: { id, data: localData }
|
|
2835
2887
|
}]);
|
|
2836
2888
|
return { ok: true };
|
|
2837
2889
|
}
|
|
2838
2890
|
async update(id, data) {
|
|
2839
2891
|
assertValidId(id);
|
|
2840
2892
|
const existing = await this._store.getDoc(this._collection, id);
|
|
2841
|
-
const
|
|
2893
|
+
const rawData = data;
|
|
2894
|
+
const existingBase = existing?.meta.deleted ? void 0 : existing?.data;
|
|
2895
|
+
const localData = resolveSentinelsLocally(rawData, existingBase);
|
|
2896
|
+
const mergedData = existing?.meta.deleted ? { ...localData } : { ...existing?.data ?? {}, ...localData };
|
|
2842
2897
|
const mutation = await this._queue.enqueue({
|
|
2843
2898
|
collection: this._collection,
|
|
2844
2899
|
docId: id,
|
|
2845
2900
|
op: "update",
|
|
2846
|
-
payload:
|
|
2901
|
+
payload: rawData,
|
|
2847
2902
|
baseVersion: existing?.meta.serverVersion ?? null,
|
|
2848
2903
|
batchId: null
|
|
2849
2904
|
});
|
|
@@ -2900,6 +2955,7 @@ var DbCollectionRefImpl = class {
|
|
|
2900
2955
|
const collection = this._collection;
|
|
2901
2956
|
if (op.kind === "add") {
|
|
2902
2957
|
const data = op.data;
|
|
2958
|
+
const localData = resolveSentinelsLocally(data, void 0);
|
|
2903
2959
|
const mutation = await this._queue.enqueue({
|
|
2904
2960
|
collection,
|
|
2905
2961
|
op: "add",
|
|
@@ -2911,7 +2967,7 @@ var DbCollectionRefImpl = class {
|
|
|
2911
2967
|
await this._store.putDoc({
|
|
2912
2968
|
collection,
|
|
2913
2969
|
id: docId,
|
|
2914
|
-
data,
|
|
2970
|
+
data: localData,
|
|
2915
2971
|
meta: {
|
|
2916
2972
|
serverVersion: null,
|
|
2917
2973
|
updatedAtServer: null,
|
|
@@ -2922,11 +2978,12 @@ var DbCollectionRefImpl = class {
|
|
|
2922
2978
|
ownerId: null
|
|
2923
2979
|
}
|
|
2924
2980
|
});
|
|
2925
|
-
busChanges.push({ type: "added", collection, doc: { id: docId, data } });
|
|
2981
|
+
busChanges.push({ type: "added", collection, doc: { id: docId, data: localData } });
|
|
2926
2982
|
} else if (op.kind === "set") {
|
|
2927
2983
|
const id = op.id;
|
|
2928
2984
|
const data = op.data;
|
|
2929
2985
|
const existing = await this._store.getDoc(collection, id);
|
|
2986
|
+
const localData = resolveSentinelsLocally(data, void 0);
|
|
2930
2987
|
const mutation = await this._queue.enqueue({
|
|
2931
2988
|
collection,
|
|
2932
2989
|
docId: id,
|
|
@@ -2938,7 +2995,7 @@ var DbCollectionRefImpl = class {
|
|
|
2938
2995
|
await this._store.putDoc({
|
|
2939
2996
|
collection,
|
|
2940
2997
|
id,
|
|
2941
|
-
data,
|
|
2998
|
+
data: localData,
|
|
2942
2999
|
meta: {
|
|
2943
3000
|
serverVersion: existing?.meta.serverVersion ?? null,
|
|
2944
3001
|
updatedAtServer: existing?.meta.updatedAtServer ?? null,
|
|
@@ -2952,13 +3009,15 @@ var DbCollectionRefImpl = class {
|
|
|
2952
3009
|
busChanges.push({
|
|
2953
3010
|
type: existing && !existing.meta.deleted ? "modified" : "added",
|
|
2954
3011
|
collection,
|
|
2955
|
-
doc: { id, data }
|
|
3012
|
+
doc: { id, data: localData }
|
|
2956
3013
|
});
|
|
2957
3014
|
} else if (op.kind === "update") {
|
|
2958
3015
|
const id = op.id;
|
|
2959
3016
|
const data = op.data;
|
|
2960
3017
|
const existing = await this._store.getDoc(collection, id);
|
|
2961
|
-
const
|
|
3018
|
+
const existingBase = existing?.meta.deleted ? void 0 : existing?.data;
|
|
3019
|
+
const localData = resolveSentinelsLocally(data, existingBase);
|
|
3020
|
+
const merged = { ...existing?.data ?? {}, ...localData };
|
|
2962
3021
|
const mutation = await this._queue.enqueue({
|
|
2963
3022
|
collection,
|
|
2964
3023
|
docId: id,
|
|
@@ -3990,8 +4049,14 @@ function createLazyCollectionRef(name, getDocsPromise) {
|
|
|
3990
4049
|
};
|
|
3991
4050
|
}
|
|
3992
4051
|
|
|
4052
|
+
exports.NEETRU_SENTINEL_KEY = NEETRU_SENTINEL_KEY;
|
|
3993
4053
|
exports.createNeetruDb = createNeetruDb;
|
|
3994
4054
|
exports.createRestSyncTransport = createRestSyncTransport;
|
|
3995
4055
|
exports.getWebSocketRealtimeClient = getWebSocketRealtimeClient;
|
|
4056
|
+
exports.increment = increment;
|
|
4057
|
+
exports.isFieldSentinel = isFieldSentinel;
|
|
4058
|
+
exports.isIncrementSentinel = isIncrementSentinel;
|
|
4059
|
+
exports.isServerTimestampSentinel = isServerTimestampSentinel;
|
|
4060
|
+
exports.serverTimestamp = serverTimestamp;
|
|
3996
4061
|
//# sourceMappingURL=db.cjs.map
|
|
3997
4062
|
//# sourceMappingURL=db.cjs.map
|