@neetru/sdk 3.1.0 → 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 (95) hide show
  1. package/dist/auth.cjs +159 -15
  2. package/dist/auth.cjs.map +1 -1
  3. package/dist/auth.d.cts +1 -1
  4. package/dist/auth.d.ts +1 -1
  5. package/dist/auth.mjs +159 -15
  6. package/dist/auth.mjs.map +1 -1
  7. package/dist/catalog.cjs +50 -2
  8. package/dist/catalog.cjs.map +1 -1
  9. package/dist/catalog.d.cts +1 -1
  10. package/dist/catalog.d.ts +1 -1
  11. package/dist/catalog.mjs +50 -2
  12. package/dist/catalog.mjs.map +1 -1
  13. package/dist/checkout.cjs +52 -3
  14. package/dist/checkout.cjs.map +1 -1
  15. package/dist/checkout.d.cts +1 -1
  16. package/dist/checkout.d.ts +1 -1
  17. package/dist/checkout.mjs +52 -3
  18. package/dist/checkout.mjs.map +1 -1
  19. package/dist/db-react.cjs +3 -1
  20. package/dist/db-react.cjs.map +1 -1
  21. package/dist/db-react.d.cts +1 -1
  22. package/dist/db-react.d.ts +1 -1
  23. package/dist/db-react.mjs +3 -1
  24. package/dist/db-react.mjs.map +1 -1
  25. package/dist/db.cjs +93 -6
  26. package/dist/db.cjs.map +1 -1
  27. package/dist/db.d.cts +1 -1
  28. package/dist/db.d.ts +1 -1
  29. package/dist/db.mjs +93 -6
  30. package/dist/db.mjs.map +1 -1
  31. package/dist/entitlements.cjs +50 -2
  32. package/dist/entitlements.cjs.map +1 -1
  33. package/dist/entitlements.d.cts +1 -1
  34. package/dist/entitlements.d.ts +1 -1
  35. package/dist/entitlements.mjs +50 -2
  36. package/dist/entitlements.mjs.map +1 -1
  37. package/dist/errors.cjs.map +1 -1
  38. package/dist/errors.d.cts +3 -1
  39. package/dist/errors.d.ts +3 -1
  40. package/dist/errors.mjs.map +1 -1
  41. package/dist/firestore-compat.cjs +14 -2
  42. package/dist/firestore-compat.cjs.map +1 -1
  43. package/dist/firestore-compat.d.cts +28 -3
  44. package/dist/firestore-compat.d.ts +28 -3
  45. package/dist/firestore-compat.mjs +14 -2
  46. package/dist/firestore-compat.mjs.map +1 -1
  47. package/dist/index.cjs +195 -16
  48. package/dist/index.cjs.map +1 -1
  49. package/dist/index.d.cts +9 -3
  50. package/dist/index.d.ts +9 -3
  51. package/dist/index.mjs +194 -17
  52. package/dist/index.mjs.map +1 -1
  53. package/dist/mocks.cjs.map +1 -1
  54. package/dist/mocks.d.cts +1 -1
  55. package/dist/mocks.d.ts +1 -1
  56. package/dist/mocks.mjs.map +1 -1
  57. package/dist/notifications.cjs +50 -2
  58. package/dist/notifications.cjs.map +1 -1
  59. package/dist/notifications.d.cts +1 -1
  60. package/dist/notifications.d.ts +1 -1
  61. package/dist/notifications.mjs +50 -2
  62. package/dist/notifications.mjs.map +1 -1
  63. package/dist/react.cjs +2 -3
  64. package/dist/react.cjs.map +1 -1
  65. package/dist/react.d.cts +22 -4
  66. package/dist/react.d.ts +22 -4
  67. package/dist/react.mjs +2 -3
  68. package/dist/react.mjs.map +1 -1
  69. package/dist/support.cjs +50 -2
  70. package/dist/support.cjs.map +1 -1
  71. package/dist/support.d.cts +1 -1
  72. package/dist/support.d.ts +1 -1
  73. package/dist/support.mjs +50 -2
  74. package/dist/support.mjs.map +1 -1
  75. package/dist/telemetry.cjs +65 -4
  76. package/dist/telemetry.cjs.map +1 -1
  77. package/dist/telemetry.d.cts +1 -1
  78. package/dist/telemetry.d.ts +1 -1
  79. package/dist/telemetry.mjs +65 -4
  80. package/dist/telemetry.mjs.map +1 -1
  81. package/dist/{types-BRv8wBxX.d.ts → types-B3XFHD4A.d.ts} +60 -1
  82. package/dist/{types-nwErcRX8.d.cts → types-DePdSU3P.d.cts} +60 -1
  83. package/dist/usage.cjs +50 -2
  84. package/dist/usage.cjs.map +1 -1
  85. package/dist/usage.d.cts +1 -1
  86. package/dist/usage.d.ts +1 -1
  87. package/dist/usage.mjs +50 -2
  88. package/dist/usage.mjs.map +1 -1
  89. package/dist/webhooks.cjs +83 -2
  90. package/dist/webhooks.cjs.map +1 -1
  91. package/dist/webhooks.d.cts +1 -1
  92. package/dist/webhooks.d.ts +1 -1
  93. package/dist/webhooks.mjs +83 -3
  94. package/dist/webhooks.mjs.map +1 -1
  95. package/package.json +1 -1
package/dist/auth.cjs CHANGED
@@ -53,8 +53,47 @@ var init_idempotency = __esm({
53
53
  // src/http.ts
54
54
  var http_exports = {};
55
55
  __export(http_exports, {
56
+ checkCoreVersionCompat: () => checkCoreVersionCompat,
56
57
  httpRequest: () => httpRequest
57
58
  });
59
+ function stripRevision(v) {
60
+ return v.split("+")[0] ?? v;
61
+ }
62
+ function compareCalVer(a, b) {
63
+ const partsA = stripRevision(a).split(".").map(Number);
64
+ const partsB = stripRevision(b).split(".").map(Number);
65
+ const len = Math.max(partsA.length, partsB.length);
66
+ for (let i = 0; i < len; i++) {
67
+ const pa = partsA[i] ?? 0;
68
+ const pb = partsB[i] ?? 0;
69
+ if (pa !== pb) return pa - pb;
70
+ }
71
+ return 0;
72
+ }
73
+ function getSdkMajor() {
74
+ try {
75
+ const ver = globalThis.__NEETRU_SDK_VERSION__;
76
+ if (ver) return parseInt(ver.split(".")[0] ?? "3", 10);
77
+ } catch {
78
+ }
79
+ return 3;
80
+ }
81
+ function checkCoreVersionCompat(coreVersion) {
82
+ if (!coreVersion) return;
83
+ const sdkMajor = getSdkMajor();
84
+ const minCore = COMPAT_MATRIX[sdkMajor];
85
+ if (!minCore) return;
86
+ const stripped = stripRevision(coreVersion);
87
+ if (compareCalVer(stripped, minCore) < 0) {
88
+ const key = `${sdkMajor}:${stripped}`;
89
+ if (!_warnedCoreTooOld.has(key)) {
90
+ _warnedCoreTooOld.add(key);
91
+ console.warn(
92
+ `[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.`
93
+ );
94
+ }
95
+ }
96
+ }
58
97
  function backoffMs(attempt) {
59
98
  const base = 200 * Math.pow(4, attempt);
60
99
  const jitter = base * 0.2 * (Math.random() * 2 - 1);
@@ -63,11 +102,11 @@ function backoffMs(attempt) {
63
102
  function parseRetryAfter(value) {
64
103
  if (!value) return null;
65
104
  const secs = Number(value);
66
- if (Number.isFinite(secs) && secs >= 0) return Math.round(secs * 1e3);
105
+ if (Number.isFinite(secs) && secs >= 0) return Math.min(Math.round(secs * 1e3), MAX_RETRY_AFTER_MS);
67
106
  const dateMs = Date.parse(value);
68
107
  if (Number.isFinite(dateMs)) {
69
108
  const delta = dateMs - Date.now();
70
- if (delta > 0) return delta;
109
+ if (delta > 0) return Math.min(delta, MAX_RETRY_AFTER_MS);
71
110
  }
72
111
  return null;
73
112
  }
@@ -78,6 +117,7 @@ function statusToCode(status) {
78
117
  if (status === 401) return "unauthorized";
79
118
  if (status === 403) return "forbidden";
80
119
  if (status === 404) return "not_found";
120
+ if (status === 409) return "conflict";
81
121
  if (status === 422 || status === 400) return "validation_failed";
82
122
  if (status === 429) return "rate_limited";
83
123
  if (status >= 500) return "server_error";
@@ -149,6 +189,8 @@ async function httpRequest(config, opts) {
149
189
  }
150
190
  const requestId = res.headers.get("x-request-id") ?? res.headers.get("x-correlation-id") ?? void 0;
151
191
  if (res.ok) {
192
+ const coreVersion = res.headers.get("x-neetru-version");
193
+ if (coreVersion) checkCoreVersionCompat(coreVersion);
152
194
  const parsed = await safeJson(res);
153
195
  return parsed;
154
196
  }
@@ -177,11 +219,17 @@ async function httpRequest(config, opts) {
177
219
  }
178
220
  throw lastError ?? new NeetruError("unknown", "unexpected httpRequest exit");
179
221
  }
180
- var DEFAULT_RETRIES_READ, DEFAULT_RETRIES_WRITE, READ_METHODS, RETRYABLE_CODES;
222
+ var COMPAT_MATRIX, _warnedCoreTooOld, DEFAULT_RETRIES_READ, DEFAULT_RETRIES_WRITE, READ_METHODS, RETRYABLE_CODES, MAX_RETRY_AFTER_MS;
181
223
  var init_http = __esm({
182
224
  "src/http.ts"() {
183
225
  init_errors();
184
226
  init_idempotency();
227
+ COMPAT_MATRIX = {
228
+ // SDK major 3.x requer Core ≥ 2026.05.1 (suporte a X-Neetru-Version header,
229
+ // runTransaction write-after-read, verifyWebhookRequest, webhooks 3.0+)
230
+ 3: "2026.05.1"
231
+ };
232
+ _warnedCoreTooOld = /* @__PURE__ */ new Set();
185
233
  DEFAULT_RETRIES_READ = 2;
186
234
  DEFAULT_RETRIES_WRITE = 0;
187
235
  READ_METHODS = /* @__PURE__ */ new Set(["GET", "HEAD"]);
@@ -190,6 +238,7 @@ var init_http = __esm({
190
238
  "server_error",
191
239
  "network_error"
192
240
  ]);
241
+ MAX_RETRY_AFTER_MS = 6e4;
193
242
  }
194
243
  });
195
244
 
@@ -891,11 +940,11 @@ var TRACK_MAX_QUEUE = 50;
891
940
  function createTelemetryNamespace(config) {
892
941
  const queue = [];
893
942
  let flushTimer = null;
943
+ let _inflightDrain = null;
894
944
  async function drainQueue() {
895
945
  if (queue.length === 0) return;
896
946
  const batch = queue.splice(0, queue.length);
897
- flushTimer = null;
898
- await Promise.allSettled(
947
+ const work = Promise.allSettled(
899
948
  batch.map(async (ev) => {
900
949
  try {
901
950
  const body = { name: ev.name };
@@ -917,6 +966,14 @@ function createTelemetryNamespace(config) {
917
966
  }
918
967
  })
919
968
  );
969
+ _inflightDrain = work.then(() => {
970
+ _inflightDrain = null;
971
+ });
972
+ await work;
973
+ flushTimer = null;
974
+ if (queue.length > 0) {
975
+ scheduleFlush();
976
+ }
920
977
  }
921
978
  function scheduleFlush() {
922
979
  if (flushTimer) return;
@@ -979,6 +1036,10 @@ function createTelemetryNamespace(config) {
979
1036
  if (input.name.length > 128) return;
980
1037
  queue.push(input);
981
1038
  if (queue.length >= TRACK_MAX_QUEUE) {
1039
+ if (flushTimer) {
1040
+ clearTimeout(flushTimer);
1041
+ flushTimer = null;
1042
+ }
982
1043
  void drainQueue();
983
1044
  } else {
984
1045
  scheduleFlush();
@@ -993,6 +1054,7 @@ function createTelemetryNamespace(config) {
993
1054
  clearTimeout(flushTimer);
994
1055
  flushTimer = null;
995
1056
  }
1057
+ if (_inflightDrain) await _inflightDrain;
996
1058
  await drainQueue();
997
1059
  },
998
1060
  /**
@@ -1481,6 +1543,31 @@ var LocalStore = class {
1481
1543
  this.db?.close();
1482
1544
  this.db = null;
1483
1545
  }
1546
+ /**
1547
+ * Apaga completamente o banco IndexedDB e o recria do zero.
1548
+ *
1549
+ * Ao contrário de `close()` + `open()` (que reabre o banco existente sem
1550
+ * apagar dados), `clearAll()` chama `indexedDB.deleteDatabase(dbName)` e
1551
+ * só então chama `open()` — garantindo que nenhum documento, mutação ou
1552
+ * metadado sobrevive.
1553
+ *
1554
+ * BUG fix: `clearCache()` em `NeetruDbDocumentsImpl` chamava close+open,
1555
+ * que reabre o mesmo banco. `clearAll()` é o método correto.
1556
+ */
1557
+ async clearAll() {
1558
+ if (this.db !== null) {
1559
+ this.db.close();
1560
+ this.db = null;
1561
+ }
1562
+ await new Promise((resolve, reject) => {
1563
+ const req = indexedDB.deleteDatabase(this.dbName);
1564
+ req.onsuccess = () => resolve();
1565
+ req.onerror = () => reject(req.error);
1566
+ req.onblocked = () => {
1567
+ };
1568
+ });
1569
+ await this.open();
1570
+ }
1484
1571
  assertOpen() {
1485
1572
  if (this.db === null) {
1486
1573
  throw new Error("LocalStore: banco n\xE3o est\xE1 aberto. Chame open() primeiro.");
@@ -1688,6 +1775,12 @@ var MemoryStore = class {
1688
1775
  async open() {
1689
1776
  }
1690
1777
  async close() {
1778
+ }
1779
+ /**
1780
+ * Apaga todos os dados em memória — equivalente ao `indexedDB.deleteDatabase`
1781
+ * que `LocalStore.clearAll()` executa em ambientes com IndexedDB.
1782
+ */
1783
+ async clearAll() {
1691
1784
  this._docs.clear();
1692
1785
  this._mutations.clear();
1693
1786
  this._meta.clear();
@@ -2202,6 +2295,8 @@ var SyncEngine = class {
2202
2295
  const phase1Ok = await this._phase1Push();
2203
2296
  if (!phase1Ok) {
2204
2297
  this._updateState({ status: "idle" });
2298
+ this._refreshPendingWrites().catch(() => {
2299
+ });
2205
2300
  this._syncing = false;
2206
2301
  return;
2207
2302
  }
@@ -2588,7 +2683,7 @@ var SyncEngine = class {
2588
2683
  ownerId: localDoc.meta.ownerId
2589
2684
  }
2590
2685
  };
2591
- const pendingMuts = await this._queue.drain();
2686
+ const pendingMuts = await this._queue.listAll();
2592
2687
  const docMut = pendingMuts.find(
2593
2688
  (m) => m.collection === serverDoc.collection && m.docId === serverDoc.id
2594
2689
  );
@@ -3942,8 +4037,13 @@ var NeetruDbDocumentsImpl = class {
3942
4037
  return this._engine.flush();
3943
4038
  }
3944
4039
  async clearCache() {
3945
- await this._store.close();
3946
- await this._store.open();
4040
+ const storeWithClear = this._store;
4041
+ if (typeof storeWithClear.clearAll === "function") {
4042
+ await storeWithClear.clearAll();
4043
+ } else {
4044
+ await this._store.close();
4045
+ await this._store.open();
4046
+ }
3947
4047
  }
3948
4048
  async getConflicts() {
3949
4049
  return this._store.listConflicts();
@@ -4830,7 +4930,8 @@ function createHttpCheckoutNamespace(config) {
4830
4930
  method: "POST",
4831
4931
  path: "/api/v1/checkout/intents",
4832
4932
  body,
4833
- requireAuth: true
4933
+ requireAuth: true,
4934
+ idempotencyKey: true
4834
4935
  });
4835
4936
  const result = parseStartResponse(raw);
4836
4937
  const shouldRedirect = input.autoRedirect !== false;
@@ -5585,6 +5686,7 @@ function createOidcAuthNamespace(config) {
5585
5686
  const STORAGE_KEY = "neetru_id_token";
5586
5687
  const listeners = /* @__PURE__ */ new Set();
5587
5688
  let cachedUser = null;
5689
+ let _expiryTimerId = null;
5588
5690
  function getStorage() {
5589
5691
  try {
5590
5692
  return typeof globalThis.localStorage !== "undefined" ? globalThis.localStorage : null;
@@ -5607,6 +5709,9 @@ function createOidcAuthNamespace(config) {
5607
5709
  function tokenToUser(token) {
5608
5710
  const payload = decodeJwtPayload(token);
5609
5711
  if (!payload || typeof payload.sub !== "string") return null;
5712
+ if (typeof payload.exp === "number" && payload.exp * 1e3 < Date.now()) {
5713
+ return null;
5714
+ }
5610
5715
  return {
5611
5716
  uid: payload.sub,
5612
5717
  email: typeof payload.email === "string" ? payload.email : "",
@@ -5620,12 +5725,28 @@ function createOidcAuthNamespace(config) {
5620
5725
  };
5621
5726
  }
5622
5727
  function loadCachedUser() {
5623
- if (cachedUser) return cachedUser;
5728
+ if (cachedUser) {
5729
+ const exp = cachedUser.exp;
5730
+ if (typeof exp === "number" && exp * 1e3 < Date.now()) {
5731
+ cachedUser = null;
5732
+ const storage2 = getStorage();
5733
+ storage2?.removeItem(STORAGE_KEY);
5734
+ notify();
5735
+ return null;
5736
+ }
5737
+ return cachedUser;
5738
+ }
5624
5739
  const storage = getStorage();
5625
5740
  if (!storage) return null;
5626
5741
  const token = storage.getItem(STORAGE_KEY);
5627
5742
  if (!token) return null;
5628
- cachedUser = tokenToUser(token);
5743
+ const user = tokenToUser(token);
5744
+ if (!user) {
5745
+ storage.removeItem(STORAGE_KEY);
5746
+ return null;
5747
+ }
5748
+ cachedUser = user;
5749
+ scheduleExpiryTimer(user);
5629
5750
  return cachedUser;
5630
5751
  }
5631
5752
  function notify() {
@@ -5636,6 +5757,23 @@ function createOidcAuthNamespace(config) {
5636
5757
  }
5637
5758
  }
5638
5759
  }
5760
+ function cancelExpiryTimer() {
5761
+ if (_expiryTimerId !== null) {
5762
+ clearTimeout(_expiryTimerId);
5763
+ _expiryTimerId = null;
5764
+ }
5765
+ }
5766
+ function scheduleExpiryTimer(user) {
5767
+ cancelExpiryTimer();
5768
+ const exp = user.exp;
5769
+ if (typeof exp !== "number") return;
5770
+ const msUntilExpiry = exp * 1e3 - Date.now();
5771
+ if (msUntilExpiry <= 0) return;
5772
+ _expiryTimerId = setTimeout(() => {
5773
+ _expiryTimerId = null;
5774
+ loadCachedUser();
5775
+ }, msUntilExpiry + 1);
5776
+ }
5639
5777
  return {
5640
5778
  async signIn(options) {
5641
5779
  if (typeof globalThis.location !== "undefined" && typeof globalThis.location.assign === "function") {
@@ -5663,18 +5801,24 @@ function createOidcAuthNamespace(config) {
5663
5801
  },
5664
5802
  async signOut() {
5665
5803
  const storage = getStorage();
5804
+ const storedToken = storage?.getItem(STORAGE_KEY) ?? null;
5666
5805
  if (storage) storage.removeItem(STORAGE_KEY);
5806
+ cancelExpiryTimer();
5667
5807
  cachedUser = null;
5668
5808
  const overrideAuthUrl = typeof globalThis.NEETRU_AUTH_URL === "string" ? globalThis.NEETRU_AUTH_URL : null;
5669
5809
  const idpOrigin = overrideAuthUrl ?? config.baseUrl.replace(/^https?:\/\/api\./, "https://auth.");
5670
5810
  const revokeUrl = `${idpOrigin}/api/v1/oauth/revoke`;
5811
+ const revokeBody = storedToken ? JSON.stringify({ token: storedToken }) : null;
5671
5812
  let revokeError = null;
5672
5813
  for (let attempt = 0; attempt < 2; attempt++) {
5673
5814
  try {
5674
- await config.fetch(revokeUrl, {
5675
- method: "POST",
5676
- headers: { "content-type": "application/json" }
5677
- });
5815
+ if (revokeBody) {
5816
+ await config.fetch(revokeUrl, {
5817
+ method: "POST",
5818
+ headers: { "content-type": "application/json" },
5819
+ body: revokeBody
5820
+ });
5821
+ }
5678
5822
  revokeError = null;
5679
5823
  break;
5680
5824
  } catch (err) {