@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.
- package/CHANGELOG.md +554 -530
- package/dist/auth.cjs +219 -54
- package/dist/auth.cjs.map +1 -1
- package/dist/auth.d.cts +1 -2
- package/dist/auth.d.ts +1 -2
- package/dist/auth.mjs +219 -54
- package/dist/auth.mjs.map +1 -1
- package/dist/catalog.cjs +50 -2
- package/dist/catalog.cjs.map +1 -1
- package/dist/catalog.d.cts +1 -2
- package/dist/catalog.d.ts +1 -2
- package/dist/catalog.mjs +50 -2
- package/dist/catalog.mjs.map +1 -1
- package/dist/checkout.cjs +52 -3
- package/dist/checkout.cjs.map +1 -1
- package/dist/checkout.d.cts +1 -2
- package/dist/checkout.d.ts +1 -2
- package/dist/checkout.mjs +52 -3
- package/dist/checkout.mjs.map +1 -1
- package/dist/db-react.cjs +18 -6
- package/dist/db-react.cjs.map +1 -1
- package/dist/db-react.d.cts +35 -13
- package/dist/db-react.d.ts +35 -13
- package/dist/db-react.mjs +18 -6
- package/dist/db-react.mjs.map +1 -1
- package/dist/db.cjs +164 -56
- package/dist/db.cjs.map +1 -1
- package/dist/db.d.cts +1 -2
- package/dist/db.d.ts +1 -2
- package/dist/db.mjs +164 -56
- package/dist/db.mjs.map +1 -1
- package/dist/entitlements.cjs +50 -2
- package/dist/entitlements.cjs.map +1 -1
- package/dist/entitlements.d.cts +1 -2
- package/dist/entitlements.d.ts +1 -2
- package/dist/entitlements.mjs +50 -2
- package/dist/entitlements.mjs.map +1 -1
- package/dist/errors.cjs.map +1 -1
- package/dist/errors.d.cts +3 -1
- package/dist/errors.d.ts +3 -1
- package/dist/errors.mjs.map +1 -1
- package/dist/firestore-compat.cjs +411 -0
- package/dist/firestore-compat.cjs.map +1 -0
- package/dist/firestore-compat.d.cts +319 -0
- package/dist/firestore-compat.d.ts +319 -0
- package/dist/firestore-compat.mjs +383 -0
- package/dist/firestore-compat.mjs.map +1 -0
- package/dist/index.cjs +266 -66
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +9 -4
- package/dist/index.d.ts +9 -4
- package/dist/index.mjs +265 -67
- package/dist/index.mjs.map +1 -1
- package/dist/mocks.cjs.map +1 -1
- package/dist/mocks.d.cts +1 -2
- package/dist/mocks.d.ts +1 -2
- package/dist/mocks.mjs.map +1 -1
- package/dist/notifications.cjs +50 -2
- package/dist/notifications.cjs.map +1 -1
- package/dist/notifications.d.cts +1 -2
- package/dist/notifications.d.ts +1 -2
- package/dist/notifications.mjs +50 -2
- package/dist/notifications.mjs.map +1 -1
- package/dist/react.cjs +268 -2
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +135 -3
- package/dist/react.d.ts +135 -3
- package/dist/react.mjs +266 -3
- package/dist/react.mjs.map +1 -1
- package/dist/support.cjs +50 -2
- package/dist/support.cjs.map +1 -1
- package/dist/support.d.cts +1 -2
- package/dist/support.d.ts +1 -2
- package/dist/support.mjs +50 -2
- package/dist/support.mjs.map +1 -1
- package/dist/telemetry.cjs +65 -4
- package/dist/telemetry.cjs.map +1 -1
- package/dist/telemetry.d.cts +1 -2
- package/dist/telemetry.d.ts +1 -2
- package/dist/telemetry.mjs +65 -4
- package/dist/telemetry.mjs.map +1 -1
- package/dist/{types-D7zVkO3n.d.ts → types-B3XFHD4A.d.ts} +640 -2
- package/dist/{types-Dc4bjrrt.d.cts → types-DePdSU3P.d.cts} +640 -2
- package/dist/usage.cjs +50 -2
- package/dist/usage.cjs.map +1 -1
- package/dist/usage.d.cts +1 -2
- package/dist/usage.d.ts +1 -2
- package/dist/usage.mjs +50 -2
- package/dist/usage.mjs.map +1 -1
- package/dist/webhooks.cjs +83 -2
- package/dist/webhooks.cjs.map +1 -1
- package/dist/webhooks.d.cts +1 -2
- package/dist/webhooks.d.ts +1 -2
- package/dist/webhooks.mjs +83 -3
- package/dist/webhooks.mjs.map +1 -1
- package/package.json +156 -151
- package/dist/collection-ref-BDdfD87k.d.cts +0 -581
- package/dist/collection-ref-BDdfD87k.d.ts +0 -581
package/dist/index.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 exports.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
|
-
|
|
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();
|
|
@@ -2043,7 +2136,71 @@ var MutationQueue = class {
|
|
|
2043
2136
|
}
|
|
2044
2137
|
};
|
|
2045
2138
|
|
|
2139
|
+
// src/db/field-value.ts
|
|
2140
|
+
var NEETRU_SENTINEL_KEY = "__neetru__";
|
|
2141
|
+
function serverTimestamp() {
|
|
2142
|
+
return { __neetru__: "serverTimestamp" };
|
|
2143
|
+
}
|
|
2144
|
+
function increment(n) {
|
|
2145
|
+
if (typeof n !== "number" || !Number.isFinite(n)) {
|
|
2146
|
+
throw new TypeError(
|
|
2147
|
+
`increment(n): n deve ser um n\xFAmero finito (recebido: ${String(n)}).`
|
|
2148
|
+
);
|
|
2149
|
+
}
|
|
2150
|
+
return { __neetru__: "increment", operand: n };
|
|
2151
|
+
}
|
|
2152
|
+
function isFieldSentinel(v) {
|
|
2153
|
+
return typeof v === "object" && v !== null && NEETRU_SENTINEL_KEY in v && typeof v[NEETRU_SENTINEL_KEY] === "string";
|
|
2154
|
+
}
|
|
2155
|
+
function isServerTimestampSentinel(v) {
|
|
2156
|
+
return isFieldSentinel(v) && v.__neetru__ === "serverTimestamp";
|
|
2157
|
+
}
|
|
2158
|
+
function isIncrementSentinel(v) {
|
|
2159
|
+
return isFieldSentinel(v) && v.__neetru__ === "increment" && typeof v.operand === "number" && Number.isFinite(v.operand);
|
|
2160
|
+
}
|
|
2161
|
+
function resolveSentinelLocally(value, baseValue) {
|
|
2162
|
+
if (isServerTimestampSentinel(value)) {
|
|
2163
|
+
return Date.now();
|
|
2164
|
+
}
|
|
2165
|
+
if (isIncrementSentinel(value)) {
|
|
2166
|
+
const base = typeof baseValue === "number" && Number.isFinite(baseValue) ? baseValue : 0;
|
|
2167
|
+
return base + value.operand;
|
|
2168
|
+
}
|
|
2169
|
+
return value;
|
|
2170
|
+
}
|
|
2171
|
+
function resolveSentinelsLocally(data, existing) {
|
|
2172
|
+
let touched = false;
|
|
2173
|
+
const out = {};
|
|
2174
|
+
for (const [k, v] of Object.entries(data)) {
|
|
2175
|
+
if (isFieldSentinel(v)) {
|
|
2176
|
+
touched = true;
|
|
2177
|
+
out[k] = resolveSentinelLocally(v, existing?.[k]);
|
|
2178
|
+
} else {
|
|
2179
|
+
out[k] = v;
|
|
2180
|
+
}
|
|
2181
|
+
}
|
|
2182
|
+
return touched ? out : data;
|
|
2183
|
+
}
|
|
2184
|
+
function hasFieldSentinel(data) {
|
|
2185
|
+
for (const v of Object.values(data)) {
|
|
2186
|
+
if (isFieldSentinel(v)) return true;
|
|
2187
|
+
}
|
|
2188
|
+
return false;
|
|
2189
|
+
}
|
|
2190
|
+
|
|
2046
2191
|
// src/db/offline/sync-engine.ts
|
|
2192
|
+
function hasSentinels(payload) {
|
|
2193
|
+
return payload !== null && hasFieldSentinel(payload);
|
|
2194
|
+
}
|
|
2195
|
+
function sanitizeSentinelPayload(payload) {
|
|
2196
|
+
if (payload === null) return null;
|
|
2197
|
+
if (!hasFieldSentinel(payload)) return payload;
|
|
2198
|
+
const out = {};
|
|
2199
|
+
for (const [k, v] of Object.entries(payload)) {
|
|
2200
|
+
if (!isFieldSentinel(v)) out[k] = v;
|
|
2201
|
+
}
|
|
2202
|
+
return out;
|
|
2203
|
+
}
|
|
2047
2204
|
var SyncEngine = class {
|
|
2048
2205
|
_store;
|
|
2049
2206
|
_queue;
|
|
@@ -2149,6 +2306,8 @@ var SyncEngine = class {
|
|
|
2149
2306
|
const phase1Ok = await this._phase1Push();
|
|
2150
2307
|
if (!phase1Ok) {
|
|
2151
2308
|
this._updateState({ status: "idle" });
|
|
2309
|
+
this._refreshPendingWrites().catch(() => {
|
|
2310
|
+
});
|
|
2152
2311
|
this._syncing = false;
|
|
2153
2312
|
return;
|
|
2154
2313
|
}
|
|
@@ -2251,11 +2410,16 @@ var SyncEngine = class {
|
|
|
2251
2410
|
}
|
|
2252
2411
|
return;
|
|
2253
2412
|
}
|
|
2254
|
-
const
|
|
2413
|
+
const literalPayload = sanitizeSentinelPayload(mut.payload);
|
|
2414
|
+
const newData = literalPayload ?? (existing?.data ?? {});
|
|
2255
2415
|
const updatedDoc = {
|
|
2256
2416
|
collection,
|
|
2257
2417
|
id: docId,
|
|
2258
|
-
data: mut.op === "update" ? { ...existing?.data ?? {}, ...newData } :
|
|
2418
|
+
data: mut.op === "update" ? { ...existing?.data ?? {}, ...newData } : (
|
|
2419
|
+
// add/set: se o payload tinha sentinels, o cache otimista (existing.data)
|
|
2420
|
+
// é a fonte de verdade até o pull; senão usa o payload literal.
|
|
2421
|
+
hasSentinels(mut.payload) ? { ...existing?.data ?? {}, ...newData } : newData
|
|
2422
|
+
),
|
|
2259
2423
|
meta: {
|
|
2260
2424
|
serverVersion,
|
|
2261
2425
|
updatedAtServer: serverTimestamp2,
|
|
@@ -2530,7 +2694,7 @@ var SyncEngine = class {
|
|
|
2530
2694
|
ownerId: localDoc.meta.ownerId
|
|
2531
2695
|
}
|
|
2532
2696
|
};
|
|
2533
|
-
const pendingMuts = await this._queue.
|
|
2697
|
+
const pendingMuts = await this._queue.listAll();
|
|
2534
2698
|
const docMut = pendingMuts.find(
|
|
2535
2699
|
(m) => m.collection === serverDoc.collection && m.docId === serverDoc.id
|
|
2536
2700
|
);
|
|
@@ -3337,54 +3501,6 @@ var TabCoordinator = class {
|
|
|
3337
3501
|
|
|
3338
3502
|
// src/db/collection-ref.ts
|
|
3339
3503
|
init_db_errors();
|
|
3340
|
-
|
|
3341
|
-
// src/db/field-value.ts
|
|
3342
|
-
var NEETRU_SENTINEL_KEY = "__neetru__";
|
|
3343
|
-
function serverTimestamp() {
|
|
3344
|
-
return { __neetru__: "serverTimestamp" };
|
|
3345
|
-
}
|
|
3346
|
-
function increment(n) {
|
|
3347
|
-
if (typeof n !== "number" || !Number.isFinite(n)) {
|
|
3348
|
-
throw new TypeError(
|
|
3349
|
-
`increment(n): n deve ser um n\xFAmero finito (recebido: ${String(n)}).`
|
|
3350
|
-
);
|
|
3351
|
-
}
|
|
3352
|
-
return { __neetru__: "increment", operand: n };
|
|
3353
|
-
}
|
|
3354
|
-
function isFieldSentinel(v) {
|
|
3355
|
-
return typeof v === "object" && v !== null && NEETRU_SENTINEL_KEY in v && typeof v[NEETRU_SENTINEL_KEY] === "string";
|
|
3356
|
-
}
|
|
3357
|
-
function isServerTimestampSentinel(v) {
|
|
3358
|
-
return isFieldSentinel(v) && v.__neetru__ === "serverTimestamp";
|
|
3359
|
-
}
|
|
3360
|
-
function isIncrementSentinel(v) {
|
|
3361
|
-
return isFieldSentinel(v) && v.__neetru__ === "increment" && typeof v.operand === "number" && Number.isFinite(v.operand);
|
|
3362
|
-
}
|
|
3363
|
-
function resolveSentinelLocally(value, baseValue) {
|
|
3364
|
-
if (isServerTimestampSentinel(value)) {
|
|
3365
|
-
return Date.now();
|
|
3366
|
-
}
|
|
3367
|
-
if (isIncrementSentinel(value)) {
|
|
3368
|
-
const base = typeof baseValue === "number" && Number.isFinite(baseValue) ? baseValue : 0;
|
|
3369
|
-
return base + value.operand;
|
|
3370
|
-
}
|
|
3371
|
-
return value;
|
|
3372
|
-
}
|
|
3373
|
-
function resolveSentinelsLocally(data, existing) {
|
|
3374
|
-
let touched = false;
|
|
3375
|
-
const out = {};
|
|
3376
|
-
for (const [k, v] of Object.entries(data)) {
|
|
3377
|
-
if (isFieldSentinel(v)) {
|
|
3378
|
-
touched = true;
|
|
3379
|
-
out[k] = resolveSentinelLocally(v, existing?.[k]);
|
|
3380
|
-
} else {
|
|
3381
|
-
out[k] = v;
|
|
3382
|
-
}
|
|
3383
|
-
}
|
|
3384
|
-
return touched ? out : data;
|
|
3385
|
-
}
|
|
3386
|
-
|
|
3387
|
-
// src/db/collection-ref.ts
|
|
3388
3504
|
function isIndexedDBUnavailable() {
|
|
3389
3505
|
return typeof indexedDB === "undefined" || typeof window === "undefined";
|
|
3390
3506
|
}
|
|
@@ -3932,8 +4048,13 @@ var NeetruDbDocumentsImpl = class {
|
|
|
3932
4048
|
return this._engine.flush();
|
|
3933
4049
|
}
|
|
3934
4050
|
async clearCache() {
|
|
3935
|
-
|
|
3936
|
-
|
|
4051
|
+
const storeWithClear = this._store;
|
|
4052
|
+
if (typeof storeWithClear.clearAll === "function") {
|
|
4053
|
+
await storeWithClear.clearAll();
|
|
4054
|
+
} else {
|
|
4055
|
+
await this._store.close();
|
|
4056
|
+
await this._store.open();
|
|
4057
|
+
}
|
|
3937
4058
|
}
|
|
3938
4059
|
async getConflicts() {
|
|
3939
4060
|
return this._store.listConflicts();
|
|
@@ -4820,7 +4941,8 @@ function createHttpCheckoutNamespace(config) {
|
|
|
4820
4941
|
method: "POST",
|
|
4821
4942
|
path: "/api/v1/checkout/intents",
|
|
4822
4943
|
body,
|
|
4823
|
-
requireAuth: true
|
|
4944
|
+
requireAuth: true,
|
|
4945
|
+
idempotencyKey: true
|
|
4824
4946
|
});
|
|
4825
4947
|
const result = parseStartResponse(raw);
|
|
4826
4948
|
const shouldRedirect = input.autoRedirect !== false;
|
|
@@ -4951,6 +5073,38 @@ async function verifyWebhookSignature(payload, signature, secret) {
|
|
|
4951
5073
|
}
|
|
4952
5074
|
return diff === 0;
|
|
4953
5075
|
}
|
|
5076
|
+
async function verifyWebhookRequest(payload, headers, secret, options = {}) {
|
|
5077
|
+
const toleranceMs = options.toleranceMs ?? 3e5;
|
|
5078
|
+
const getHeader = (name) => {
|
|
5079
|
+
if (typeof headers.get === "function") {
|
|
5080
|
+
return headers.get(name);
|
|
5081
|
+
}
|
|
5082
|
+
const rec = headers;
|
|
5083
|
+
const lower = name.toLowerCase();
|
|
5084
|
+
for (const key of Object.keys(rec)) {
|
|
5085
|
+
if (key.toLowerCase() === lower) return rec[key] ?? null;
|
|
5086
|
+
}
|
|
5087
|
+
return null;
|
|
5088
|
+
};
|
|
5089
|
+
const tsHeader = getHeader("x-neetru-timestamp");
|
|
5090
|
+
if (!tsHeader) {
|
|
5091
|
+
return { ok: false, reason: "timestamp_missing" };
|
|
5092
|
+
}
|
|
5093
|
+
const tsMs = Number(tsHeader);
|
|
5094
|
+
if (!Number.isFinite(tsMs)) {
|
|
5095
|
+
return { ok: false, reason: "timestamp_missing" };
|
|
5096
|
+
}
|
|
5097
|
+
const skewMs = Math.abs(Date.now() - tsMs);
|
|
5098
|
+
if (skewMs > toleranceMs) {
|
|
5099
|
+
return { ok: false, reason: "timestamp_expired" };
|
|
5100
|
+
}
|
|
5101
|
+
const sig = getHeader("x-neetru-signature");
|
|
5102
|
+
const valid = await verifyWebhookSignature(payload, sig, secret);
|
|
5103
|
+
if (!valid) {
|
|
5104
|
+
return { ok: false, reason: "invalid_signature" };
|
|
5105
|
+
}
|
|
5106
|
+
return { ok: true };
|
|
5107
|
+
}
|
|
4954
5108
|
function hexToBytes(hex) {
|
|
4955
5109
|
if (hex.length % 2 !== 0) return null;
|
|
4956
5110
|
const bytes = new Uint8Array(hex.length / 2);
|
|
@@ -5933,6 +6087,7 @@ function createOidcAuthNamespace(config) {
|
|
|
5933
6087
|
const STORAGE_KEY = "neetru_id_token";
|
|
5934
6088
|
const listeners = /* @__PURE__ */ new Set();
|
|
5935
6089
|
let cachedUser = null;
|
|
6090
|
+
let _expiryTimerId = null;
|
|
5936
6091
|
function getStorage() {
|
|
5937
6092
|
try {
|
|
5938
6093
|
return typeof globalThis.localStorage !== "undefined" ? globalThis.localStorage : null;
|
|
@@ -5955,6 +6110,9 @@ function createOidcAuthNamespace(config) {
|
|
|
5955
6110
|
function tokenToUser(token) {
|
|
5956
6111
|
const payload = decodeJwtPayload(token);
|
|
5957
6112
|
if (!payload || typeof payload.sub !== "string") return null;
|
|
6113
|
+
if (typeof payload.exp === "number" && payload.exp * 1e3 < Date.now()) {
|
|
6114
|
+
return null;
|
|
6115
|
+
}
|
|
5958
6116
|
return {
|
|
5959
6117
|
uid: payload.sub,
|
|
5960
6118
|
email: typeof payload.email === "string" ? payload.email : "",
|
|
@@ -5968,12 +6126,28 @@ function createOidcAuthNamespace(config) {
|
|
|
5968
6126
|
};
|
|
5969
6127
|
}
|
|
5970
6128
|
function loadCachedUser() {
|
|
5971
|
-
if (cachedUser)
|
|
6129
|
+
if (cachedUser) {
|
|
6130
|
+
const exp = cachedUser.exp;
|
|
6131
|
+
if (typeof exp === "number" && exp * 1e3 < Date.now()) {
|
|
6132
|
+
cachedUser = null;
|
|
6133
|
+
const storage2 = getStorage();
|
|
6134
|
+
storage2?.removeItem(STORAGE_KEY);
|
|
6135
|
+
notify();
|
|
6136
|
+
return null;
|
|
6137
|
+
}
|
|
6138
|
+
return cachedUser;
|
|
6139
|
+
}
|
|
5972
6140
|
const storage = getStorage();
|
|
5973
6141
|
if (!storage) return null;
|
|
5974
6142
|
const token = storage.getItem(STORAGE_KEY);
|
|
5975
6143
|
if (!token) return null;
|
|
5976
|
-
|
|
6144
|
+
const user = tokenToUser(token);
|
|
6145
|
+
if (!user) {
|
|
6146
|
+
storage.removeItem(STORAGE_KEY);
|
|
6147
|
+
return null;
|
|
6148
|
+
}
|
|
6149
|
+
cachedUser = user;
|
|
6150
|
+
scheduleExpiryTimer(user);
|
|
5977
6151
|
return cachedUser;
|
|
5978
6152
|
}
|
|
5979
6153
|
function notify() {
|
|
@@ -5984,6 +6158,23 @@ function createOidcAuthNamespace(config) {
|
|
|
5984
6158
|
}
|
|
5985
6159
|
}
|
|
5986
6160
|
}
|
|
6161
|
+
function cancelExpiryTimer() {
|
|
6162
|
+
if (_expiryTimerId !== null) {
|
|
6163
|
+
clearTimeout(_expiryTimerId);
|
|
6164
|
+
_expiryTimerId = null;
|
|
6165
|
+
}
|
|
6166
|
+
}
|
|
6167
|
+
function scheduleExpiryTimer(user) {
|
|
6168
|
+
cancelExpiryTimer();
|
|
6169
|
+
const exp = user.exp;
|
|
6170
|
+
if (typeof exp !== "number") return;
|
|
6171
|
+
const msUntilExpiry = exp * 1e3 - Date.now();
|
|
6172
|
+
if (msUntilExpiry <= 0) return;
|
|
6173
|
+
_expiryTimerId = setTimeout(() => {
|
|
6174
|
+
_expiryTimerId = null;
|
|
6175
|
+
loadCachedUser();
|
|
6176
|
+
}, msUntilExpiry + 1);
|
|
6177
|
+
}
|
|
5987
6178
|
return {
|
|
5988
6179
|
async signIn(options) {
|
|
5989
6180
|
if (typeof globalThis.location !== "undefined" && typeof globalThis.location.assign === "function") {
|
|
@@ -6011,18 +6202,24 @@ function createOidcAuthNamespace(config) {
|
|
|
6011
6202
|
},
|
|
6012
6203
|
async signOut() {
|
|
6013
6204
|
const storage = getStorage();
|
|
6205
|
+
const storedToken = storage?.getItem(STORAGE_KEY) ?? null;
|
|
6014
6206
|
if (storage) storage.removeItem(STORAGE_KEY);
|
|
6207
|
+
cancelExpiryTimer();
|
|
6015
6208
|
cachedUser = null;
|
|
6016
6209
|
const overrideAuthUrl = typeof globalThis.NEETRU_AUTH_URL === "string" ? globalThis.NEETRU_AUTH_URL : null;
|
|
6017
6210
|
const idpOrigin = overrideAuthUrl ?? config.baseUrl.replace(/^https?:\/\/api\./, "https://auth.");
|
|
6018
6211
|
const revokeUrl = `${idpOrigin}/api/v1/oauth/revoke`;
|
|
6212
|
+
const revokeBody = storedToken ? JSON.stringify({ token: storedToken }) : null;
|
|
6019
6213
|
let revokeError = null;
|
|
6020
6214
|
for (let attempt = 0; attempt < 2; attempt++) {
|
|
6021
6215
|
try {
|
|
6022
|
-
|
|
6023
|
-
|
|
6024
|
-
|
|
6025
|
-
|
|
6216
|
+
if (revokeBody) {
|
|
6217
|
+
await config.fetch(revokeUrl, {
|
|
6218
|
+
method: "POST",
|
|
6219
|
+
headers: { "content-type": "application/json" },
|
|
6220
|
+
body: revokeBody
|
|
6221
|
+
});
|
|
6222
|
+
}
|
|
6026
6223
|
revokeError = null;
|
|
6027
6224
|
break;
|
|
6028
6225
|
} catch (err) {
|
|
@@ -6142,7 +6339,8 @@ function createNeetruClient(config = {}) {
|
|
|
6142
6339
|
// src/index.ts
|
|
6143
6340
|
init_errors();
|
|
6144
6341
|
init_db_errors();
|
|
6145
|
-
|
|
6342
|
+
init_http();
|
|
6343
|
+
var VERSION = "3.1.1";
|
|
6146
6344
|
|
|
6147
6345
|
exports.DEFAULT_BASE_URL = DEFAULT_BASE_URL;
|
|
6148
6346
|
exports.DEV_FIXTURE_USER = DEV_FIXTURE_USER;
|
|
@@ -6156,6 +6354,7 @@ exports.MockUsage = MockUsage;
|
|
|
6156
6354
|
exports.MockWebhooks = MockWebhooks;
|
|
6157
6355
|
exports.NEETRU_SENTINEL_KEY = NEETRU_SENTINEL_KEY;
|
|
6158
6356
|
exports.VERSION = VERSION;
|
|
6357
|
+
exports.checkCoreVersionCompat = checkCoreVersionCompat;
|
|
6159
6358
|
exports.createCheckoutNamespace = createCheckoutNamespace;
|
|
6160
6359
|
exports.createNeetruClient = createNeetruClient;
|
|
6161
6360
|
exports.createNeetruDb = createNeetruDb;
|
|
@@ -6166,6 +6365,7 @@ exports.isFieldSentinel = isFieldSentinel;
|
|
|
6166
6365
|
exports.isIncrementSentinel = isIncrementSentinel;
|
|
6167
6366
|
exports.isServerTimestampSentinel = isServerTimestampSentinel;
|
|
6168
6367
|
exports.serverTimestamp = serverTimestamp;
|
|
6368
|
+
exports.verifyWebhookRequest = verifyWebhookRequest;
|
|
6169
6369
|
exports.verifyWebhookSignature = verifyWebhookSignature;
|
|
6170
6370
|
//# sourceMappingURL=index.cjs.map
|
|
6171
6371
|
//# sourceMappingURL=index.cjs.map
|