@neetru/sdk 3.1.6 → 3.1.9

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 (77) hide show
  1. package/dist/auth.cjs +65 -22
  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 +65 -22
  6. package/dist/auth.mjs.map +1 -1
  7. package/dist/catalog.cjs.map +1 -1
  8. package/dist/catalog.d.cts +1 -1
  9. package/dist/catalog.d.ts +1 -1
  10. package/dist/catalog.mjs.map +1 -1
  11. package/dist/checkout.cjs.map +1 -1
  12. package/dist/checkout.d.cts +1 -1
  13. package/dist/checkout.d.ts +1 -1
  14. package/dist/checkout.mjs.map +1 -1
  15. package/dist/db-react.d.cts +1 -1
  16. package/dist/db-react.d.ts +1 -1
  17. package/dist/db.cjs +55 -16
  18. package/dist/db.cjs.map +1 -1
  19. package/dist/db.d.cts +1 -1
  20. package/dist/db.d.ts +1 -1
  21. package/dist/db.mjs +55 -16
  22. package/dist/db.mjs.map +1 -1
  23. package/dist/entitlements.cjs.map +1 -1
  24. package/dist/entitlements.d.cts +1 -1
  25. package/dist/entitlements.d.ts +1 -1
  26. package/dist/entitlements.mjs.map +1 -1
  27. package/dist/errors.cjs.map +1 -1
  28. package/dist/errors.d.cts +8 -8
  29. package/dist/errors.d.ts +8 -8
  30. package/dist/errors.mjs.map +1 -1
  31. package/dist/firestore-compat.cjs +4 -9
  32. package/dist/firestore-compat.cjs.map +1 -1
  33. package/dist/firestore-compat.d.cts +2 -2
  34. package/dist/firestore-compat.d.ts +2 -2
  35. package/dist/firestore-compat.mjs +4 -9
  36. package/dist/firestore-compat.mjs.map +1 -1
  37. package/dist/index.cjs +66 -23
  38. package/dist/index.cjs.map +1 -1
  39. package/dist/index.d.cts +2 -2
  40. package/dist/index.d.ts +2 -2
  41. package/dist/index.mjs +66 -23
  42. package/dist/index.mjs.map +1 -1
  43. package/dist/mocks.cjs.map +1 -1
  44. package/dist/mocks.d.cts +1 -1
  45. package/dist/mocks.d.ts +1 -1
  46. package/dist/mocks.mjs.map +1 -1
  47. package/dist/notifications.cjs.map +1 -1
  48. package/dist/notifications.d.cts +1 -1
  49. package/dist/notifications.d.ts +1 -1
  50. package/dist/notifications.mjs.map +1 -1
  51. package/dist/react.cjs.map +1 -1
  52. package/dist/react.d.cts +1 -1
  53. package/dist/react.d.ts +1 -1
  54. package/dist/react.mjs.map +1 -1
  55. package/dist/support.cjs.map +1 -1
  56. package/dist/support.d.cts +1 -1
  57. package/dist/support.d.ts +1 -1
  58. package/dist/support.mjs.map +1 -1
  59. package/dist/telemetry.cjs +5 -1
  60. package/dist/telemetry.cjs.map +1 -1
  61. package/dist/telemetry.d.cts +1 -1
  62. package/dist/telemetry.d.ts +1 -1
  63. package/dist/telemetry.mjs +5 -1
  64. package/dist/telemetry.mjs.map +1 -1
  65. package/dist/{types-BXvGHppn.d.cts → types-DDiVzgCT.d.cts} +19 -2
  66. package/dist/{types-CQVak2a3.d.ts → types-HWDOUmlC.d.ts} +19 -2
  67. package/dist/usage.cjs +2 -2
  68. package/dist/usage.cjs.map +1 -1
  69. package/dist/usage.d.cts +1 -1
  70. package/dist/usage.d.ts +1 -1
  71. package/dist/usage.mjs +2 -2
  72. package/dist/usage.mjs.map +1 -1
  73. package/dist/webhooks.cjs.map +1 -1
  74. package/dist/webhooks.d.cts +1 -1
  75. package/dist/webhooks.d.ts +1 -1
  76. package/dist/webhooks.mjs.map +1 -1
  77. package/package.json +156 -156
package/dist/auth.d.cts CHANGED
@@ -1,4 +1,4 @@
1
- import { G as NeetruClientConfig, B as NeetruClient } from './types-BXvGHppn.cjs';
1
+ import { G as NeetruClientConfig, B as NeetruClient } from './types-DDiVzgCT.cjs';
2
2
  import 'drizzle-orm/node-postgres';
3
3
  import './errors.cjs';
4
4
 
package/dist/auth.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { G as NeetruClientConfig, B as NeetruClient } from './types-CQVak2a3.js';
1
+ import { G as NeetruClientConfig, B as NeetruClient } from './types-HWDOUmlC.js';
2
2
  import 'drizzle-orm/node-postgres';
3
3
  import './errors.js';
4
4
 
package/dist/auth.mjs CHANGED
@@ -446,7 +446,7 @@ __export(sql_client_exports, {
446
446
  function mapEnvToCore(sdkEnv) {
447
447
  if (sdkEnv === "workspace") return "staging";
448
448
  if (sdkEnv === "prod") return "production";
449
- return "dev";
449
+ return "dev-local";
450
450
  }
451
451
  function createHttpLeaseFetcher(config) {
452
452
  return async (opts) => {
@@ -955,7 +955,11 @@ function createTelemetryNamespace(config) {
955
955
  path: "/api/sdk/v1/telemetry/event",
956
956
  body,
957
957
  requireAuth: true,
958
- retries: 1
958
+ retries: 1,
959
+ // caça-bruxas: retry sem Idempotency-Key duplicava o evento se o
960
+ // servidor já tinha persistido antes da falha transiente. Mesmo
961
+ // padrão do usage.report (key estável por chamada, reusada no retry).
962
+ idempotencyKey: true
959
963
  });
960
964
  } catch (err) {
961
965
  if (typeof console !== "undefined") {
@@ -1159,8 +1163,8 @@ function createUsageNamespace(config) {
1159
1163
  if (!resource || typeof resource !== "string") {
1160
1164
  throw new NeetruError("validation_failed", "resource is required");
1161
1165
  }
1162
- if (!Number.isFinite(qty) || qty <= 0) {
1163
- throw new NeetruError("validation_failed", "qty must be positive integer");
1166
+ if (!Number.isFinite(qty) || qty <= 0 || !Number.isInteger(qty)) {
1167
+ throw new NeetruError("validation_failed", "qty must be a positive integer");
1164
1168
  }
1165
1169
  const productId = options?.productId ?? config.productId;
1166
1170
  const tenantId = options?.tenantId ?? config.tenantId;
@@ -1409,7 +1413,12 @@ function evaluateFilter(data, filter) {
1409
1413
  if (!Array.isArray(value)) return true;
1410
1414
  return !value.includes(fieldValue);
1411
1415
  default:
1412
- return false;
1416
+ if (op === "array-contains-any") {
1417
+ return Array.isArray(fieldValue) && Array.isArray(value) && value.some((v) => fieldValue.includes(v));
1418
+ }
1419
+ throw new Error(
1420
+ `QueryEngine: operador de filtro '${String(op)}' n\xE3o suportado em leitura offline/cache. Use um operador suportado (==, !=, <, <=, >, >=, array-contains, array-contains-any, in, not-in) ou fa\xE7a a query server-side.`
1421
+ );
1413
1422
  }
1414
1423
  }
1415
1424
  function matchesAllFilters(data, filters) {
@@ -1939,7 +1948,7 @@ var MutationQueue = class {
1939
1948
  * - `{ removed: true }` — a mutação existente deve ser deletada (add+remove)
1940
1949
  * - `null` — não é possível coalescir
1941
1950
  */
1942
- tryCoalesce(existing, newOp, newPayload, newBatchId) {
1951
+ tryCoalesce(existing, newOp, newPayload, newBatchId, newUpsert) {
1943
1952
  if (existing.batchId !== newBatchId) return null;
1944
1953
  if (existing.status === "inflight") return null;
1945
1954
  const existingOp = existing.op;
@@ -1947,10 +1956,12 @@ var MutationQueue = class {
1947
1956
  return { removed: true };
1948
1957
  }
1949
1958
  if (existingOp === "update" && newOp === "update") {
1959
+ const mergedUpsert = existing.upsert || newUpsert;
1950
1960
  return {
1951
1961
  coalesced: {
1952
1962
  ...existing,
1953
- payload: { ...existing.payload ?? {}, ...newPayload ?? {} }
1963
+ payload: { ...existing.payload ?? {}, ...newPayload ?? {} },
1964
+ ...mergedUpsert ? { upsert: true } : {}
1954
1965
  }
1955
1966
  };
1956
1967
  }
@@ -1999,13 +2010,13 @@ var MutationQueue = class {
1999
2010
  * Atômico: a persistência final é uma única operação putMutation.
2000
2011
  */
2001
2012
  async enqueue(params) {
2002
- const { collection, op, payload, baseVersion, batchId } = params;
2013
+ const { collection, op, payload, baseVersion, batchId, upsert } = params;
2003
2014
  const docId = params.docId ?? (op === "add" ? generateUUIDv4() : "");
2004
2015
  await this.syncSeq();
2005
2016
  const existingMutations = await this.store.listMutations({ collection, docId });
2006
2017
  const lastQueued = existingMutations.filter((m) => m.status === "queued").sort((a, b) => b.seq - a.seq)[0];
2007
2018
  if (lastQueued !== void 0) {
2008
- const coalesceResult = this.tryCoalesce(lastQueued, op, payload, batchId);
2019
+ const coalesceResult = this.tryCoalesce(lastQueued, op, payload, batchId, upsert);
2009
2020
  if (coalesceResult !== null) {
2010
2021
  if ("removed" in coalesceResult) {
2011
2022
  await this.store.deleteMutation(lastQueued.mutationId);
@@ -2043,7 +2054,9 @@ var MutationQueue = class {
2043
2054
  attempts: 0,
2044
2055
  lastError: null,
2045
2056
  status: "queued",
2046
- batchId
2057
+ batchId,
2058
+ // bug_0273e4dd: só persiste o flag quando verdadeiro (mantém o doc limpo).
2059
+ ...upsert ? { upsert: true } : {}
2047
2060
  };
2048
2061
  await this.store.putMutation(mutation);
2049
2062
  return mutation;
@@ -2372,10 +2385,15 @@ var SyncEngine = class {
2372
2385
  await this._applyRejected(mut, res, busChanges);
2373
2386
  }
2374
2387
  }
2388
+ const processedIds = new Set(result.results.map((r) => r.mutationId));
2389
+ const unprocessed = mutations.filter((m) => !processedIds.has(m.mutationId));
2390
+ for (const mut of unprocessed) {
2391
+ await this._queue.markRetry(mut.mutationId, "partial_push: muta\xE7\xE3o n\xE3o processada no lote");
2392
+ }
2375
2393
  if (busChanges.length > 0) {
2376
2394
  this._bus.emit(busChanges);
2377
2395
  }
2378
- return true;
2396
+ return unprocessed.length === 0;
2379
2397
  }
2380
2398
  /** Atualiza o cache após confirmação de escrita (outcome=confirmed). */
2381
2399
  async _applySyncedDoc(collection, docId, serverVersion, serverTimestamp, mut, busChanges) {
@@ -3682,7 +3700,7 @@ var DbCollectionRefImpl = class {
3682
3700
  }]);
3683
3701
  return { ok: true };
3684
3702
  }
3685
- async update(id, data) {
3703
+ async update(id, data, opts) {
3686
3704
  assertValidId(id);
3687
3705
  const existing = await this._store.getDoc(this._collection, id);
3688
3706
  const rawData = data;
@@ -3695,7 +3713,9 @@ var DbCollectionRefImpl = class {
3695
3713
  op: "update",
3696
3714
  payload: rawData,
3697
3715
  baseVersion: existing?.meta.serverVersion ?? null,
3698
- batchId: null
3716
+ batchId: null,
3717
+ // bug_0273e4dd: propaga merge-upsert (setDoc({merge:true}) cria-se-ausente).
3718
+ upsert: opts?.upsert
3699
3719
  });
3700
3720
  await this._store.putDoc({
3701
3721
  collection: this._collection,
@@ -3819,7 +3839,9 @@ var DbCollectionRefImpl = class {
3819
3839
  op: "update",
3820
3840
  payload: data,
3821
3841
  baseVersion: existing?.meta.serverVersion ?? null,
3822
- batchId
3842
+ batchId,
3843
+ // bug_0273e4dd: propaga merge-upsert (writeBatch.set(merge) cria-se-ausente).
3844
+ upsert: op.upsert
3823
3845
  });
3824
3846
  await this._store.putDoc({
3825
3847
  collection,
@@ -4081,6 +4103,10 @@ async function createOfflineDocumentsNamespace(opts) {
4081
4103
  connectivity,
4082
4104
  periodicSyncIntervalMs: opts.periodicSyncIntervalMs ?? 5 * 60 * 1e3
4083
4105
  });
4106
+ connectivity.start();
4107
+ if (!opts.singleTab && tabCoordinatorLike instanceof TabCoordinator) {
4108
+ tabCoordinatorLike.start();
4109
+ }
4084
4110
  return new NeetruDbDocumentsImpl({
4085
4111
  store,
4086
4112
  queue,
@@ -4550,11 +4576,11 @@ function createRestSyncTransport(config) {
4550
4576
  let raw;
4551
4577
  if (m.op === "add") {
4552
4578
  raw = await httpRequest2(config, {
4553
- method: "POST",
4554
- path: DATASTORE_COLLECTION_ENDPOINT(m.collection),
4579
+ method: "PUT",
4580
+ path: DATASTORE_DOC_ENDPOINT(m.collection, m.docId),
4555
4581
  body: { data: m.payload ?? {} },
4556
4582
  requireAuth: true,
4557
- retries: 0,
4583
+ retries: 2,
4558
4584
  headers: tenantHeaders
4559
4585
  });
4560
4586
  } else if (m.op === "set") {
@@ -4570,7 +4596,7 @@ function createRestSyncTransport(config) {
4570
4596
  raw = await httpRequest2(config, {
4571
4597
  method: "PATCH",
4572
4598
  path: DATASTORE_DOC_ENDPOINT(m.collection, m.docId),
4573
- body: { data: m.payload ?? {} },
4599
+ body: { data: m.payload ?? {}, ...m.upsert ? { upsert: true } : {} },
4574
4600
  requireAuth: true,
4575
4601
  retries: 2,
4576
4602
  headers: tenantHeaders
@@ -4593,7 +4619,24 @@ function createRestSyncTransport(config) {
4593
4619
  });
4594
4620
  } catch (err) {
4595
4621
  const msg = err instanceof Error ? err.message : String(err);
4596
- throw new NeetruDbError("db_unavailable", `pushMutations falhou: ${msg}`);
4622
+ const code = err?.code;
4623
+ const status = err?.status;
4624
+ const isPermanent = code === "forbidden" || code === "unauthorized" || code === "not_found" || code === "conflict" || code === "validation_failed" || typeof status === "number" && status >= 400 && status < 500 && status !== 408 && status !== 429;
4625
+ if (isPermanent) {
4626
+ const reason = code === "forbidden" || code === "unauthorized" ? "rejected_permission" : "rejected_validation";
4627
+ results.push({
4628
+ mutationId: m.mutationId,
4629
+ outcome: "rejected",
4630
+ reason,
4631
+ serverVersion: null,
4632
+ serverData: null
4633
+ });
4634
+ continue;
4635
+ }
4636
+ if (results.length === 0) {
4637
+ throw new NeetruDbError("db_unavailable", `pushMutations falhou: ${msg}`);
4638
+ }
4639
+ break;
4597
4640
  }
4598
4641
  }
4599
4642
  return { results };
@@ -6109,15 +6152,15 @@ function createOidcAuthNamespace(config) {
6109
6152
  const overrideAuthUrl = typeof globalThis.NEETRU_AUTH_URL === "string" ? globalThis.NEETRU_AUTH_URL : null;
6110
6153
  const idpOrigin = overrideAuthUrl ?? config.baseUrl.replace(/^https?:\/\/api\./, "https://auth.");
6111
6154
  const issuer = idpOrigin;
6112
- const jwksUrl = `${idpOrigin}/api/v1/oauth/.well-known/jwks.json`;
6155
+ const jwksUrl = `${idpOrigin}/api/v1/oauth/.well-known/jwks`;
6113
6156
  const cacheTtlMs = options?.jwksCacheTtlMs ?? DEFAULT_JWKS_TTL_MS;
6114
6157
  const audienceFromOptions = options?.audience;
6115
6158
  const parsedKey = tryParseApiKey(config.apiKey);
6116
- const audience = audienceFromOptions ?? parsedKey?.keyId;
6159
+ const audience = audienceFromOptions ?? config.oidcClientId ?? parsedKey?.keyId;
6117
6160
  if (!audience) {
6118
6161
  throw new NeetruError(
6119
6162
  "invalid_config",
6120
- "verifyToken requires an audience: provide apiKey on the client or pass options.audience. Calling jwtVerify without an audience allows tokens issued for other OIDC clients to pass."
6163
+ "verifyToken requires an audience: provide oidcClientId/apiKey on the client or pass options.audience. Calling jwtVerify without an audience allows tokens issued for other OIDC clients to pass."
6121
6164
  );
6122
6165
  }
6123
6166
  const jwks = _getJwks(jwksUrl, cacheTtlMs);