@neetru/sdk 3.3.0 → 3.4.1
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 +43 -0
- package/dist/ai.d.cts +1 -1
- package/dist/ai.d.ts +1 -1
- package/dist/auth.cjs +365 -25
- package/dist/auth.cjs.map +1 -1
- package/dist/auth.d.cts +1 -1
- package/dist/auth.d.ts +1 -1
- package/dist/auth.mjs +365 -25
- package/dist/auth.mjs.map +1 -1
- package/dist/catalog.d.cts +1 -1
- package/dist/catalog.d.ts +1 -1
- package/dist/checkout.d.cts +1 -1
- package/dist/checkout.d.ts +1 -1
- package/dist/db-react.d.cts +1 -1
- package/dist/db-react.d.ts +1 -1
- package/dist/db.cjs +202 -22
- package/dist/db.cjs.map +1 -1
- package/dist/db.d.cts +1 -1
- package/dist/db.d.ts +1 -1
- package/dist/db.mjs +202 -23
- package/dist/db.mjs.map +1 -1
- package/dist/entitlements.d.cts +1 -1
- package/dist/entitlements.d.ts +1 -1
- package/dist/firestore-compat.d.cts +1 -1
- package/dist/firestore-compat.d.ts +1 -1
- package/dist/index.cjs +366 -26
- 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 +366 -26
- package/dist/index.mjs.map +1 -1
- package/dist/mocks.cjs +68 -1
- package/dist/mocks.cjs.map +1 -1
- package/dist/mocks.d.cts +17 -1
- package/dist/mocks.d.ts +17 -1
- package/dist/mocks.mjs +68 -1
- package/dist/mocks.mjs.map +1 -1
- package/dist/notifications.d.cts +1 -1
- package/dist/notifications.d.ts +1 -1
- package/dist/react.d.cts +1 -1
- package/dist/react.d.ts +1 -1
- package/dist/support.cjs +98 -1
- package/dist/support.cjs.map +1 -1
- package/dist/support.d.cts +1 -1
- package/dist/support.d.ts +1 -1
- package/dist/support.mjs +98 -1
- package/dist/support.mjs.map +1 -1
- package/dist/telemetry.d.cts +1 -1
- package/dist/telemetry.d.ts +1 -1
- package/dist/{types-DslphKIM.d.ts → types-BlOLDMR0.d.ts} +192 -4
- package/dist/{types-DQplLXIk.d.cts → types-D-QdG502.d.cts} +192 -4
- package/dist/usage.d.cts +1 -1
- package/dist/usage.d.ts +1 -1
- package/dist/webhooks.d.cts +1 -1
- package/dist/webhooks.d.ts +1 -1
- package/package.json +1 -1
package/dist/catalog.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ag as ResolvedConfig, C as CatalogListOptions, n as CatalogListResponse, a8 as Product } from './types-
|
|
1
|
+
import { ag as ResolvedConfig, C as CatalogListOptions, n as CatalogListResponse, a8 as Product } from './types-D-QdG502.cjs';
|
|
2
2
|
import 'drizzle-orm/node-postgres';
|
|
3
3
|
import './errors.cjs';
|
|
4
4
|
|
package/dist/catalog.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ag as ResolvedConfig, C as CatalogListOptions, n as CatalogListResponse, a8 as Product } from './types-
|
|
1
|
+
import { ag as ResolvedConfig, C as CatalogListOptions, n as CatalogListResponse, a8 as Product } from './types-BlOLDMR0.js';
|
|
2
2
|
import 'drizzle-orm/node-postgres';
|
|
3
3
|
import './errors.js';
|
|
4
4
|
|
package/dist/checkout.d.cts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export { o as CheckoutAuthOpts, p as CheckoutIntentInfo, q as CheckoutIntentStatus, r as CheckoutNamespace, s as CheckoutStartInput, t as CheckoutStartResult, u as CheckoutTenantType, S as MockCheckout,
|
|
1
|
+
export { o as CheckoutAuthOpts, p as CheckoutIntentInfo, q as CheckoutIntentStatus, r as CheckoutNamespace, s as CheckoutStartInput, t as CheckoutStartResult, u as CheckoutTenantType, S as MockCheckout, aK as createCheckoutNamespace } from './types-D-QdG502.cjs';
|
|
2
2
|
import 'drizzle-orm/node-postgres';
|
|
3
3
|
import './errors.cjs';
|
package/dist/checkout.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export { o as CheckoutAuthOpts, p as CheckoutIntentInfo, q as CheckoutIntentStatus, r as CheckoutNamespace, s as CheckoutStartInput, t as CheckoutStartResult, u as CheckoutTenantType, S as MockCheckout,
|
|
1
|
+
export { o as CheckoutAuthOpts, p as CheckoutIntentInfo, q as CheckoutIntentStatus, r as CheckoutNamespace, s as CheckoutStartInput, t as CheckoutStartResult, u as CheckoutTenantType, S as MockCheckout, aK as createCheckoutNamespace } from './types-BlOLDMR0.js';
|
|
2
2
|
import 'drizzle-orm/node-postgres';
|
|
3
3
|
import './errors.js';
|
package/dist/db-react.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { aq as SyncState, av as Unsubscribe, F as DbDoc, W as NeetruClient, E as DbCollectionRef, K as DbQuery } from './types-D-QdG502.cjs';
|
|
2
2
|
import 'drizzle-orm/node-postgres';
|
|
3
3
|
import './errors.cjs';
|
|
4
4
|
|
package/dist/db-react.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { aq as SyncState, av as Unsubscribe, F as DbDoc, W as NeetruClient, E as DbCollectionRef, K as DbQuery } from './types-BlOLDMR0.js';
|
|
2
2
|
import 'drizzle-orm/node-postgres';
|
|
3
3
|
import './errors.js';
|
|
4
4
|
|
package/dist/db.cjs
CHANGED
|
@@ -1736,8 +1736,10 @@ var MutationQueue = class {
|
|
|
1736
1736
|
await this.store.deleteMutation(mutationId);
|
|
1737
1737
|
}
|
|
1738
1738
|
/**
|
|
1739
|
-
* Marca uma mutação como `failed` e registra o erro.
|
|
1740
|
-
* Incrementa `attempts`. Chamado pelo SyncEngine
|
|
1739
|
+
* Marca uma mutação como `failed` (dead-letter) e registra o erro.
|
|
1740
|
+
* Incrementa `attempts` e carimba `lastAttemptAt`. Chamado pelo SyncEngine
|
|
1741
|
+
* quando a mutação esgota o teto de tentativas (mutação-veneno): sai do
|
|
1742
|
+
* conjunto `queued`, então `drain()` para de devolvê-la e a fila DESTRAVA.
|
|
1741
1743
|
*/
|
|
1742
1744
|
async markFailed(mutationId, error) {
|
|
1743
1745
|
const mut = await this.store.getMutation(mutationId);
|
|
@@ -1746,12 +1748,14 @@ var MutationQueue = class {
|
|
|
1746
1748
|
...mut,
|
|
1747
1749
|
status: "failed",
|
|
1748
1750
|
lastError: error,
|
|
1749
|
-
attempts: mut.attempts + 1
|
|
1751
|
+
attempts: mut.attempts + 1,
|
|
1752
|
+
lastAttemptAt: Date.now()
|
|
1750
1753
|
});
|
|
1751
1754
|
}
|
|
1752
1755
|
/**
|
|
1753
|
-
* Incrementa `attempts` e volta para `queued`
|
|
1754
|
-
* Chamado pelo SyncEngine em falha
|
|
1756
|
+
* Incrementa `attempts`, carimba `lastAttemptAt` e volta para `queued`
|
|
1757
|
+
* (falha transiente com backoff). Chamado pelo SyncEngine em falha
|
|
1758
|
+
* transiente (5xx, timeout) da mutação que REALMENTE foi tentada.
|
|
1755
1759
|
*/
|
|
1756
1760
|
async markRetry(mutationId, error) {
|
|
1757
1761
|
const mut = await this.store.getMutation(mutationId);
|
|
@@ -1760,9 +1764,24 @@ var MutationQueue = class {
|
|
|
1760
1764
|
...mut,
|
|
1761
1765
|
status: "queued",
|
|
1762
1766
|
lastError: error,
|
|
1763
|
-
attempts: mut.attempts + 1
|
|
1767
|
+
attempts: mut.attempts + 1,
|
|
1768
|
+
lastAttemptAt: Date.now()
|
|
1764
1769
|
});
|
|
1765
1770
|
}
|
|
1771
|
+
/**
|
|
1772
|
+
* Reverte uma mutação `inflight` de volta a `queued` SEM tocar em `attempts`
|
|
1773
|
+
* nem `lastAttemptAt`. Usado para mutações que foram marcadas inflight mas
|
|
1774
|
+
* NUNCA chegaram a ser enviadas (estavam atrás da head no FIFO e o push
|
|
1775
|
+
* parou antes delas): contá-las como tentativa as empurraria pro dead-letter
|
|
1776
|
+
* por engano, e adiaria seu envio via backoff sem que tenham falhado.
|
|
1777
|
+
* No-op se a mutação não está mais `inflight`.
|
|
1778
|
+
*/
|
|
1779
|
+
async requeueInflight(mutationId) {
|
|
1780
|
+
const mut = await this.store.getMutation(mutationId);
|
|
1781
|
+
if (!mut) return;
|
|
1782
|
+
if (mut.status !== "inflight") return;
|
|
1783
|
+
await this.store.putMutation({ ...mut, status: "queued" });
|
|
1784
|
+
}
|
|
1766
1785
|
};
|
|
1767
1786
|
|
|
1768
1787
|
// src/db/field-value.ts
|
|
@@ -1818,6 +1837,10 @@ function hasFieldSentinel(data) {
|
|
|
1818
1837
|
}
|
|
1819
1838
|
|
|
1820
1839
|
// src/db/offline/sync-engine.ts
|
|
1840
|
+
var MAX_MUTATION_ATTEMPTS = 8;
|
|
1841
|
+
var BACKOFF_BASE_MS = 1e3;
|
|
1842
|
+
var BACKOFF_CAP_MS = 5 * 60 * 1e3;
|
|
1843
|
+
var TRUNCATED_COLLECTIONS_META_KEY = "truncatedCollections";
|
|
1821
1844
|
function hasSentinels(payload) {
|
|
1822
1845
|
return payload !== null && hasFieldSentinel(payload);
|
|
1823
1846
|
}
|
|
@@ -1852,6 +1875,8 @@ var SyncEngine = class {
|
|
|
1852
1875
|
_state;
|
|
1853
1876
|
/** Listeners de mudança de SyncState. */
|
|
1854
1877
|
_stateListeners = /* @__PURE__ */ new Set();
|
|
1878
|
+
/** Listeners de erro de ciclo de sync (P2-C — não mais engolido em silêncio). */
|
|
1879
|
+
_errorListeners = /* @__PURE__ */ new Set();
|
|
1855
1880
|
/** Cleanup functions dos listeners externos. */
|
|
1856
1881
|
_cleanups = [];
|
|
1857
1882
|
/** Timer de resync periódico. */
|
|
@@ -1910,6 +1935,19 @@ var SyncEngine = class {
|
|
|
1910
1935
|
this._stateListeners.delete(cb);
|
|
1911
1936
|
};
|
|
1912
1937
|
}
|
|
1938
|
+
/**
|
|
1939
|
+
* Registra um hook de erro de sync (P2-C). Disparado quando um ciclo falha
|
|
1940
|
+
* (exceção inesperada nas fases, falha de pull/resync ou dead-letter de uma
|
|
1941
|
+
* mutação-veneno) — o que antes era engolido com "log silencioso".
|
|
1942
|
+
* O mesmo diagnóstico também aparece em `SyncState.lastError`.
|
|
1943
|
+
* Retorna uma função de unsubscribe.
|
|
1944
|
+
*/
|
|
1945
|
+
onError(cb) {
|
|
1946
|
+
this._errorListeners.add(cb);
|
|
1947
|
+
return () => {
|
|
1948
|
+
this._errorListeners.delete(cb);
|
|
1949
|
+
};
|
|
1950
|
+
}
|
|
1913
1951
|
/**
|
|
1914
1952
|
* Força um sync imediato (I3 §10.3 `flush()`).
|
|
1915
1953
|
* Resolve quando a fila esvazia OU rejeita se o engine estiver destruído.
|
|
@@ -1942,8 +1980,10 @@ var SyncEngine = class {
|
|
|
1942
1980
|
}
|
|
1943
1981
|
await this._phase2Pull();
|
|
1944
1982
|
this._phase3Realtime();
|
|
1945
|
-
this._updateState({ status: "idle", lastSyncedAt: Date.now() });
|
|
1983
|
+
this._updateState({ status: "idle", lastSyncedAt: Date.now(), lastError: null });
|
|
1946
1984
|
} catch (err) {
|
|
1985
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
1986
|
+
this._recordSyncError("cycle", `ciclo de sync falhou: ${msg}`);
|
|
1947
1987
|
this._updateState({ status: "idle" });
|
|
1948
1988
|
} finally {
|
|
1949
1989
|
this._syncing = false;
|
|
@@ -1978,6 +2018,9 @@ var SyncEngine = class {
|
|
|
1978
2018
|
async _phase1Push() {
|
|
1979
2019
|
const mutations = await this._queue.drain();
|
|
1980
2020
|
if (mutations.length === 0) return true;
|
|
2021
|
+
if (this._isInBackoff(mutations[0])) {
|
|
2022
|
+
return true;
|
|
2023
|
+
}
|
|
1981
2024
|
for (const mut of mutations) {
|
|
1982
2025
|
await this._queue.markInflight(mut.mutationId);
|
|
1983
2026
|
}
|
|
@@ -1985,10 +2028,16 @@ var SyncEngine = class {
|
|
|
1985
2028
|
try {
|
|
1986
2029
|
result = await this._transport.pushMutations(mutations);
|
|
1987
2030
|
} catch (err) {
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
2031
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
2032
|
+
const busChangesOnThrow = [];
|
|
2033
|
+
const [head, ...behind] = mutations;
|
|
2034
|
+
await this._retryOrDeadLetter(head, errMsg, busChangesOnThrow);
|
|
2035
|
+
for (const mut of behind) {
|
|
2036
|
+
await this._queue.requeueInflight(mut.mutationId);
|
|
1991
2037
|
}
|
|
2038
|
+
if (busChangesOnThrow.length > 0) this._bus.emit(busChangesOnThrow);
|
|
2039
|
+
this._refreshPendingWrites().catch(() => {
|
|
2040
|
+
});
|
|
1992
2041
|
return false;
|
|
1993
2042
|
}
|
|
1994
2043
|
const busChanges = [];
|
|
@@ -2015,8 +2064,16 @@ var SyncEngine = class {
|
|
|
2015
2064
|
}
|
|
2016
2065
|
const processedIds = new Set(result.results.map((r) => r.mutationId));
|
|
2017
2066
|
const unprocessed = mutations.filter((m) => !processedIds.has(m.mutationId));
|
|
2018
|
-
|
|
2019
|
-
|
|
2067
|
+
if (unprocessed.length > 0) {
|
|
2068
|
+
const [boundary, ...behind] = unprocessed;
|
|
2069
|
+
await this._retryOrDeadLetter(
|
|
2070
|
+
boundary,
|
|
2071
|
+
"partial_push: muta\xE7\xE3o n\xE3o processada no lote",
|
|
2072
|
+
busChanges
|
|
2073
|
+
);
|
|
2074
|
+
for (const mut of behind) {
|
|
2075
|
+
await this._queue.requeueInflight(mut.mutationId);
|
|
2076
|
+
}
|
|
2020
2077
|
}
|
|
2021
2078
|
if (busChanges.length > 0) {
|
|
2022
2079
|
this._bus.emit(busChanges);
|
|
@@ -2208,6 +2265,67 @@ var SyncEngine = class {
|
|
|
2208
2265
|
});
|
|
2209
2266
|
}
|
|
2210
2267
|
}
|
|
2268
|
+
// ─── Dead-letter + backoff (P1-C) ────────────────────────────────────────────
|
|
2269
|
+
/**
|
|
2270
|
+
* Trata a falha transiente de UMA mutação que foi realmente tentada.
|
|
2271
|
+
*
|
|
2272
|
+
* - Se ainda há orçamento de tentativas → `markRetry` (volta pra fila com
|
|
2273
|
+
* backoff, `attempts++`).
|
|
2274
|
+
* - Se esgotou (`attempts+1 >= MAX_MUTATION_ATTEMPTS`) → DEAD-LETTER:
|
|
2275
|
+
* `markFailed` (sai do conjunto `queued`, então `drain()` para de devolvê-la
|
|
2276
|
+
* e a fila FIFO DESTRAVA para as demais coleções) + grava um ConflictRecord
|
|
2277
|
+
* `failed_max_attempts` (o produto vê via `getConflicts()`) + limpa o estado
|
|
2278
|
+
* pending do doc no cache + registra `lastError`/dispara `onError`.
|
|
2279
|
+
*/
|
|
2280
|
+
async _retryOrDeadLetter(mut, errMsg, busChanges) {
|
|
2281
|
+
if (mut.attempts + 1 < MAX_MUTATION_ATTEMPTS) {
|
|
2282
|
+
await this._queue.markRetry(mut.mutationId, errMsg);
|
|
2283
|
+
return;
|
|
2284
|
+
}
|
|
2285
|
+
await this._queue.markFailed(mut.mutationId, errMsg);
|
|
2286
|
+
const conflictRecord = {
|
|
2287
|
+
collection: mut.collection,
|
|
2288
|
+
docId: mut.docId,
|
|
2289
|
+
mutationId: mut.mutationId,
|
|
2290
|
+
losingData: mut.payload ?? {},
|
|
2291
|
+
winningData: {},
|
|
2292
|
+
reason: "failed_max_attempts",
|
|
2293
|
+
detectedAt: Date.now(),
|
|
2294
|
+
delivered: false
|
|
2295
|
+
};
|
|
2296
|
+
await this._store.appendConflict(conflictRecord);
|
|
2297
|
+
const existing = await this._store.getDoc(mut.collection, mut.docId);
|
|
2298
|
+
if (existing) {
|
|
2299
|
+
await this._store.putDoc({
|
|
2300
|
+
...existing,
|
|
2301
|
+
meta: { ...existing.meta, state: "synced", pendingMutationIds: [] }
|
|
2302
|
+
});
|
|
2303
|
+
busChanges.push({
|
|
2304
|
+
type: "modified",
|
|
2305
|
+
collection: mut.collection,
|
|
2306
|
+
doc: { id: mut.docId, data: existing.data }
|
|
2307
|
+
});
|
|
2308
|
+
}
|
|
2309
|
+
this._recordSyncError(
|
|
2310
|
+
"push",
|
|
2311
|
+
`muta\xE7\xE3o ${mut.mutationId} (${mut.collection}/${mut.docId}) descartada ap\xF3s ${mut.attempts + 1} tentativas: ${errMsg}`
|
|
2312
|
+
);
|
|
2313
|
+
}
|
|
2314
|
+
/**
|
|
2315
|
+
* `true` se a mutação (head do FIFO) ainda está dentro da janela de backoff
|
|
2316
|
+
* desde a última tentativa falha. Só se aplica a mutações já tentadas
|
|
2317
|
+
* (`attempts > 0` e `lastAttemptAt` carimbado).
|
|
2318
|
+
*/
|
|
2319
|
+
_isInBackoff(mut) {
|
|
2320
|
+
if (mut.attempts <= 0) return false;
|
|
2321
|
+
const last = mut.lastAttemptAt;
|
|
2322
|
+
if (typeof last !== "number") return false;
|
|
2323
|
+
return Date.now() < last + this._backoffDelayMs(mut.attempts);
|
|
2324
|
+
}
|
|
2325
|
+
/** Backoff exponencial keyed em `attempts`, com teto `BACKOFF_CAP_MS`. */
|
|
2326
|
+
_backoffDelayMs(attempts) {
|
|
2327
|
+
return Math.min(BACKOFF_BASE_MS * 2 ** (attempts - 1), BACKOFF_CAP_MS);
|
|
2328
|
+
}
|
|
2211
2329
|
// ─── FASE 2 — pull (reconciliar o cache) ─────────────────────────────────────
|
|
2212
2330
|
/**
|
|
2213
2331
|
* Busca mudanças do servidor e aplica ao cache via ConflictResolver.
|
|
@@ -2247,10 +2365,14 @@ var SyncEngine = class {
|
|
|
2247
2365
|
}
|
|
2248
2366
|
const busChanges = [];
|
|
2249
2367
|
await this._applyServerDocs(resyncResult.docs, busChanges, true);
|
|
2368
|
+
const truncatedCollections = resyncResult.truncatedCollections ?? [];
|
|
2369
|
+
const truncatedSet = new Set(truncatedCollections);
|
|
2370
|
+
await this._store.setMeta(TRUNCATED_COLLECTIONS_META_KEY, truncatedCollections);
|
|
2250
2371
|
const returnedKeys = new Set(
|
|
2251
2372
|
resyncResult.docs.map((d) => `${d.collection}::${d.id}`)
|
|
2252
2373
|
);
|
|
2253
2374
|
for (const col of collections) {
|
|
2375
|
+
if (truncatedSet.has(col)) continue;
|
|
2254
2376
|
const queryResult = await this._store.listDocs(col, {});
|
|
2255
2377
|
for (const { id } of queryResult.docs) {
|
|
2256
2378
|
const key = `${col}::${id}`;
|
|
@@ -2410,7 +2532,8 @@ var SyncEngine = class {
|
|
|
2410
2532
|
status: isOnline ? "idle" : "offline",
|
|
2411
2533
|
pendingWrites: 0,
|
|
2412
2534
|
lastSyncedAt: null,
|
|
2413
|
-
isLeaderTab: this._tabCoordinator.isLeader()
|
|
2535
|
+
isLeaderTab: this._tabCoordinator.isLeader(),
|
|
2536
|
+
lastError: null
|
|
2414
2537
|
};
|
|
2415
2538
|
}
|
|
2416
2539
|
/**
|
|
@@ -2420,7 +2543,7 @@ var SyncEngine = class {
|
|
|
2420
2543
|
_updateState(partial) {
|
|
2421
2544
|
const prev = this._state;
|
|
2422
2545
|
const next = { ...prev, ...partial };
|
|
2423
|
-
const changed = prev.status !== next.status || prev.pendingWrites !== next.pendingWrites || prev.lastSyncedAt !== next.lastSyncedAt || prev.isLeaderTab !== next.isLeaderTab;
|
|
2546
|
+
const changed = prev.status !== next.status || prev.pendingWrites !== next.pendingWrites || prev.lastSyncedAt !== next.lastSyncedAt || prev.isLeaderTab !== next.isLeaderTab || (prev.lastError ?? null) !== (next.lastError ?? null);
|
|
2424
2547
|
this._state = next;
|
|
2425
2548
|
if (changed) {
|
|
2426
2549
|
this._emitState(next);
|
|
@@ -2434,6 +2557,21 @@ var SyncEngine = class {
|
|
|
2434
2557
|
}
|
|
2435
2558
|
}
|
|
2436
2559
|
}
|
|
2560
|
+
/**
|
|
2561
|
+
* P2-C: registra a falha de um ciclo de sync — antes engolida em silêncio.
|
|
2562
|
+
* Publica em `SyncState.lastError` (visível via `onSyncStateChanged`) e
|
|
2563
|
+
* dispara o hook `onError`. Nunca lança (diagnóstico é best-effort).
|
|
2564
|
+
*/
|
|
2565
|
+
_recordSyncError(phase, message) {
|
|
2566
|
+
this._updateState({ lastError: message });
|
|
2567
|
+
const event = { phase, message, at: Date.now() };
|
|
2568
|
+
for (const listener of this._errorListeners) {
|
|
2569
|
+
try {
|
|
2570
|
+
listener({ ...event });
|
|
2571
|
+
} catch {
|
|
2572
|
+
}
|
|
2573
|
+
}
|
|
2574
|
+
}
|
|
2437
2575
|
/**
|
|
2438
2576
|
* Atualiza `pendingWrites` no estado.
|
|
2439
2577
|
* Chamado de forma assíncrona quando necessário.
|
|
@@ -3243,7 +3381,9 @@ var DbCollectionRefImpl = class {
|
|
|
3243
3381
|
const isOnline = this._isOnline();
|
|
3244
3382
|
const syncState = this._engine.getSyncState();
|
|
3245
3383
|
const nextCursor = buildNextCursor(result.docs, effectiveLimit);
|
|
3246
|
-
const
|
|
3384
|
+
const truncatedRaw = await this._store.getMeta(TRUNCATED_COLLECTIONS_META_KEY);
|
|
3385
|
+
const truncated = Array.isArray(truncatedRaw) && truncatedRaw.includes(this._collection);
|
|
3386
|
+
const stale = !isOnline || syncState.status === "offline" || truncated;
|
|
3247
3387
|
const fromCache = hasPending || syncState.lastSyncedAt === null || stale;
|
|
3248
3388
|
return {
|
|
3249
3389
|
docs: result.docs.map((d) => ({ id: d.id, data: d.data })),
|
|
@@ -3251,7 +3391,8 @@ var DbCollectionRefImpl = class {
|
|
|
3251
3391
|
fromCache,
|
|
3252
3392
|
stale,
|
|
3253
3393
|
hasPendingWrites: hasPending,
|
|
3254
|
-
changes: []
|
|
3394
|
+
changes: [],
|
|
3395
|
+
truncated
|
|
3255
3396
|
};
|
|
3256
3397
|
}
|
|
3257
3398
|
// ── CRUD ────────────────────────────────────────────────────────────────────
|
|
@@ -3697,6 +3838,9 @@ var NeetruDbDocumentsImpl = class {
|
|
|
3697
3838
|
async getConflicts() {
|
|
3698
3839
|
return this._store.listConflicts();
|
|
3699
3840
|
}
|
|
3841
|
+
onError(cb) {
|
|
3842
|
+
return this._engine.onError(cb);
|
|
3843
|
+
}
|
|
3700
3844
|
};
|
|
3701
3845
|
async function createOfflineDocumentsNamespace(opts) {
|
|
3702
3846
|
const store = isIndexedDBUnavailable() ? new MemoryStore() : new LocalStore(opts.dbName);
|
|
@@ -4173,6 +4317,7 @@ var defaultWebSocketFactory = (url) => {
|
|
|
4173
4317
|
init_db_errors();
|
|
4174
4318
|
var DATASTORE_COLLECTION_ENDPOINT = (collection) => `/api/sdk/v1/datastore/${encodeURIComponent(collection)}`;
|
|
4175
4319
|
var DATASTORE_DOC_ENDPOINT = (collection, docId) => `/api/sdk/v1/datastore/${encodeURIComponent(collection)}/${encodeURIComponent(docId)}`;
|
|
4320
|
+
var DOCUMENTS_RESYNC_PAGE_CAP = 200;
|
|
4176
4321
|
function resolveTransport(transport, opts, config) {
|
|
4177
4322
|
if (opts._transport) {
|
|
4178
4323
|
return opts._transport;
|
|
@@ -4285,6 +4430,7 @@ function createRestSyncTransport(config) {
|
|
|
4285
4430
|
const tenantHeaders = tenantScopeId ? { "x-neetru-tenant": tenantScopeId } : void 0;
|
|
4286
4431
|
const now = Date.now();
|
|
4287
4432
|
const docs = [];
|
|
4433
|
+
const truncatedCollections = [];
|
|
4288
4434
|
for (const collection of collections) {
|
|
4289
4435
|
let raw;
|
|
4290
4436
|
try {
|
|
@@ -4294,11 +4440,11 @@ function createRestSyncTransport(config) {
|
|
|
4294
4440
|
requireAuth: true,
|
|
4295
4441
|
retries: 2,
|
|
4296
4442
|
headers: tenantHeaders,
|
|
4297
|
-
// Teto do Core
|
|
4298
|
-
//
|
|
4299
|
-
// deleção-por-ausência
|
|
4300
|
-
//
|
|
4301
|
-
query: { limit:
|
|
4443
|
+
// Teto do Core (DOCUMENTS_RESYNC_PAGE_CAP). Coleções acima dele vêm
|
|
4444
|
+
// TRUNCADAS — reportadas em `truncatedCollections` p/ o SyncEngine
|
|
4445
|
+
// (a) NÃO rodar deleção-por-ausência e (b) sinalizar
|
|
4446
|
+
// `DbListResult.truncated`. Paginação por cursor é tarefa Core (gated).
|
|
4447
|
+
query: { limit: DOCUMENTS_RESYNC_PAGE_CAP }
|
|
4302
4448
|
});
|
|
4303
4449
|
} catch (err) {
|
|
4304
4450
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -4309,6 +4455,9 @@ function createRestSyncTransport(config) {
|
|
|
4309
4455
|
}
|
|
4310
4456
|
const resp = raw;
|
|
4311
4457
|
const items = Array.isArray(resp?.items) ? resp.items : [];
|
|
4458
|
+
if (items.length >= DOCUMENTS_RESYNC_PAGE_CAP) {
|
|
4459
|
+
truncatedCollections.push(collection);
|
|
4460
|
+
}
|
|
4312
4461
|
for (const item of items) {
|
|
4313
4462
|
if (!item || typeof item !== "object") continue;
|
|
4314
4463
|
const record = item;
|
|
@@ -4336,7 +4485,7 @@ function createRestSyncTransport(config) {
|
|
|
4336
4485
|
});
|
|
4337
4486
|
}
|
|
4338
4487
|
}
|
|
4339
|
-
return { docs, newWatermark: now };
|
|
4488
|
+
return { docs, newWatermark: now, truncatedCollections };
|
|
4340
4489
|
}
|
|
4341
4490
|
};
|
|
4342
4491
|
}
|
|
@@ -4401,7 +4550,23 @@ function buildTicketProvider(config, opts) {
|
|
|
4401
4550
|
function getWebSocketRealtimeClient(gatewayUrl, ticketProvider, dbId, wsFactory) {
|
|
4402
4551
|
return new NeetruRealtimeClient({ gatewayUrl, ticketProvider, dbId, webSocketFactory: wsFactory });
|
|
4403
4552
|
}
|
|
4553
|
+
var _hasWarnedDbIdIgnored = false;
|
|
4554
|
+
function __resetDbIdWarningForTests() {
|
|
4555
|
+
_hasWarnedDbIdIgnored = false;
|
|
4556
|
+
}
|
|
4557
|
+
function warnDbIdIgnoredOnce(config, dbOpts) {
|
|
4558
|
+
if (_hasWarnedDbIdIgnored) return;
|
|
4559
|
+
if (!dbOpts.dbId) return;
|
|
4560
|
+
if (config.env === "prod") return;
|
|
4561
|
+
if (typeof window === "undefined") return;
|
|
4562
|
+
if (typeof console === "undefined" || typeof console.warn !== "function") return;
|
|
4563
|
+
_hasWarnedDbIdIgnored = true;
|
|
4564
|
+
console.warn(
|
|
4565
|
+
'[neetru/sdk] client.db recebeu `dbId` ("' + dbOpts.dbId + '") mas ele N\xC3O roteia nem escopa nada no servidor: as escritas do mundo Documentos (add/set/update/remove/list) SEMPRE caem no datastore gen\xE9rico do Core, escopado s\xF3 por tenant/produto (header x-neetru-tenant). `dbId` apenas nomeia o cache offline LOCAL (IndexedDB). ATEN\xC7\xC3O: usar dbIds distintos no MESMO tenant/produto N\xC3O isola os dados no servidor \u2014 eles se misturam (co-mingling). Veja o JSDoc de NeetruDbOptions.dbId.'
|
|
4566
|
+
);
|
|
4567
|
+
}
|
|
4404
4568
|
function createNeetruDb(config, dbOpts = {}) {
|
|
4569
|
+
warnDbIdIgnoredOnce(config, dbOpts);
|
|
4405
4570
|
let resolvedTransport;
|
|
4406
4571
|
if (dbOpts.transport !== void 0) {
|
|
4407
4572
|
resolvedTransport = dbOpts.transport;
|
|
@@ -4479,6 +4644,20 @@ function createNeetruDb(config, dbOpts = {}) {
|
|
|
4479
4644
|
async getConflicts() {
|
|
4480
4645
|
const docs = await getDocsPromise();
|
|
4481
4646
|
return docs.getConflicts();
|
|
4647
|
+
},
|
|
4648
|
+
onError(cb) {
|
|
4649
|
+
if (_docsResolved) return _docsResolved.onError(cb);
|
|
4650
|
+
let unsub = null;
|
|
4651
|
+
let cancelled = false;
|
|
4652
|
+
getDocsPromise().then((docs) => {
|
|
4653
|
+
if (!cancelled) {
|
|
4654
|
+
unsub = docs.onError(cb);
|
|
4655
|
+
}
|
|
4656
|
+
});
|
|
4657
|
+
return () => {
|
|
4658
|
+
cancelled = true;
|
|
4659
|
+
unsub?.();
|
|
4660
|
+
};
|
|
4482
4661
|
}
|
|
4483
4662
|
};
|
|
4484
4663
|
}
|
|
@@ -4577,6 +4756,7 @@ function createLazyCollectionRef(name, getDocsPromise) {
|
|
|
4577
4756
|
}
|
|
4578
4757
|
|
|
4579
4758
|
exports.NEETRU_SENTINEL_KEY = NEETRU_SENTINEL_KEY;
|
|
4759
|
+
exports.__resetDbIdWarningForTests = __resetDbIdWarningForTests;
|
|
4580
4760
|
exports.createNeetruDb = createNeetruDb;
|
|
4581
4761
|
exports.createRestSyncTransport = createRestSyncTransport;
|
|
4582
4762
|
exports.getWebSocketRealtimeClient = getWebSocketRealtimeClient;
|