@neetru/sdk 3.4.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 +18 -0
- package/dist/ai.d.cts +1 -1
- package/dist/ai.d.ts +1 -1
- package/dist/auth.cjs +193 -30
- 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 +193 -30
- 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 +193 -30
- 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 +193 -30
- 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 +194 -31
- 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 +194 -31
- package/dist/index.mjs.map +1 -1
- package/dist/mocks.d.cts +1 -1
- package/dist/mocks.d.ts +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.d.cts +1 -1
- package/dist/support.d.ts +1 -1
- package/dist/telemetry.d.cts +1 -1
- package/dist/telemetry.d.ts +1 -1
- package/dist/{types-DdJyqBy-.d.ts → types-BlOLDMR0.d.ts} +79 -6
- package/dist/{types-BoFSCHF_.d.cts → types-D-QdG502.d.cts} +79 -6
- 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/auth.d.cts
CHANGED
package/dist/auth.d.ts
CHANGED
package/dist/auth.mjs
CHANGED
|
@@ -2541,8 +2541,10 @@ var MutationQueue = class {
|
|
|
2541
2541
|
await this.store.deleteMutation(mutationId);
|
|
2542
2542
|
}
|
|
2543
2543
|
/**
|
|
2544
|
-
* Marca uma mutação como `failed` e registra o erro.
|
|
2545
|
-
* Incrementa `attempts`. Chamado pelo SyncEngine
|
|
2544
|
+
* Marca uma mutação como `failed` (dead-letter) e registra o erro.
|
|
2545
|
+
* Incrementa `attempts` e carimba `lastAttemptAt`. Chamado pelo SyncEngine
|
|
2546
|
+
* quando a mutação esgota o teto de tentativas (mutação-veneno): sai do
|
|
2547
|
+
* conjunto `queued`, então `drain()` para de devolvê-la e a fila DESTRAVA.
|
|
2546
2548
|
*/
|
|
2547
2549
|
async markFailed(mutationId, error) {
|
|
2548
2550
|
const mut = await this.store.getMutation(mutationId);
|
|
@@ -2551,12 +2553,14 @@ var MutationQueue = class {
|
|
|
2551
2553
|
...mut,
|
|
2552
2554
|
status: "failed",
|
|
2553
2555
|
lastError: error,
|
|
2554
|
-
attempts: mut.attempts + 1
|
|
2556
|
+
attempts: mut.attempts + 1,
|
|
2557
|
+
lastAttemptAt: Date.now()
|
|
2555
2558
|
});
|
|
2556
2559
|
}
|
|
2557
2560
|
/**
|
|
2558
|
-
* Incrementa `attempts` e volta para `queued`
|
|
2559
|
-
* Chamado pelo SyncEngine em falha
|
|
2561
|
+
* Incrementa `attempts`, carimba `lastAttemptAt` e volta para `queued`
|
|
2562
|
+
* (falha transiente com backoff). Chamado pelo SyncEngine em falha
|
|
2563
|
+
* transiente (5xx, timeout) da mutação que REALMENTE foi tentada.
|
|
2560
2564
|
*/
|
|
2561
2565
|
async markRetry(mutationId, error) {
|
|
2562
2566
|
const mut = await this.store.getMutation(mutationId);
|
|
@@ -2565,9 +2569,24 @@ var MutationQueue = class {
|
|
|
2565
2569
|
...mut,
|
|
2566
2570
|
status: "queued",
|
|
2567
2571
|
lastError: error,
|
|
2568
|
-
attempts: mut.attempts + 1
|
|
2572
|
+
attempts: mut.attempts + 1,
|
|
2573
|
+
lastAttemptAt: Date.now()
|
|
2569
2574
|
});
|
|
2570
2575
|
}
|
|
2576
|
+
/**
|
|
2577
|
+
* Reverte uma mutação `inflight` de volta a `queued` SEM tocar em `attempts`
|
|
2578
|
+
* nem `lastAttemptAt`. Usado para mutações que foram marcadas inflight mas
|
|
2579
|
+
* NUNCA chegaram a ser enviadas (estavam atrás da head no FIFO e o push
|
|
2580
|
+
* parou antes delas): contá-las como tentativa as empurraria pro dead-letter
|
|
2581
|
+
* por engano, e adiaria seu envio via backoff sem que tenham falhado.
|
|
2582
|
+
* No-op se a mutação não está mais `inflight`.
|
|
2583
|
+
*/
|
|
2584
|
+
async requeueInflight(mutationId) {
|
|
2585
|
+
const mut = await this.store.getMutation(mutationId);
|
|
2586
|
+
if (!mut) return;
|
|
2587
|
+
if (mut.status !== "inflight") return;
|
|
2588
|
+
await this.store.putMutation({ ...mut, status: "queued" });
|
|
2589
|
+
}
|
|
2571
2590
|
};
|
|
2572
2591
|
|
|
2573
2592
|
// src/db/field-value.ts
|
|
@@ -2612,6 +2631,10 @@ function hasFieldSentinel(data) {
|
|
|
2612
2631
|
}
|
|
2613
2632
|
|
|
2614
2633
|
// src/db/offline/sync-engine.ts
|
|
2634
|
+
var MAX_MUTATION_ATTEMPTS = 8;
|
|
2635
|
+
var BACKOFF_BASE_MS = 1e3;
|
|
2636
|
+
var BACKOFF_CAP_MS = 5 * 60 * 1e3;
|
|
2637
|
+
var TRUNCATED_COLLECTIONS_META_KEY = "truncatedCollections";
|
|
2615
2638
|
function hasSentinels(payload) {
|
|
2616
2639
|
return payload !== null && hasFieldSentinel(payload);
|
|
2617
2640
|
}
|
|
@@ -2646,6 +2669,8 @@ var SyncEngine = class {
|
|
|
2646
2669
|
_state;
|
|
2647
2670
|
/** Listeners de mudança de SyncState. */
|
|
2648
2671
|
_stateListeners = /* @__PURE__ */ new Set();
|
|
2672
|
+
/** Listeners de erro de ciclo de sync (P2-C — não mais engolido em silêncio). */
|
|
2673
|
+
_errorListeners = /* @__PURE__ */ new Set();
|
|
2649
2674
|
/** Cleanup functions dos listeners externos. */
|
|
2650
2675
|
_cleanups = [];
|
|
2651
2676
|
/** Timer de resync periódico. */
|
|
@@ -2704,6 +2729,19 @@ var SyncEngine = class {
|
|
|
2704
2729
|
this._stateListeners.delete(cb);
|
|
2705
2730
|
};
|
|
2706
2731
|
}
|
|
2732
|
+
/**
|
|
2733
|
+
* Registra um hook de erro de sync (P2-C). Disparado quando um ciclo falha
|
|
2734
|
+
* (exceção inesperada nas fases, falha de pull/resync ou dead-letter de uma
|
|
2735
|
+
* mutação-veneno) — o que antes era engolido com "log silencioso".
|
|
2736
|
+
* O mesmo diagnóstico também aparece em `SyncState.lastError`.
|
|
2737
|
+
* Retorna uma função de unsubscribe.
|
|
2738
|
+
*/
|
|
2739
|
+
onError(cb) {
|
|
2740
|
+
this._errorListeners.add(cb);
|
|
2741
|
+
return () => {
|
|
2742
|
+
this._errorListeners.delete(cb);
|
|
2743
|
+
};
|
|
2744
|
+
}
|
|
2707
2745
|
/**
|
|
2708
2746
|
* Força um sync imediato (I3 §10.3 `flush()`).
|
|
2709
2747
|
* Resolve quando a fila esvazia OU rejeita se o engine estiver destruído.
|
|
@@ -2736,8 +2774,10 @@ var SyncEngine = class {
|
|
|
2736
2774
|
}
|
|
2737
2775
|
await this._phase2Pull();
|
|
2738
2776
|
this._phase3Realtime();
|
|
2739
|
-
this._updateState({ status: "idle", lastSyncedAt: Date.now() });
|
|
2777
|
+
this._updateState({ status: "idle", lastSyncedAt: Date.now(), lastError: null });
|
|
2740
2778
|
} catch (err) {
|
|
2779
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
2780
|
+
this._recordSyncError("cycle", `ciclo de sync falhou: ${msg}`);
|
|
2741
2781
|
this._updateState({ status: "idle" });
|
|
2742
2782
|
} finally {
|
|
2743
2783
|
this._syncing = false;
|
|
@@ -2772,6 +2812,9 @@ var SyncEngine = class {
|
|
|
2772
2812
|
async _phase1Push() {
|
|
2773
2813
|
const mutations = await this._queue.drain();
|
|
2774
2814
|
if (mutations.length === 0) return true;
|
|
2815
|
+
if (this._isInBackoff(mutations[0])) {
|
|
2816
|
+
return true;
|
|
2817
|
+
}
|
|
2775
2818
|
for (const mut of mutations) {
|
|
2776
2819
|
await this._queue.markInflight(mut.mutationId);
|
|
2777
2820
|
}
|
|
@@ -2779,10 +2822,16 @@ var SyncEngine = class {
|
|
|
2779
2822
|
try {
|
|
2780
2823
|
result = await this._transport.pushMutations(mutations);
|
|
2781
2824
|
} catch (err) {
|
|
2782
|
-
|
|
2783
|
-
|
|
2784
|
-
|
|
2785
|
-
|
|
2825
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
2826
|
+
const busChangesOnThrow = [];
|
|
2827
|
+
const [head, ...behind] = mutations;
|
|
2828
|
+
await this._retryOrDeadLetter(head, errMsg, busChangesOnThrow);
|
|
2829
|
+
for (const mut of behind) {
|
|
2830
|
+
await this._queue.requeueInflight(mut.mutationId);
|
|
2831
|
+
}
|
|
2832
|
+
if (busChangesOnThrow.length > 0) this._bus.emit(busChangesOnThrow);
|
|
2833
|
+
this._refreshPendingWrites().catch(() => {
|
|
2834
|
+
});
|
|
2786
2835
|
return false;
|
|
2787
2836
|
}
|
|
2788
2837
|
const busChanges = [];
|
|
@@ -2809,8 +2858,16 @@ var SyncEngine = class {
|
|
|
2809
2858
|
}
|
|
2810
2859
|
const processedIds = new Set(result.results.map((r) => r.mutationId));
|
|
2811
2860
|
const unprocessed = mutations.filter((m) => !processedIds.has(m.mutationId));
|
|
2812
|
-
|
|
2813
|
-
|
|
2861
|
+
if (unprocessed.length > 0) {
|
|
2862
|
+
const [boundary, ...behind] = unprocessed;
|
|
2863
|
+
await this._retryOrDeadLetter(
|
|
2864
|
+
boundary,
|
|
2865
|
+
"partial_push: muta\xE7\xE3o n\xE3o processada no lote",
|
|
2866
|
+
busChanges
|
|
2867
|
+
);
|
|
2868
|
+
for (const mut of behind) {
|
|
2869
|
+
await this._queue.requeueInflight(mut.mutationId);
|
|
2870
|
+
}
|
|
2814
2871
|
}
|
|
2815
2872
|
if (busChanges.length > 0) {
|
|
2816
2873
|
this._bus.emit(busChanges);
|
|
@@ -3002,6 +3059,67 @@ var SyncEngine = class {
|
|
|
3002
3059
|
});
|
|
3003
3060
|
}
|
|
3004
3061
|
}
|
|
3062
|
+
// ─── Dead-letter + backoff (P1-C) ────────────────────────────────────────────
|
|
3063
|
+
/**
|
|
3064
|
+
* Trata a falha transiente de UMA mutação que foi realmente tentada.
|
|
3065
|
+
*
|
|
3066
|
+
* - Se ainda há orçamento de tentativas → `markRetry` (volta pra fila com
|
|
3067
|
+
* backoff, `attempts++`).
|
|
3068
|
+
* - Se esgotou (`attempts+1 >= MAX_MUTATION_ATTEMPTS`) → DEAD-LETTER:
|
|
3069
|
+
* `markFailed` (sai do conjunto `queued`, então `drain()` para de devolvê-la
|
|
3070
|
+
* e a fila FIFO DESTRAVA para as demais coleções) + grava um ConflictRecord
|
|
3071
|
+
* `failed_max_attempts` (o produto vê via `getConflicts()`) + limpa o estado
|
|
3072
|
+
* pending do doc no cache + registra `lastError`/dispara `onError`.
|
|
3073
|
+
*/
|
|
3074
|
+
async _retryOrDeadLetter(mut, errMsg, busChanges) {
|
|
3075
|
+
if (mut.attempts + 1 < MAX_MUTATION_ATTEMPTS) {
|
|
3076
|
+
await this._queue.markRetry(mut.mutationId, errMsg);
|
|
3077
|
+
return;
|
|
3078
|
+
}
|
|
3079
|
+
await this._queue.markFailed(mut.mutationId, errMsg);
|
|
3080
|
+
const conflictRecord = {
|
|
3081
|
+
collection: mut.collection,
|
|
3082
|
+
docId: mut.docId,
|
|
3083
|
+
mutationId: mut.mutationId,
|
|
3084
|
+
losingData: mut.payload ?? {},
|
|
3085
|
+
winningData: {},
|
|
3086
|
+
reason: "failed_max_attempts",
|
|
3087
|
+
detectedAt: Date.now(),
|
|
3088
|
+
delivered: false
|
|
3089
|
+
};
|
|
3090
|
+
await this._store.appendConflict(conflictRecord);
|
|
3091
|
+
const existing = await this._store.getDoc(mut.collection, mut.docId);
|
|
3092
|
+
if (existing) {
|
|
3093
|
+
await this._store.putDoc({
|
|
3094
|
+
...existing,
|
|
3095
|
+
meta: { ...existing.meta, state: "synced", pendingMutationIds: [] }
|
|
3096
|
+
});
|
|
3097
|
+
busChanges.push({
|
|
3098
|
+
type: "modified",
|
|
3099
|
+
collection: mut.collection,
|
|
3100
|
+
doc: { id: mut.docId, data: existing.data }
|
|
3101
|
+
});
|
|
3102
|
+
}
|
|
3103
|
+
this._recordSyncError(
|
|
3104
|
+
"push",
|
|
3105
|
+
`muta\xE7\xE3o ${mut.mutationId} (${mut.collection}/${mut.docId}) descartada ap\xF3s ${mut.attempts + 1} tentativas: ${errMsg}`
|
|
3106
|
+
);
|
|
3107
|
+
}
|
|
3108
|
+
/**
|
|
3109
|
+
* `true` se a mutação (head do FIFO) ainda está dentro da janela de backoff
|
|
3110
|
+
* desde a última tentativa falha. Só se aplica a mutações já tentadas
|
|
3111
|
+
* (`attempts > 0` e `lastAttemptAt` carimbado).
|
|
3112
|
+
*/
|
|
3113
|
+
_isInBackoff(mut) {
|
|
3114
|
+
if (mut.attempts <= 0) return false;
|
|
3115
|
+
const last = mut.lastAttemptAt;
|
|
3116
|
+
if (typeof last !== "number") return false;
|
|
3117
|
+
return Date.now() < last + this._backoffDelayMs(mut.attempts);
|
|
3118
|
+
}
|
|
3119
|
+
/** Backoff exponencial keyed em `attempts`, com teto `BACKOFF_CAP_MS`. */
|
|
3120
|
+
_backoffDelayMs(attempts) {
|
|
3121
|
+
return Math.min(BACKOFF_BASE_MS * 2 ** (attempts - 1), BACKOFF_CAP_MS);
|
|
3122
|
+
}
|
|
3005
3123
|
// ─── FASE 2 — pull (reconciliar o cache) ─────────────────────────────────────
|
|
3006
3124
|
/**
|
|
3007
3125
|
* Busca mudanças do servidor e aplica ao cache via ConflictResolver.
|
|
@@ -3041,10 +3159,14 @@ var SyncEngine = class {
|
|
|
3041
3159
|
}
|
|
3042
3160
|
const busChanges = [];
|
|
3043
3161
|
await this._applyServerDocs(resyncResult.docs, busChanges, true);
|
|
3162
|
+
const truncatedCollections = resyncResult.truncatedCollections ?? [];
|
|
3163
|
+
const truncatedSet = new Set(truncatedCollections);
|
|
3164
|
+
await this._store.setMeta(TRUNCATED_COLLECTIONS_META_KEY, truncatedCollections);
|
|
3044
3165
|
const returnedKeys = new Set(
|
|
3045
3166
|
resyncResult.docs.map((d) => `${d.collection}::${d.id}`)
|
|
3046
3167
|
);
|
|
3047
3168
|
for (const col of collections) {
|
|
3169
|
+
if (truncatedSet.has(col)) continue;
|
|
3048
3170
|
const queryResult = await this._store.listDocs(col, {});
|
|
3049
3171
|
for (const { id } of queryResult.docs) {
|
|
3050
3172
|
const key = `${col}::${id}`;
|
|
@@ -3204,7 +3326,8 @@ var SyncEngine = class {
|
|
|
3204
3326
|
status: isOnline ? "idle" : "offline",
|
|
3205
3327
|
pendingWrites: 0,
|
|
3206
3328
|
lastSyncedAt: null,
|
|
3207
|
-
isLeaderTab: this._tabCoordinator.isLeader()
|
|
3329
|
+
isLeaderTab: this._tabCoordinator.isLeader(),
|
|
3330
|
+
lastError: null
|
|
3208
3331
|
};
|
|
3209
3332
|
}
|
|
3210
3333
|
/**
|
|
@@ -3214,7 +3337,7 @@ var SyncEngine = class {
|
|
|
3214
3337
|
_updateState(partial) {
|
|
3215
3338
|
const prev = this._state;
|
|
3216
3339
|
const next = { ...prev, ...partial };
|
|
3217
|
-
const changed = prev.status !== next.status || prev.pendingWrites !== next.pendingWrites || prev.lastSyncedAt !== next.lastSyncedAt || prev.isLeaderTab !== next.isLeaderTab;
|
|
3340
|
+
const changed = prev.status !== next.status || prev.pendingWrites !== next.pendingWrites || prev.lastSyncedAt !== next.lastSyncedAt || prev.isLeaderTab !== next.isLeaderTab || (prev.lastError ?? null) !== (next.lastError ?? null);
|
|
3218
3341
|
this._state = next;
|
|
3219
3342
|
if (changed) {
|
|
3220
3343
|
this._emitState(next);
|
|
@@ -3228,6 +3351,21 @@ var SyncEngine = class {
|
|
|
3228
3351
|
}
|
|
3229
3352
|
}
|
|
3230
3353
|
}
|
|
3354
|
+
/**
|
|
3355
|
+
* P2-C: registra a falha de um ciclo de sync — antes engolida em silêncio.
|
|
3356
|
+
* Publica em `SyncState.lastError` (visível via `onSyncStateChanged`) e
|
|
3357
|
+
* dispara o hook `onError`. Nunca lança (diagnóstico é best-effort).
|
|
3358
|
+
*/
|
|
3359
|
+
_recordSyncError(phase, message) {
|
|
3360
|
+
this._updateState({ lastError: message });
|
|
3361
|
+
const event = { phase, message, at: Date.now() };
|
|
3362
|
+
for (const listener of this._errorListeners) {
|
|
3363
|
+
try {
|
|
3364
|
+
listener({ ...event });
|
|
3365
|
+
} catch {
|
|
3366
|
+
}
|
|
3367
|
+
}
|
|
3368
|
+
}
|
|
3231
3369
|
/**
|
|
3232
3370
|
* Atualiza `pendingWrites` no estado.
|
|
3233
3371
|
* Chamado de forma assíncrona quando necessário.
|
|
@@ -4037,7 +4175,9 @@ var DbCollectionRefImpl = class {
|
|
|
4037
4175
|
const isOnline = this._isOnline();
|
|
4038
4176
|
const syncState = this._engine.getSyncState();
|
|
4039
4177
|
const nextCursor = buildNextCursor(result.docs, effectiveLimit);
|
|
4040
|
-
const
|
|
4178
|
+
const truncatedRaw = await this._store.getMeta(TRUNCATED_COLLECTIONS_META_KEY);
|
|
4179
|
+
const truncated = Array.isArray(truncatedRaw) && truncatedRaw.includes(this._collection);
|
|
4180
|
+
const stale = !isOnline || syncState.status === "offline" || truncated;
|
|
4041
4181
|
const fromCache = hasPending || syncState.lastSyncedAt === null || stale;
|
|
4042
4182
|
return {
|
|
4043
4183
|
docs: result.docs.map((d) => ({ id: d.id, data: d.data })),
|
|
@@ -4045,7 +4185,8 @@ var DbCollectionRefImpl = class {
|
|
|
4045
4185
|
fromCache,
|
|
4046
4186
|
stale,
|
|
4047
4187
|
hasPendingWrites: hasPending,
|
|
4048
|
-
changes: []
|
|
4188
|
+
changes: [],
|
|
4189
|
+
truncated
|
|
4049
4190
|
};
|
|
4050
4191
|
}
|
|
4051
4192
|
// ── CRUD ────────────────────────────────────────────────────────────────────
|
|
@@ -4491,6 +4632,9 @@ var NeetruDbDocumentsImpl = class {
|
|
|
4491
4632
|
async getConflicts() {
|
|
4492
4633
|
return this._store.listConflicts();
|
|
4493
4634
|
}
|
|
4635
|
+
onError(cb) {
|
|
4636
|
+
return this._engine.onError(cb);
|
|
4637
|
+
}
|
|
4494
4638
|
};
|
|
4495
4639
|
async function createOfflineDocumentsNamespace(opts) {
|
|
4496
4640
|
const store = isIndexedDBUnavailable() ? new MemoryStore() : new LocalStore(opts.dbName);
|
|
@@ -4966,6 +5110,7 @@ var defaultWebSocketFactory = (url) => {
|
|
|
4966
5110
|
// src/db/client-db.ts
|
|
4967
5111
|
var DATASTORE_COLLECTION_ENDPOINT = (collection) => `/api/sdk/v1/datastore/${encodeURIComponent(collection)}`;
|
|
4968
5112
|
var DATASTORE_DOC_ENDPOINT = (collection, docId) => `/api/sdk/v1/datastore/${encodeURIComponent(collection)}/${encodeURIComponent(docId)}`;
|
|
5113
|
+
var DOCUMENTS_RESYNC_PAGE_CAP = 200;
|
|
4969
5114
|
function resolveTransport(transport, opts, config) {
|
|
4970
5115
|
if (opts._transport) {
|
|
4971
5116
|
return opts._transport;
|
|
@@ -5078,6 +5223,7 @@ function createRestSyncTransport(config) {
|
|
|
5078
5223
|
const tenantHeaders = tenantScopeId ? { "x-neetru-tenant": tenantScopeId } : void 0;
|
|
5079
5224
|
const now = Date.now();
|
|
5080
5225
|
const docs = [];
|
|
5226
|
+
const truncatedCollections = [];
|
|
5081
5227
|
for (const collection of collections) {
|
|
5082
5228
|
let raw;
|
|
5083
5229
|
try {
|
|
@@ -5087,11 +5233,11 @@ function createRestSyncTransport(config) {
|
|
|
5087
5233
|
requireAuth: true,
|
|
5088
5234
|
retries: 2,
|
|
5089
5235
|
headers: tenantHeaders,
|
|
5090
|
-
// Teto do Core
|
|
5091
|
-
//
|
|
5092
|
-
// deleção-por-ausência
|
|
5093
|
-
//
|
|
5094
|
-
query: { limit:
|
|
5236
|
+
// Teto do Core (DOCUMENTS_RESYNC_PAGE_CAP). Coleções acima dele vêm
|
|
5237
|
+
// TRUNCADAS — reportadas em `truncatedCollections` p/ o SyncEngine
|
|
5238
|
+
// (a) NÃO rodar deleção-por-ausência e (b) sinalizar
|
|
5239
|
+
// `DbListResult.truncated`. Paginação por cursor é tarefa Core (gated).
|
|
5240
|
+
query: { limit: DOCUMENTS_RESYNC_PAGE_CAP }
|
|
5095
5241
|
});
|
|
5096
5242
|
} catch (err) {
|
|
5097
5243
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -5102,6 +5248,9 @@ function createRestSyncTransport(config) {
|
|
|
5102
5248
|
}
|
|
5103
5249
|
const resp = raw;
|
|
5104
5250
|
const items = Array.isArray(resp?.items) ? resp.items : [];
|
|
5251
|
+
if (items.length >= DOCUMENTS_RESYNC_PAGE_CAP) {
|
|
5252
|
+
truncatedCollections.push(collection);
|
|
5253
|
+
}
|
|
5105
5254
|
for (const item of items) {
|
|
5106
5255
|
if (!item || typeof item !== "object") continue;
|
|
5107
5256
|
const record = item;
|
|
@@ -5129,7 +5278,7 @@ function createRestSyncTransport(config) {
|
|
|
5129
5278
|
});
|
|
5130
5279
|
}
|
|
5131
5280
|
}
|
|
5132
|
-
return { docs, newWatermark: now };
|
|
5281
|
+
return { docs, newWatermark: now, truncatedCollections };
|
|
5133
5282
|
}
|
|
5134
5283
|
};
|
|
5135
5284
|
}
|
|
@@ -5191,20 +5340,20 @@ function buildTicketProvider(config, opts) {
|
|
|
5191
5340
|
return ticket;
|
|
5192
5341
|
};
|
|
5193
5342
|
}
|
|
5194
|
-
var
|
|
5195
|
-
function
|
|
5196
|
-
if (
|
|
5197
|
-
if (dbOpts.dbId) return;
|
|
5343
|
+
var _hasWarnedDbIdIgnored = false;
|
|
5344
|
+
function warnDbIdIgnoredOnce(config, dbOpts) {
|
|
5345
|
+
if (_hasWarnedDbIdIgnored) return;
|
|
5346
|
+
if (!dbOpts.dbId) return;
|
|
5198
5347
|
if (config.env === "prod") return;
|
|
5199
5348
|
if (typeof window === "undefined") return;
|
|
5200
5349
|
if (typeof console === "undefined" || typeof console.warn !== "function") return;
|
|
5201
|
-
|
|
5350
|
+
_hasWarnedDbIdIgnored = true;
|
|
5202
5351
|
console.warn(
|
|
5203
|
-
|
|
5352
|
+
'[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.'
|
|
5204
5353
|
);
|
|
5205
5354
|
}
|
|
5206
5355
|
function createNeetruDb(config, dbOpts = {}) {
|
|
5207
|
-
|
|
5356
|
+
warnDbIdIgnoredOnce(config, dbOpts);
|
|
5208
5357
|
let resolvedTransport;
|
|
5209
5358
|
if (dbOpts.transport !== void 0) {
|
|
5210
5359
|
resolvedTransport = dbOpts.transport;
|
|
@@ -5282,6 +5431,20 @@ function createNeetruDb(config, dbOpts = {}) {
|
|
|
5282
5431
|
async getConflicts() {
|
|
5283
5432
|
const docs = await getDocsPromise();
|
|
5284
5433
|
return docs.getConflicts();
|
|
5434
|
+
},
|
|
5435
|
+
onError(cb) {
|
|
5436
|
+
if (_docsResolved) return _docsResolved.onError(cb);
|
|
5437
|
+
let unsub = null;
|
|
5438
|
+
let cancelled = false;
|
|
5439
|
+
getDocsPromise().then((docs) => {
|
|
5440
|
+
if (!cancelled) {
|
|
5441
|
+
unsub = docs.onError(cb);
|
|
5442
|
+
}
|
|
5443
|
+
});
|
|
5444
|
+
return () => {
|
|
5445
|
+
cancelled = true;
|
|
5446
|
+
unsub?.();
|
|
5447
|
+
};
|
|
5285
5448
|
}
|
|
5286
5449
|
};
|
|
5287
5450
|
}
|