@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/entitlements.d.cts
CHANGED
package/dist/entitlements.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Y as NeetruDb, E as DbCollectionRef, P as FieldSentinel, K as DbQuery } from './types-
|
|
1
|
+
import { Y as NeetruDb, E as DbCollectionRef, P as FieldSentinel, K as DbQuery } from './types-D-QdG502.cjs';
|
|
2
2
|
import 'drizzle-orm/node-postgres';
|
|
3
3
|
import './errors.cjs';
|
|
4
4
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Y as NeetruDb, E as DbCollectionRef, P as FieldSentinel, K as DbQuery } from './types-
|
|
1
|
+
import { Y as NeetruDb, E as DbCollectionRef, P as FieldSentinel, K as DbQuery } from './types-BlOLDMR0.js';
|
|
2
2
|
import 'drizzle-orm/node-postgres';
|
|
3
3
|
import './errors.js';
|
|
4
4
|
|
package/dist/index.cjs
CHANGED
|
@@ -2543,8 +2543,10 @@ var MutationQueue = class {
|
|
|
2543
2543
|
await this.store.deleteMutation(mutationId);
|
|
2544
2544
|
}
|
|
2545
2545
|
/**
|
|
2546
|
-
* Marca uma mutação como `failed` e registra o erro.
|
|
2547
|
-
* Incrementa `attempts`. Chamado pelo SyncEngine
|
|
2546
|
+
* Marca uma mutação como `failed` (dead-letter) e registra o erro.
|
|
2547
|
+
* Incrementa `attempts` e carimba `lastAttemptAt`. Chamado pelo SyncEngine
|
|
2548
|
+
* quando a mutação esgota o teto de tentativas (mutação-veneno): sai do
|
|
2549
|
+
* conjunto `queued`, então `drain()` para de devolvê-la e a fila DESTRAVA.
|
|
2548
2550
|
*/
|
|
2549
2551
|
async markFailed(mutationId, error) {
|
|
2550
2552
|
const mut = await this.store.getMutation(mutationId);
|
|
@@ -2553,12 +2555,14 @@ var MutationQueue = class {
|
|
|
2553
2555
|
...mut,
|
|
2554
2556
|
status: "failed",
|
|
2555
2557
|
lastError: error,
|
|
2556
|
-
attempts: mut.attempts + 1
|
|
2558
|
+
attempts: mut.attempts + 1,
|
|
2559
|
+
lastAttemptAt: Date.now()
|
|
2557
2560
|
});
|
|
2558
2561
|
}
|
|
2559
2562
|
/**
|
|
2560
|
-
* Incrementa `attempts` e volta para `queued`
|
|
2561
|
-
* Chamado pelo SyncEngine em falha
|
|
2563
|
+
* Incrementa `attempts`, carimba `lastAttemptAt` e volta para `queued`
|
|
2564
|
+
* (falha transiente com backoff). Chamado pelo SyncEngine em falha
|
|
2565
|
+
* transiente (5xx, timeout) da mutação que REALMENTE foi tentada.
|
|
2562
2566
|
*/
|
|
2563
2567
|
async markRetry(mutationId, error) {
|
|
2564
2568
|
const mut = await this.store.getMutation(mutationId);
|
|
@@ -2567,9 +2571,24 @@ var MutationQueue = class {
|
|
|
2567
2571
|
...mut,
|
|
2568
2572
|
status: "queued",
|
|
2569
2573
|
lastError: error,
|
|
2570
|
-
attempts: mut.attempts + 1
|
|
2574
|
+
attempts: mut.attempts + 1,
|
|
2575
|
+
lastAttemptAt: Date.now()
|
|
2571
2576
|
});
|
|
2572
2577
|
}
|
|
2578
|
+
/**
|
|
2579
|
+
* Reverte uma mutação `inflight` de volta a `queued` SEM tocar em `attempts`
|
|
2580
|
+
* nem `lastAttemptAt`. Usado para mutações que foram marcadas inflight mas
|
|
2581
|
+
* NUNCA chegaram a ser enviadas (estavam atrás da head no FIFO e o push
|
|
2582
|
+
* parou antes delas): contá-las como tentativa as empurraria pro dead-letter
|
|
2583
|
+
* por engano, e adiaria seu envio via backoff sem que tenham falhado.
|
|
2584
|
+
* No-op se a mutação não está mais `inflight`.
|
|
2585
|
+
*/
|
|
2586
|
+
async requeueInflight(mutationId) {
|
|
2587
|
+
const mut = await this.store.getMutation(mutationId);
|
|
2588
|
+
if (!mut) return;
|
|
2589
|
+
if (mut.status !== "inflight") return;
|
|
2590
|
+
await this.store.putMutation({ ...mut, status: "queued" });
|
|
2591
|
+
}
|
|
2573
2592
|
};
|
|
2574
2593
|
|
|
2575
2594
|
// src/db/field-value.ts
|
|
@@ -2625,6 +2644,10 @@ function hasFieldSentinel(data) {
|
|
|
2625
2644
|
}
|
|
2626
2645
|
|
|
2627
2646
|
// src/db/offline/sync-engine.ts
|
|
2647
|
+
var MAX_MUTATION_ATTEMPTS = 8;
|
|
2648
|
+
var BACKOFF_BASE_MS = 1e3;
|
|
2649
|
+
var BACKOFF_CAP_MS = 5 * 60 * 1e3;
|
|
2650
|
+
var TRUNCATED_COLLECTIONS_META_KEY = "truncatedCollections";
|
|
2628
2651
|
function hasSentinels(payload) {
|
|
2629
2652
|
return payload !== null && hasFieldSentinel(payload);
|
|
2630
2653
|
}
|
|
@@ -2659,6 +2682,8 @@ var SyncEngine = class {
|
|
|
2659
2682
|
_state;
|
|
2660
2683
|
/** Listeners de mudança de SyncState. */
|
|
2661
2684
|
_stateListeners = /* @__PURE__ */ new Set();
|
|
2685
|
+
/** Listeners de erro de ciclo de sync (P2-C — não mais engolido em silêncio). */
|
|
2686
|
+
_errorListeners = /* @__PURE__ */ new Set();
|
|
2662
2687
|
/** Cleanup functions dos listeners externos. */
|
|
2663
2688
|
_cleanups = [];
|
|
2664
2689
|
/** Timer de resync periódico. */
|
|
@@ -2717,6 +2742,19 @@ var SyncEngine = class {
|
|
|
2717
2742
|
this._stateListeners.delete(cb);
|
|
2718
2743
|
};
|
|
2719
2744
|
}
|
|
2745
|
+
/**
|
|
2746
|
+
* Registra um hook de erro de sync (P2-C). Disparado quando um ciclo falha
|
|
2747
|
+
* (exceção inesperada nas fases, falha de pull/resync ou dead-letter de uma
|
|
2748
|
+
* mutação-veneno) — o que antes era engolido com "log silencioso".
|
|
2749
|
+
* O mesmo diagnóstico também aparece em `SyncState.lastError`.
|
|
2750
|
+
* Retorna uma função de unsubscribe.
|
|
2751
|
+
*/
|
|
2752
|
+
onError(cb) {
|
|
2753
|
+
this._errorListeners.add(cb);
|
|
2754
|
+
return () => {
|
|
2755
|
+
this._errorListeners.delete(cb);
|
|
2756
|
+
};
|
|
2757
|
+
}
|
|
2720
2758
|
/**
|
|
2721
2759
|
* Força um sync imediato (I3 §10.3 `flush()`).
|
|
2722
2760
|
* Resolve quando a fila esvazia OU rejeita se o engine estiver destruído.
|
|
@@ -2749,8 +2787,10 @@ var SyncEngine = class {
|
|
|
2749
2787
|
}
|
|
2750
2788
|
await this._phase2Pull();
|
|
2751
2789
|
this._phase3Realtime();
|
|
2752
|
-
this._updateState({ status: "idle", lastSyncedAt: Date.now() });
|
|
2790
|
+
this._updateState({ status: "idle", lastSyncedAt: Date.now(), lastError: null });
|
|
2753
2791
|
} catch (err) {
|
|
2792
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
2793
|
+
this._recordSyncError("cycle", `ciclo de sync falhou: ${msg}`);
|
|
2754
2794
|
this._updateState({ status: "idle" });
|
|
2755
2795
|
} finally {
|
|
2756
2796
|
this._syncing = false;
|
|
@@ -2785,6 +2825,9 @@ var SyncEngine = class {
|
|
|
2785
2825
|
async _phase1Push() {
|
|
2786
2826
|
const mutations = await this._queue.drain();
|
|
2787
2827
|
if (mutations.length === 0) return true;
|
|
2828
|
+
if (this._isInBackoff(mutations[0])) {
|
|
2829
|
+
return true;
|
|
2830
|
+
}
|
|
2788
2831
|
for (const mut of mutations) {
|
|
2789
2832
|
await this._queue.markInflight(mut.mutationId);
|
|
2790
2833
|
}
|
|
@@ -2792,10 +2835,16 @@ var SyncEngine = class {
|
|
|
2792
2835
|
try {
|
|
2793
2836
|
result = await this._transport.pushMutations(mutations);
|
|
2794
2837
|
} catch (err) {
|
|
2795
|
-
|
|
2796
|
-
|
|
2797
|
-
|
|
2798
|
-
|
|
2838
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
2839
|
+
const busChangesOnThrow = [];
|
|
2840
|
+
const [head, ...behind] = mutations;
|
|
2841
|
+
await this._retryOrDeadLetter(head, errMsg, busChangesOnThrow);
|
|
2842
|
+
for (const mut of behind) {
|
|
2843
|
+
await this._queue.requeueInflight(mut.mutationId);
|
|
2844
|
+
}
|
|
2845
|
+
if (busChangesOnThrow.length > 0) this._bus.emit(busChangesOnThrow);
|
|
2846
|
+
this._refreshPendingWrites().catch(() => {
|
|
2847
|
+
});
|
|
2799
2848
|
return false;
|
|
2800
2849
|
}
|
|
2801
2850
|
const busChanges = [];
|
|
@@ -2822,8 +2871,16 @@ var SyncEngine = class {
|
|
|
2822
2871
|
}
|
|
2823
2872
|
const processedIds = new Set(result.results.map((r) => r.mutationId));
|
|
2824
2873
|
const unprocessed = mutations.filter((m) => !processedIds.has(m.mutationId));
|
|
2825
|
-
|
|
2826
|
-
|
|
2874
|
+
if (unprocessed.length > 0) {
|
|
2875
|
+
const [boundary, ...behind] = unprocessed;
|
|
2876
|
+
await this._retryOrDeadLetter(
|
|
2877
|
+
boundary,
|
|
2878
|
+
"partial_push: muta\xE7\xE3o n\xE3o processada no lote",
|
|
2879
|
+
busChanges
|
|
2880
|
+
);
|
|
2881
|
+
for (const mut of behind) {
|
|
2882
|
+
await this._queue.requeueInflight(mut.mutationId);
|
|
2883
|
+
}
|
|
2827
2884
|
}
|
|
2828
2885
|
if (busChanges.length > 0) {
|
|
2829
2886
|
this._bus.emit(busChanges);
|
|
@@ -3015,6 +3072,67 @@ var SyncEngine = class {
|
|
|
3015
3072
|
});
|
|
3016
3073
|
}
|
|
3017
3074
|
}
|
|
3075
|
+
// ─── Dead-letter + backoff (P1-C) ────────────────────────────────────────────
|
|
3076
|
+
/**
|
|
3077
|
+
* Trata a falha transiente de UMA mutação que foi realmente tentada.
|
|
3078
|
+
*
|
|
3079
|
+
* - Se ainda há orçamento de tentativas → `markRetry` (volta pra fila com
|
|
3080
|
+
* backoff, `attempts++`).
|
|
3081
|
+
* - Se esgotou (`attempts+1 >= MAX_MUTATION_ATTEMPTS`) → DEAD-LETTER:
|
|
3082
|
+
* `markFailed` (sai do conjunto `queued`, então `drain()` para de devolvê-la
|
|
3083
|
+
* e a fila FIFO DESTRAVA para as demais coleções) + grava um ConflictRecord
|
|
3084
|
+
* `failed_max_attempts` (o produto vê via `getConflicts()`) + limpa o estado
|
|
3085
|
+
* pending do doc no cache + registra `lastError`/dispara `onError`.
|
|
3086
|
+
*/
|
|
3087
|
+
async _retryOrDeadLetter(mut, errMsg, busChanges) {
|
|
3088
|
+
if (mut.attempts + 1 < MAX_MUTATION_ATTEMPTS) {
|
|
3089
|
+
await this._queue.markRetry(mut.mutationId, errMsg);
|
|
3090
|
+
return;
|
|
3091
|
+
}
|
|
3092
|
+
await this._queue.markFailed(mut.mutationId, errMsg);
|
|
3093
|
+
const conflictRecord = {
|
|
3094
|
+
collection: mut.collection,
|
|
3095
|
+
docId: mut.docId,
|
|
3096
|
+
mutationId: mut.mutationId,
|
|
3097
|
+
losingData: mut.payload ?? {},
|
|
3098
|
+
winningData: {},
|
|
3099
|
+
reason: "failed_max_attempts",
|
|
3100
|
+
detectedAt: Date.now(),
|
|
3101
|
+
delivered: false
|
|
3102
|
+
};
|
|
3103
|
+
await this._store.appendConflict(conflictRecord);
|
|
3104
|
+
const existing = await this._store.getDoc(mut.collection, mut.docId);
|
|
3105
|
+
if (existing) {
|
|
3106
|
+
await this._store.putDoc({
|
|
3107
|
+
...existing,
|
|
3108
|
+
meta: { ...existing.meta, state: "synced", pendingMutationIds: [] }
|
|
3109
|
+
});
|
|
3110
|
+
busChanges.push({
|
|
3111
|
+
type: "modified",
|
|
3112
|
+
collection: mut.collection,
|
|
3113
|
+
doc: { id: mut.docId, data: existing.data }
|
|
3114
|
+
});
|
|
3115
|
+
}
|
|
3116
|
+
this._recordSyncError(
|
|
3117
|
+
"push",
|
|
3118
|
+
`muta\xE7\xE3o ${mut.mutationId} (${mut.collection}/${mut.docId}) descartada ap\xF3s ${mut.attempts + 1} tentativas: ${errMsg}`
|
|
3119
|
+
);
|
|
3120
|
+
}
|
|
3121
|
+
/**
|
|
3122
|
+
* `true` se a mutação (head do FIFO) ainda está dentro da janela de backoff
|
|
3123
|
+
* desde a última tentativa falha. Só se aplica a mutações já tentadas
|
|
3124
|
+
* (`attempts > 0` e `lastAttemptAt` carimbado).
|
|
3125
|
+
*/
|
|
3126
|
+
_isInBackoff(mut) {
|
|
3127
|
+
if (mut.attempts <= 0) return false;
|
|
3128
|
+
const last = mut.lastAttemptAt;
|
|
3129
|
+
if (typeof last !== "number") return false;
|
|
3130
|
+
return Date.now() < last + this._backoffDelayMs(mut.attempts);
|
|
3131
|
+
}
|
|
3132
|
+
/** Backoff exponencial keyed em `attempts`, com teto `BACKOFF_CAP_MS`. */
|
|
3133
|
+
_backoffDelayMs(attempts) {
|
|
3134
|
+
return Math.min(BACKOFF_BASE_MS * 2 ** (attempts - 1), BACKOFF_CAP_MS);
|
|
3135
|
+
}
|
|
3018
3136
|
// ─── FASE 2 — pull (reconciliar o cache) ─────────────────────────────────────
|
|
3019
3137
|
/**
|
|
3020
3138
|
* Busca mudanças do servidor e aplica ao cache via ConflictResolver.
|
|
@@ -3054,10 +3172,14 @@ var SyncEngine = class {
|
|
|
3054
3172
|
}
|
|
3055
3173
|
const busChanges = [];
|
|
3056
3174
|
await this._applyServerDocs(resyncResult.docs, busChanges, true);
|
|
3175
|
+
const truncatedCollections = resyncResult.truncatedCollections ?? [];
|
|
3176
|
+
const truncatedSet = new Set(truncatedCollections);
|
|
3177
|
+
await this._store.setMeta(TRUNCATED_COLLECTIONS_META_KEY, truncatedCollections);
|
|
3057
3178
|
const returnedKeys = new Set(
|
|
3058
3179
|
resyncResult.docs.map((d) => `${d.collection}::${d.id}`)
|
|
3059
3180
|
);
|
|
3060
3181
|
for (const col of collections) {
|
|
3182
|
+
if (truncatedSet.has(col)) continue;
|
|
3061
3183
|
const queryResult = await this._store.listDocs(col, {});
|
|
3062
3184
|
for (const { id } of queryResult.docs) {
|
|
3063
3185
|
const key = `${col}::${id}`;
|
|
@@ -3217,7 +3339,8 @@ var SyncEngine = class {
|
|
|
3217
3339
|
status: isOnline ? "idle" : "offline",
|
|
3218
3340
|
pendingWrites: 0,
|
|
3219
3341
|
lastSyncedAt: null,
|
|
3220
|
-
isLeaderTab: this._tabCoordinator.isLeader()
|
|
3342
|
+
isLeaderTab: this._tabCoordinator.isLeader(),
|
|
3343
|
+
lastError: null
|
|
3221
3344
|
};
|
|
3222
3345
|
}
|
|
3223
3346
|
/**
|
|
@@ -3227,7 +3350,7 @@ var SyncEngine = class {
|
|
|
3227
3350
|
_updateState(partial) {
|
|
3228
3351
|
const prev = this._state;
|
|
3229
3352
|
const next = { ...prev, ...partial };
|
|
3230
|
-
const changed = prev.status !== next.status || prev.pendingWrites !== next.pendingWrites || prev.lastSyncedAt !== next.lastSyncedAt || prev.isLeaderTab !== next.isLeaderTab;
|
|
3353
|
+
const changed = prev.status !== next.status || prev.pendingWrites !== next.pendingWrites || prev.lastSyncedAt !== next.lastSyncedAt || prev.isLeaderTab !== next.isLeaderTab || (prev.lastError ?? null) !== (next.lastError ?? null);
|
|
3231
3354
|
this._state = next;
|
|
3232
3355
|
if (changed) {
|
|
3233
3356
|
this._emitState(next);
|
|
@@ -3241,6 +3364,21 @@ var SyncEngine = class {
|
|
|
3241
3364
|
}
|
|
3242
3365
|
}
|
|
3243
3366
|
}
|
|
3367
|
+
/**
|
|
3368
|
+
* P2-C: registra a falha de um ciclo de sync — antes engolida em silêncio.
|
|
3369
|
+
* Publica em `SyncState.lastError` (visível via `onSyncStateChanged`) e
|
|
3370
|
+
* dispara o hook `onError`. Nunca lança (diagnóstico é best-effort).
|
|
3371
|
+
*/
|
|
3372
|
+
_recordSyncError(phase, message) {
|
|
3373
|
+
this._updateState({ lastError: message });
|
|
3374
|
+
const event = { phase, message, at: Date.now() };
|
|
3375
|
+
for (const listener of this._errorListeners) {
|
|
3376
|
+
try {
|
|
3377
|
+
listener({ ...event });
|
|
3378
|
+
} catch {
|
|
3379
|
+
}
|
|
3380
|
+
}
|
|
3381
|
+
}
|
|
3244
3382
|
/**
|
|
3245
3383
|
* Atualiza `pendingWrites` no estado.
|
|
3246
3384
|
* Chamado de forma assíncrona quando necessário.
|
|
@@ -4050,7 +4188,9 @@ var DbCollectionRefImpl = class {
|
|
|
4050
4188
|
const isOnline = this._isOnline();
|
|
4051
4189
|
const syncState = this._engine.getSyncState();
|
|
4052
4190
|
const nextCursor = buildNextCursor(result.docs, effectiveLimit);
|
|
4053
|
-
const
|
|
4191
|
+
const truncatedRaw = await this._store.getMeta(TRUNCATED_COLLECTIONS_META_KEY);
|
|
4192
|
+
const truncated = Array.isArray(truncatedRaw) && truncatedRaw.includes(this._collection);
|
|
4193
|
+
const stale = !isOnline || syncState.status === "offline" || truncated;
|
|
4054
4194
|
const fromCache = hasPending || syncState.lastSyncedAt === null || stale;
|
|
4055
4195
|
return {
|
|
4056
4196
|
docs: result.docs.map((d) => ({ id: d.id, data: d.data })),
|
|
@@ -4058,7 +4198,8 @@ var DbCollectionRefImpl = class {
|
|
|
4058
4198
|
fromCache,
|
|
4059
4199
|
stale,
|
|
4060
4200
|
hasPendingWrites: hasPending,
|
|
4061
|
-
changes: []
|
|
4201
|
+
changes: [],
|
|
4202
|
+
truncated
|
|
4062
4203
|
};
|
|
4063
4204
|
}
|
|
4064
4205
|
// ── CRUD ────────────────────────────────────────────────────────────────────
|
|
@@ -4504,6 +4645,9 @@ var NeetruDbDocumentsImpl = class {
|
|
|
4504
4645
|
async getConflicts() {
|
|
4505
4646
|
return this._store.listConflicts();
|
|
4506
4647
|
}
|
|
4648
|
+
onError(cb) {
|
|
4649
|
+
return this._engine.onError(cb);
|
|
4650
|
+
}
|
|
4507
4651
|
};
|
|
4508
4652
|
async function createOfflineDocumentsNamespace(opts) {
|
|
4509
4653
|
const store = isIndexedDBUnavailable() ? new MemoryStore() : new LocalStore(opts.dbName);
|
|
@@ -4979,6 +5123,7 @@ var defaultWebSocketFactory = (url) => {
|
|
|
4979
5123
|
// src/db/client-db.ts
|
|
4980
5124
|
var DATASTORE_COLLECTION_ENDPOINT = (collection) => `/api/sdk/v1/datastore/${encodeURIComponent(collection)}`;
|
|
4981
5125
|
var DATASTORE_DOC_ENDPOINT = (collection, docId) => `/api/sdk/v1/datastore/${encodeURIComponent(collection)}/${encodeURIComponent(docId)}`;
|
|
5126
|
+
var DOCUMENTS_RESYNC_PAGE_CAP = 200;
|
|
4982
5127
|
function resolveTransport(transport, opts, config) {
|
|
4983
5128
|
if (opts._transport) {
|
|
4984
5129
|
return opts._transport;
|
|
@@ -5091,6 +5236,7 @@ function createRestSyncTransport(config) {
|
|
|
5091
5236
|
const tenantHeaders = tenantScopeId ? { "x-neetru-tenant": tenantScopeId } : void 0;
|
|
5092
5237
|
const now = Date.now();
|
|
5093
5238
|
const docs = [];
|
|
5239
|
+
const truncatedCollections = [];
|
|
5094
5240
|
for (const collection of collections) {
|
|
5095
5241
|
let raw;
|
|
5096
5242
|
try {
|
|
@@ -5100,11 +5246,11 @@ function createRestSyncTransport(config) {
|
|
|
5100
5246
|
requireAuth: true,
|
|
5101
5247
|
retries: 2,
|
|
5102
5248
|
headers: tenantHeaders,
|
|
5103
|
-
// Teto do Core
|
|
5104
|
-
//
|
|
5105
|
-
// deleção-por-ausência
|
|
5106
|
-
//
|
|
5107
|
-
query: { limit:
|
|
5249
|
+
// Teto do Core (DOCUMENTS_RESYNC_PAGE_CAP). Coleções acima dele vêm
|
|
5250
|
+
// TRUNCADAS — reportadas em `truncatedCollections` p/ o SyncEngine
|
|
5251
|
+
// (a) NÃO rodar deleção-por-ausência e (b) sinalizar
|
|
5252
|
+
// `DbListResult.truncated`. Paginação por cursor é tarefa Core (gated).
|
|
5253
|
+
query: { limit: DOCUMENTS_RESYNC_PAGE_CAP }
|
|
5108
5254
|
});
|
|
5109
5255
|
} catch (err) {
|
|
5110
5256
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -5115,6 +5261,9 @@ function createRestSyncTransport(config) {
|
|
|
5115
5261
|
}
|
|
5116
5262
|
const resp = raw;
|
|
5117
5263
|
const items = Array.isArray(resp?.items) ? resp.items : [];
|
|
5264
|
+
if (items.length >= DOCUMENTS_RESYNC_PAGE_CAP) {
|
|
5265
|
+
truncatedCollections.push(collection);
|
|
5266
|
+
}
|
|
5118
5267
|
for (const item of items) {
|
|
5119
5268
|
if (!item || typeof item !== "object") continue;
|
|
5120
5269
|
const record = item;
|
|
@@ -5142,7 +5291,7 @@ function createRestSyncTransport(config) {
|
|
|
5142
5291
|
});
|
|
5143
5292
|
}
|
|
5144
5293
|
}
|
|
5145
|
-
return { docs, newWatermark: now };
|
|
5294
|
+
return { docs, newWatermark: now, truncatedCollections };
|
|
5146
5295
|
}
|
|
5147
5296
|
};
|
|
5148
5297
|
}
|
|
@@ -5204,20 +5353,20 @@ function buildTicketProvider(config, opts) {
|
|
|
5204
5353
|
return ticket;
|
|
5205
5354
|
};
|
|
5206
5355
|
}
|
|
5207
|
-
var
|
|
5208
|
-
function
|
|
5209
|
-
if (
|
|
5210
|
-
if (dbOpts.dbId) return;
|
|
5356
|
+
var _hasWarnedDbIdIgnored = false;
|
|
5357
|
+
function warnDbIdIgnoredOnce(config, dbOpts) {
|
|
5358
|
+
if (_hasWarnedDbIdIgnored) return;
|
|
5359
|
+
if (!dbOpts.dbId) return;
|
|
5211
5360
|
if (config.env === "prod") return;
|
|
5212
5361
|
if (typeof window === "undefined") return;
|
|
5213
5362
|
if (typeof console === "undefined" || typeof console.warn !== "function") return;
|
|
5214
|
-
|
|
5363
|
+
_hasWarnedDbIdIgnored = true;
|
|
5215
5364
|
console.warn(
|
|
5216
|
-
|
|
5365
|
+
'[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.'
|
|
5217
5366
|
);
|
|
5218
5367
|
}
|
|
5219
5368
|
function createNeetruDb(config, dbOpts = {}) {
|
|
5220
|
-
|
|
5369
|
+
warnDbIdIgnoredOnce(config, dbOpts);
|
|
5221
5370
|
let resolvedTransport;
|
|
5222
5371
|
if (dbOpts.transport !== void 0) {
|
|
5223
5372
|
resolvedTransport = dbOpts.transport;
|
|
@@ -5295,6 +5444,20 @@ function createNeetruDb(config, dbOpts = {}) {
|
|
|
5295
5444
|
async getConflicts() {
|
|
5296
5445
|
const docs = await getDocsPromise();
|
|
5297
5446
|
return docs.getConflicts();
|
|
5447
|
+
},
|
|
5448
|
+
onError(cb) {
|
|
5449
|
+
if (_docsResolved) return _docsResolved.onError(cb);
|
|
5450
|
+
let unsub = null;
|
|
5451
|
+
let cancelled = false;
|
|
5452
|
+
getDocsPromise().then((docs) => {
|
|
5453
|
+
if (!cancelled) {
|
|
5454
|
+
unsub = docs.onError(cb);
|
|
5455
|
+
}
|
|
5456
|
+
});
|
|
5457
|
+
return () => {
|
|
5458
|
+
cancelled = true;
|
|
5459
|
+
unsub?.();
|
|
5460
|
+
};
|
|
5298
5461
|
}
|
|
5299
5462
|
};
|
|
5300
5463
|
}
|
|
@@ -7841,7 +8004,7 @@ function createNeetruClient(config = {}) {
|
|
|
7841
8004
|
init_errors();
|
|
7842
8005
|
init_db_errors();
|
|
7843
8006
|
init_http();
|
|
7844
|
-
var VERSION = "3.4.
|
|
8007
|
+
var VERSION = "3.4.1";
|
|
7845
8008
|
|
|
7846
8009
|
exports.DEFAULT_BASE_URL = DEFAULT_BASE_URL;
|
|
7847
8010
|
exports.DEV_FIXTURE_USER = DEV_FIXTURE_USER;
|