@neetru/sdk 3.0.2 → 3.1.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.
Files changed (98) hide show
  1. package/CHANGELOG.md +554 -530
  2. package/dist/auth.cjs +219 -54
  3. package/dist/auth.cjs.map +1 -1
  4. package/dist/auth.d.cts +1 -2
  5. package/dist/auth.d.ts +1 -2
  6. package/dist/auth.mjs +219 -54
  7. package/dist/auth.mjs.map +1 -1
  8. package/dist/catalog.cjs +50 -2
  9. package/dist/catalog.cjs.map +1 -1
  10. package/dist/catalog.d.cts +1 -2
  11. package/dist/catalog.d.ts +1 -2
  12. package/dist/catalog.mjs +50 -2
  13. package/dist/catalog.mjs.map +1 -1
  14. package/dist/checkout.cjs +52 -3
  15. package/dist/checkout.cjs.map +1 -1
  16. package/dist/checkout.d.cts +1 -2
  17. package/dist/checkout.d.ts +1 -2
  18. package/dist/checkout.mjs +52 -3
  19. package/dist/checkout.mjs.map +1 -1
  20. package/dist/db-react.cjs +18 -6
  21. package/dist/db-react.cjs.map +1 -1
  22. package/dist/db-react.d.cts +35 -13
  23. package/dist/db-react.d.ts +35 -13
  24. package/dist/db-react.mjs +18 -6
  25. package/dist/db-react.mjs.map +1 -1
  26. package/dist/db.cjs +164 -56
  27. package/dist/db.cjs.map +1 -1
  28. package/dist/db.d.cts +1 -2
  29. package/dist/db.d.ts +1 -2
  30. package/dist/db.mjs +164 -56
  31. package/dist/db.mjs.map +1 -1
  32. package/dist/entitlements.cjs +50 -2
  33. package/dist/entitlements.cjs.map +1 -1
  34. package/dist/entitlements.d.cts +1 -2
  35. package/dist/entitlements.d.ts +1 -2
  36. package/dist/entitlements.mjs +50 -2
  37. package/dist/entitlements.mjs.map +1 -1
  38. package/dist/errors.cjs.map +1 -1
  39. package/dist/errors.d.cts +3 -1
  40. package/dist/errors.d.ts +3 -1
  41. package/dist/errors.mjs.map +1 -1
  42. package/dist/firestore-compat.cjs +411 -0
  43. package/dist/firestore-compat.cjs.map +1 -0
  44. package/dist/firestore-compat.d.cts +319 -0
  45. package/dist/firestore-compat.d.ts +319 -0
  46. package/dist/firestore-compat.mjs +383 -0
  47. package/dist/firestore-compat.mjs.map +1 -0
  48. package/dist/index.cjs +266 -66
  49. package/dist/index.cjs.map +1 -1
  50. package/dist/index.d.cts +9 -4
  51. package/dist/index.d.ts +9 -4
  52. package/dist/index.mjs +265 -67
  53. package/dist/index.mjs.map +1 -1
  54. package/dist/mocks.cjs.map +1 -1
  55. package/dist/mocks.d.cts +1 -2
  56. package/dist/mocks.d.ts +1 -2
  57. package/dist/mocks.mjs.map +1 -1
  58. package/dist/notifications.cjs +50 -2
  59. package/dist/notifications.cjs.map +1 -1
  60. package/dist/notifications.d.cts +1 -2
  61. package/dist/notifications.d.ts +1 -2
  62. package/dist/notifications.mjs +50 -2
  63. package/dist/notifications.mjs.map +1 -1
  64. package/dist/react.cjs +268 -2
  65. package/dist/react.cjs.map +1 -1
  66. package/dist/react.d.cts +135 -3
  67. package/dist/react.d.ts +135 -3
  68. package/dist/react.mjs +266 -3
  69. package/dist/react.mjs.map +1 -1
  70. package/dist/support.cjs +50 -2
  71. package/dist/support.cjs.map +1 -1
  72. package/dist/support.d.cts +1 -2
  73. package/dist/support.d.ts +1 -2
  74. package/dist/support.mjs +50 -2
  75. package/dist/support.mjs.map +1 -1
  76. package/dist/telemetry.cjs +65 -4
  77. package/dist/telemetry.cjs.map +1 -1
  78. package/dist/telemetry.d.cts +1 -2
  79. package/dist/telemetry.d.ts +1 -2
  80. package/dist/telemetry.mjs +65 -4
  81. package/dist/telemetry.mjs.map +1 -1
  82. package/dist/{types-D7zVkO3n.d.ts → types-B3XFHD4A.d.ts} +640 -2
  83. package/dist/{types-Dc4bjrrt.d.cts → types-DePdSU3P.d.cts} +640 -2
  84. package/dist/usage.cjs +50 -2
  85. package/dist/usage.cjs.map +1 -1
  86. package/dist/usage.d.cts +1 -2
  87. package/dist/usage.d.ts +1 -2
  88. package/dist/usage.mjs +50 -2
  89. package/dist/usage.mjs.map +1 -1
  90. package/dist/webhooks.cjs +83 -2
  91. package/dist/webhooks.cjs.map +1 -1
  92. package/dist/webhooks.d.cts +1 -2
  93. package/dist/webhooks.d.ts +1 -2
  94. package/dist/webhooks.mjs +83 -3
  95. package/dist/webhooks.mjs.map +1 -1
  96. package/package.json +156 -151
  97. package/dist/collection-ref-BDdfD87k.d.cts +0 -581
  98. package/dist/collection-ref-BDdfD87k.d.ts +0 -581
package/dist/db.cjs CHANGED
@@ -87,8 +87,47 @@ var init_idempotency = __esm({
87
87
  // src/http.ts
88
88
  var http_exports = {};
89
89
  __export(http_exports, {
90
+ checkCoreVersionCompat: () => checkCoreVersionCompat,
90
91
  httpRequest: () => httpRequest
91
92
  });
93
+ function stripRevision(v) {
94
+ return v.split("+")[0] ?? v;
95
+ }
96
+ function compareCalVer(a, b) {
97
+ const partsA = stripRevision(a).split(".").map(Number);
98
+ const partsB = stripRevision(b).split(".").map(Number);
99
+ const len = Math.max(partsA.length, partsB.length);
100
+ for (let i = 0; i < len; i++) {
101
+ const pa = partsA[i] ?? 0;
102
+ const pb = partsB[i] ?? 0;
103
+ if (pa !== pb) return pa - pb;
104
+ }
105
+ return 0;
106
+ }
107
+ function getSdkMajor() {
108
+ try {
109
+ const ver = globalThis.__NEETRU_SDK_VERSION__;
110
+ if (ver) return parseInt(ver.split(".")[0] ?? "3", 10);
111
+ } catch {
112
+ }
113
+ return 3;
114
+ }
115
+ function checkCoreVersionCompat(coreVersion) {
116
+ if (!coreVersion) return;
117
+ const sdkMajor = getSdkMajor();
118
+ const minCore = COMPAT_MATRIX[sdkMajor];
119
+ if (!minCore) return;
120
+ const stripped = stripRevision(coreVersion);
121
+ if (compareCalVer(stripped, minCore) < 0) {
122
+ const key = `${sdkMajor}:${stripped}`;
123
+ if (!_warnedCoreTooOld.has(key)) {
124
+ _warnedCoreTooOld.add(key);
125
+ console.warn(
126
+ `[Neetru SDK] Aten\xE7\xE3o: Core vers\xE3o "${coreVersion}" \xE9 anterior ao m\xEDnimo recomendado "${minCore}" para SDK major ${sdkMajor}. Alguns recursos podem n\xE3o funcionar corretamente. Atualize o Core para a vers\xE3o mais recente.`
127
+ );
128
+ }
129
+ }
130
+ }
92
131
  function backoffMs(attempt) {
93
132
  const base = 200 * Math.pow(4, attempt);
94
133
  const jitter = base * 0.2 * (Math.random() * 2 - 1);
@@ -97,11 +136,11 @@ function backoffMs(attempt) {
97
136
  function parseRetryAfter(value) {
98
137
  if (!value) return null;
99
138
  const secs = Number(value);
100
- if (Number.isFinite(secs) && secs >= 0) return Math.round(secs * 1e3);
139
+ if (Number.isFinite(secs) && secs >= 0) return Math.min(Math.round(secs * 1e3), MAX_RETRY_AFTER_MS);
101
140
  const dateMs = Date.parse(value);
102
141
  if (Number.isFinite(dateMs)) {
103
142
  const delta = dateMs - Date.now();
104
- if (delta > 0) return delta;
143
+ if (delta > 0) return Math.min(delta, MAX_RETRY_AFTER_MS);
105
144
  }
106
145
  return null;
107
146
  }
@@ -112,6 +151,7 @@ function statusToCode(status) {
112
151
  if (status === 401) return "unauthorized";
113
152
  if (status === 403) return "forbidden";
114
153
  if (status === 404) return "not_found";
154
+ if (status === 409) return "conflict";
115
155
  if (status === 422 || status === 400) return "validation_failed";
116
156
  if (status === 429) return "rate_limited";
117
157
  if (status >= 500) return "server_error";
@@ -183,6 +223,8 @@ async function httpRequest(config, opts) {
183
223
  }
184
224
  const requestId = res.headers.get("x-request-id") ?? res.headers.get("x-correlation-id") ?? void 0;
185
225
  if (res.ok) {
226
+ const coreVersion = res.headers.get("x-neetru-version");
227
+ if (coreVersion) checkCoreVersionCompat(coreVersion);
186
228
  const parsed = await safeJson(res);
187
229
  return parsed;
188
230
  }
@@ -211,11 +253,17 @@ async function httpRequest(config, opts) {
211
253
  }
212
254
  throw lastError ?? new NeetruError("unknown", "unexpected httpRequest exit");
213
255
  }
214
- var DEFAULT_RETRIES_READ, DEFAULT_RETRIES_WRITE, READ_METHODS, RETRYABLE_CODES2;
256
+ var COMPAT_MATRIX, _warnedCoreTooOld, DEFAULT_RETRIES_READ, DEFAULT_RETRIES_WRITE, READ_METHODS, RETRYABLE_CODES2, MAX_RETRY_AFTER_MS;
215
257
  var init_http = __esm({
216
258
  "src/http.ts"() {
217
259
  init_errors();
218
260
  init_idempotency();
261
+ COMPAT_MATRIX = {
262
+ // SDK major 3.x requer Core ≥ 2026.05.1 (suporte a X-Neetru-Version header,
263
+ // runTransaction write-after-read, verifyWebhookRequest, webhooks 3.0+)
264
+ 3: "2026.05.1"
265
+ };
266
+ _warnedCoreTooOld = /* @__PURE__ */ new Set();
219
267
  DEFAULT_RETRIES_READ = 2;
220
268
  DEFAULT_RETRIES_WRITE = 0;
221
269
  READ_METHODS = /* @__PURE__ */ new Set(["GET", "HEAD"]);
@@ -224,6 +272,7 @@ var init_http = __esm({
224
272
  "server_error",
225
273
  "network_error"
226
274
  ]);
275
+ MAX_RETRY_AFTER_MS = 6e4;
227
276
  }
228
277
  });
229
278
 
@@ -790,6 +839,31 @@ var LocalStore = class {
790
839
  this.db?.close();
791
840
  this.db = null;
792
841
  }
842
+ /**
843
+ * Apaga completamente o banco IndexedDB e o recria do zero.
844
+ *
845
+ * Ao contrário de `close()` + `open()` (que reabre o banco existente sem
846
+ * apagar dados), `clearAll()` chama `indexedDB.deleteDatabase(dbName)` e
847
+ * só então chama `open()` — garantindo que nenhum documento, mutação ou
848
+ * metadado sobrevive.
849
+ *
850
+ * BUG fix: `clearCache()` em `NeetruDbDocumentsImpl` chamava close+open,
851
+ * que reabre o mesmo banco. `clearAll()` é o método correto.
852
+ */
853
+ async clearAll() {
854
+ if (this.db !== null) {
855
+ this.db.close();
856
+ this.db = null;
857
+ }
858
+ await new Promise((resolve, reject) => {
859
+ const req = indexedDB.deleteDatabase(this.dbName);
860
+ req.onsuccess = () => resolve();
861
+ req.onerror = () => reject(req.error);
862
+ req.onblocked = () => {
863
+ };
864
+ });
865
+ await this.open();
866
+ }
793
867
  assertOpen() {
794
868
  if (this.db === null) {
795
869
  throw new Error("LocalStore: banco n\xE3o est\xE1 aberto. Chame open() primeiro.");
@@ -997,6 +1071,12 @@ var MemoryStore = class {
997
1071
  async open() {
998
1072
  }
999
1073
  async close() {
1074
+ }
1075
+ /**
1076
+ * Apaga todos os dados em memória — equivalente ao `indexedDB.deleteDatabase`
1077
+ * que `LocalStore.clearAll()` executa em ambientes com IndexedDB.
1078
+ */
1079
+ async clearAll() {
1000
1080
  this._docs.clear();
1001
1081
  this._mutations.clear();
1002
1082
  this._meta.clear();
@@ -1352,7 +1432,71 @@ var MutationQueue = class {
1352
1432
  }
1353
1433
  };
1354
1434
 
1435
+ // src/db/field-value.ts
1436
+ var NEETRU_SENTINEL_KEY = "__neetru__";
1437
+ function serverTimestamp() {
1438
+ return { __neetru__: "serverTimestamp" };
1439
+ }
1440
+ function increment(n) {
1441
+ if (typeof n !== "number" || !Number.isFinite(n)) {
1442
+ throw new TypeError(
1443
+ `increment(n): n deve ser um n\xFAmero finito (recebido: ${String(n)}).`
1444
+ );
1445
+ }
1446
+ return { __neetru__: "increment", operand: n };
1447
+ }
1448
+ function isFieldSentinel(v) {
1449
+ return typeof v === "object" && v !== null && NEETRU_SENTINEL_KEY in v && typeof v[NEETRU_SENTINEL_KEY] === "string";
1450
+ }
1451
+ function isServerTimestampSentinel(v) {
1452
+ return isFieldSentinel(v) && v.__neetru__ === "serverTimestamp";
1453
+ }
1454
+ function isIncrementSentinel(v) {
1455
+ return isFieldSentinel(v) && v.__neetru__ === "increment" && typeof v.operand === "number" && Number.isFinite(v.operand);
1456
+ }
1457
+ function resolveSentinelLocally(value, baseValue) {
1458
+ if (isServerTimestampSentinel(value)) {
1459
+ return Date.now();
1460
+ }
1461
+ if (isIncrementSentinel(value)) {
1462
+ const base = typeof baseValue === "number" && Number.isFinite(baseValue) ? baseValue : 0;
1463
+ return base + value.operand;
1464
+ }
1465
+ return value;
1466
+ }
1467
+ function resolveSentinelsLocally(data, existing) {
1468
+ let touched = false;
1469
+ const out = {};
1470
+ for (const [k, v] of Object.entries(data)) {
1471
+ if (isFieldSentinel(v)) {
1472
+ touched = true;
1473
+ out[k] = resolveSentinelLocally(v, existing?.[k]);
1474
+ } else {
1475
+ out[k] = v;
1476
+ }
1477
+ }
1478
+ return touched ? out : data;
1479
+ }
1480
+ function hasFieldSentinel(data) {
1481
+ for (const v of Object.values(data)) {
1482
+ if (isFieldSentinel(v)) return true;
1483
+ }
1484
+ return false;
1485
+ }
1486
+
1355
1487
  // src/db/offline/sync-engine.ts
1488
+ function hasSentinels(payload) {
1489
+ return payload !== null && hasFieldSentinel(payload);
1490
+ }
1491
+ function sanitizeSentinelPayload(payload) {
1492
+ if (payload === null) return null;
1493
+ if (!hasFieldSentinel(payload)) return payload;
1494
+ const out = {};
1495
+ for (const [k, v] of Object.entries(payload)) {
1496
+ if (!isFieldSentinel(v)) out[k] = v;
1497
+ }
1498
+ return out;
1499
+ }
1356
1500
  var SyncEngine = class {
1357
1501
  _store;
1358
1502
  _queue;
@@ -1458,6 +1602,8 @@ var SyncEngine = class {
1458
1602
  const phase1Ok = await this._phase1Push();
1459
1603
  if (!phase1Ok) {
1460
1604
  this._updateState({ status: "idle" });
1605
+ this._refreshPendingWrites().catch(() => {
1606
+ });
1461
1607
  this._syncing = false;
1462
1608
  return;
1463
1609
  }
@@ -1560,11 +1706,16 @@ var SyncEngine = class {
1560
1706
  }
1561
1707
  return;
1562
1708
  }
1563
- const newData = mut.payload ?? (existing?.data ?? {});
1709
+ const literalPayload = sanitizeSentinelPayload(mut.payload);
1710
+ const newData = literalPayload ?? (existing?.data ?? {});
1564
1711
  const updatedDoc = {
1565
1712
  collection,
1566
1713
  id: docId,
1567
- data: mut.op === "update" ? { ...existing?.data ?? {}, ...newData } : newData,
1714
+ data: mut.op === "update" ? { ...existing?.data ?? {}, ...newData } : (
1715
+ // add/set: se o payload tinha sentinels, o cache otimista (existing.data)
1716
+ // é a fonte de verdade até o pull; senão usa o payload literal.
1717
+ hasSentinels(mut.payload) ? { ...existing?.data ?? {}, ...newData } : newData
1718
+ ),
1568
1719
  meta: {
1569
1720
  serverVersion,
1570
1721
  updatedAtServer: serverTimestamp2,
@@ -1839,7 +1990,7 @@ var SyncEngine = class {
1839
1990
  ownerId: localDoc.meta.ownerId
1840
1991
  }
1841
1992
  };
1842
- const pendingMuts = await this._queue.drain();
1993
+ const pendingMuts = await this._queue.listAll();
1843
1994
  const docMut = pendingMuts.find(
1844
1995
  (m) => m.collection === serverDoc.collection && m.docId === serverDoc.id
1845
1996
  );
@@ -2646,54 +2797,6 @@ var TabCoordinator = class {
2646
2797
 
2647
2798
  // src/db/collection-ref.ts
2648
2799
  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
2697
2800
  function isIndexedDBUnavailable() {
2698
2801
  return typeof indexedDB === "undefined" || typeof window === "undefined";
2699
2802
  }
@@ -3241,8 +3344,13 @@ var NeetruDbDocumentsImpl = class {
3241
3344
  return this._engine.flush();
3242
3345
  }
3243
3346
  async clearCache() {
3244
- await this._store.close();
3245
- await this._store.open();
3347
+ const storeWithClear = this._store;
3348
+ if (typeof storeWithClear.clearAll === "function") {
3349
+ await storeWithClear.clearAll();
3350
+ } else {
3351
+ await this._store.close();
3352
+ await this._store.open();
3353
+ }
3246
3354
  }
3247
3355
  async getConflicts() {
3248
3356
  return this._store.listConflicts();