@neetru/sdk 3.1.2 → 3.1.4

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 (76) hide show
  1. package/README.md +56 -5
  2. package/dist/auth.cjs +255 -18
  3. package/dist/auth.cjs.map +1 -1
  4. package/dist/auth.d.cts +1 -1
  5. package/dist/auth.d.ts +1 -1
  6. package/dist/auth.mjs +255 -18
  7. package/dist/auth.mjs.map +1 -1
  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.map +1 -1
  12. package/dist/checkout.cjs +22 -7
  13. package/dist/checkout.cjs.map +1 -1
  14. package/dist/checkout.d.cts +1 -1
  15. package/dist/checkout.d.ts +1 -1
  16. package/dist/checkout.mjs +22 -7
  17. package/dist/checkout.mjs.map +1 -1
  18. package/dist/db-react.d.cts +1 -1
  19. package/dist/db-react.d.ts +1 -1
  20. package/dist/db.cjs +59 -3
  21. package/dist/db.cjs.map +1 -1
  22. package/dist/db.d.cts +1 -1
  23. package/dist/db.d.ts +1 -1
  24. package/dist/db.mjs +59 -3
  25. package/dist/db.mjs.map +1 -1
  26. package/dist/entitlements.cjs.map +1 -1
  27. package/dist/entitlements.d.cts +1 -1
  28. package/dist/entitlements.d.ts +1 -1
  29. package/dist/entitlements.mjs.map +1 -1
  30. package/dist/errors.cjs.map +1 -1
  31. package/dist/errors.d.cts +11 -6
  32. package/dist/errors.d.ts +11 -6
  33. package/dist/errors.mjs.map +1 -1
  34. package/dist/firestore-compat.d.cts +1 -1
  35. package/dist/firestore-compat.d.ts +1 -1
  36. package/dist/index.cjs +267 -22
  37. package/dist/index.cjs.map +1 -1
  38. package/dist/index.d.cts +2 -2
  39. package/dist/index.d.ts +2 -2
  40. package/dist/index.mjs +267 -22
  41. package/dist/index.mjs.map +1 -1
  42. package/dist/mocks.cjs +25 -0
  43. package/dist/mocks.cjs.map +1 -1
  44. package/dist/mocks.d.cts +18 -1
  45. package/dist/mocks.d.ts +18 -1
  46. package/dist/mocks.mjs +25 -0
  47. package/dist/mocks.mjs.map +1 -1
  48. package/dist/notifications.cjs.map +1 -1
  49. package/dist/notifications.d.cts +1 -1
  50. package/dist/notifications.d.ts +1 -1
  51. package/dist/notifications.mjs.map +1 -1
  52. package/dist/react.d.cts +1 -1
  53. package/dist/react.d.ts +1 -1
  54. package/dist/support.cjs.map +1 -1
  55. package/dist/support.d.cts +1 -1
  56. package/dist/support.d.ts +1 -1
  57. package/dist/support.mjs.map +1 -1
  58. package/dist/telemetry.cjs +2 -1
  59. package/dist/telemetry.cjs.map +1 -1
  60. package/dist/telemetry.d.cts +1 -1
  61. package/dist/telemetry.d.ts +1 -1
  62. package/dist/telemetry.mjs +2 -1
  63. package/dist/telemetry.mjs.map +1 -1
  64. package/dist/{types-B3XFHD4A.d.ts → types-CRIIjqzC.d.ts} +105 -12
  65. package/dist/{types-DePdSU3P.d.cts → types-DvMdYbfV.d.cts} +105 -12
  66. package/dist/usage.cjs.map +1 -1
  67. package/dist/usage.d.cts +1 -1
  68. package/dist/usage.d.ts +1 -1
  69. package/dist/usage.mjs.map +1 -1
  70. package/dist/webhooks.cjs +11 -3
  71. package/dist/webhooks.cjs.map +1 -1
  72. package/dist/webhooks.d.cts +1 -1
  73. package/dist/webhooks.d.ts +1 -1
  74. package/dist/webhooks.mjs +11 -3
  75. package/dist/webhooks.mjs.map +1 -1
  76. package/package.json +1 -1
package/dist/auth.d.cts CHANGED
@@ -1,4 +1,4 @@
1
- import { B as NeetruClientConfig, z as NeetruClient } from './types-DePdSU3P.cjs';
1
+ import { G as NeetruClientConfig, B as NeetruClient } from './types-DvMdYbfV.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 { B as NeetruClientConfig, z as NeetruClient } from './types-B3XFHD4A.js';
1
+ import { G as NeetruClientConfig, B as NeetruClient } from './types-CRIIjqzC.js';
2
2
  import 'drizzle-orm/node-postgres';
3
3
  import './errors.js';
4
4
 
package/dist/auth.mjs CHANGED
@@ -1093,7 +1093,8 @@ function createTelemetryNamespace(config) {
1093
1093
  message: input.message
1094
1094
  };
1095
1095
  if (input.metadata) body.metadata = input.metadata;
1096
- if (input.productSlug) body.productSlug = input.productSlug;
1096
+ const productSlug = input.productSlug ?? config.productId;
1097
+ if (productSlug) body.productSlug = productSlug;
1097
1098
  let cid = input.correlationId;
1098
1099
  if (!cid) {
1099
1100
  try {
@@ -4601,13 +4602,69 @@ function createRestSyncTransport(config) {
4601
4602
  if (!config) {
4602
4603
  return { docs: [], newWatermark: Date.now(), resyncRequired: false };
4603
4604
  }
4604
- return { docs: [], newWatermark: Date.now(), resyncRequired: false };
4605
+ return { docs: [], newWatermark: Date.now(), resyncRequired: true };
4605
4606
  },
4606
- async fullResync(_collections) {
4607
+ async fullResync(collections) {
4607
4608
  if (!config) {
4608
4609
  return { docs: [], newWatermark: Date.now() };
4609
4610
  }
4610
- return { docs: [], newWatermark: Date.now() };
4611
+ const { httpRequest: httpRequest2 } = await Promise.resolve().then(() => (init_http(), http_exports));
4612
+ const tenantScopeId = config.tenantId ?? config.productId;
4613
+ const tenantHeaders = tenantScopeId ? { "x-neetru-tenant": tenantScopeId } : void 0;
4614
+ const now = Date.now();
4615
+ const docs = [];
4616
+ for (const collection of collections) {
4617
+ let raw;
4618
+ try {
4619
+ raw = await httpRequest2(config, {
4620
+ method: "GET",
4621
+ path: DATASTORE_COLLECTION_ENDPOINT(collection),
4622
+ requireAuth: true,
4623
+ retries: 2,
4624
+ headers: tenantHeaders,
4625
+ // Teto do Core é 200. Coleções > 200 docs truncam silenciosamente
4626
+ // aqui (paginação cursor é tarefa Core separada) — a detecção de
4627
+ // deleção-por-ausência do SyncEngine NÃO deve rodar sobre truncamento,
4628
+ // então mantemos o limite no máximo do servidor por ora.
4629
+ query: { limit: 200 }
4630
+ });
4631
+ } catch (err) {
4632
+ const msg = err instanceof Error ? err.message : String(err);
4633
+ throw new NeetruDbError(
4634
+ "db_unavailable",
4635
+ `[RestSyncTransport] fullResync falhou na cole\xE7\xE3o "${collection}": ${msg}`
4636
+ );
4637
+ }
4638
+ const resp = raw;
4639
+ const items = Array.isArray(resp?.items) ? resp.items : [];
4640
+ for (const item of items) {
4641
+ if (!item || typeof item !== "object") continue;
4642
+ const record = item;
4643
+ const id = typeof record["id"] === "string" ? record["id"] : null;
4644
+ if (!id) continue;
4645
+ const { id: _inlinedId, ...data } = record;
4646
+ let serverTimestamp = now;
4647
+ const updatedAt = data["_updatedAt"];
4648
+ if (typeof updatedAt === "number") {
4649
+ serverTimestamp = updatedAt;
4650
+ } else if (updatedAt !== null && typeof updatedAt === "object" && typeof updatedAt["_seconds"] === "number") {
4651
+ serverTimestamp = updatedAt["_seconds"] * 1e3;
4652
+ } else if (typeof updatedAt === "string") {
4653
+ const parsed = Date.parse(updatedAt);
4654
+ if (!Number.isNaN(parsed)) serverTimestamp = parsed;
4655
+ }
4656
+ const serverVersion = `rest_${id}_${serverTimestamp}`;
4657
+ docs.push({
4658
+ collection,
4659
+ id,
4660
+ data,
4661
+ serverVersion,
4662
+ serverTimestamp,
4663
+ deleted: false
4664
+ });
4665
+ }
4666
+ }
4667
+ return { docs, newWatermark: now };
4611
4668
  }
4612
4669
  };
4613
4670
  }
@@ -4847,6 +4904,15 @@ function createLazyCollectionRef(name, getDocsPromise) {
4847
4904
  // src/checkout.ts
4848
4905
  init_errors();
4849
4906
  init_http();
4907
+ function checkoutAuth(accessToken, method) {
4908
+ if (!accessToken || typeof accessToken !== "string") {
4909
+ throw new NeetruError(
4910
+ "validation_failed",
4911
+ `checkout.${method} requer o access_token OIDC do usu\xE1rio final (passe \`accessToken\`). A chave de API do SDK (nrt_*) N\xC3O \xE9 aceita pelo endpoint de checkout \u2014 ele autentica a identidade do cliente que est\xE1 assinando (Bearer access_token OIDC ou cookie de sess\xE3o).`
4912
+ );
4913
+ }
4914
+ return { requireAuth: false, headers: { authorization: `Bearer ${accessToken}` } };
4915
+ }
4850
4916
  function parseStartResponse(raw) {
4851
4917
  if (!raw || typeof raw !== "object") {
4852
4918
  throw new NeetruError("invalid_response", "checkout.start response is not an object");
@@ -4924,11 +4990,13 @@ function createHttpCheckoutNamespace(config) {
4924
4990
  };
4925
4991
  if (input.tenantType) body.targetTenantType = input.tenantType;
4926
4992
  if (input.tenantId) body.targetTenantId = input.tenantId;
4993
+ const { requireAuth, headers } = checkoutAuth(input.accessToken, "start");
4927
4994
  const raw = await httpRequest(config, {
4928
4995
  method: "POST",
4929
4996
  path: "/api/v1/checkout/intents",
4930
4997
  body,
4931
- requireAuth: true,
4998
+ requireAuth,
4999
+ headers,
4932
5000
  idempotencyKey: true
4933
5001
  });
4934
5002
  const result = parseStartResponse(raw);
@@ -4938,25 +5006,29 @@ function createHttpCheckoutNamespace(config) {
4938
5006
  }
4939
5007
  return result;
4940
5008
  },
4941
- async get(intentId) {
5009
+ async get(intentId, opts) {
4942
5010
  if (!intentId || typeof intentId !== "string") {
4943
5011
  throw new NeetruError("validation_failed", "checkout.get: intentId is required");
4944
5012
  }
5013
+ const { requireAuth, headers } = checkoutAuth(opts?.accessToken, "get");
4945
5014
  const raw = await httpRequest(config, {
4946
5015
  method: "GET",
4947
5016
  path: `/api/v1/checkout/intents/${encodeURIComponent(intentId)}`,
4948
- requireAuth: true
5017
+ requireAuth,
5018
+ headers
4949
5019
  });
4950
5020
  return parseGetResponse(raw);
4951
5021
  },
4952
- async cancel(intentId) {
5022
+ async cancel(intentId, opts) {
4953
5023
  if (!intentId || typeof intentId !== "string") {
4954
5024
  throw new NeetruError("validation_failed", "checkout.cancel: intentId is required");
4955
5025
  }
5026
+ const { requireAuth, headers } = checkoutAuth(opts?.accessToken, "cancel");
4956
5027
  const raw = await httpRequest(config, {
4957
5028
  method: "DELETE",
4958
5029
  path: `/api/v1/checkout/intents/${encodeURIComponent(intentId)}`,
4959
- requireAuth: true
5030
+ requireAuth,
5031
+ headers
4960
5032
  });
4961
5033
  return {
4962
5034
  ok: true,
@@ -5000,14 +5072,14 @@ var MockCheckout = class {
5000
5072
  requiresKyc: false
5001
5073
  };
5002
5074
  }
5003
- async get(intentId) {
5075
+ async get(intentId, _opts) {
5004
5076
  const found = this.intents.get(intentId);
5005
5077
  if (!found) {
5006
5078
  throw new NeetruError("not_found", `Mock intent ${intentId} not found`);
5007
5079
  }
5008
5080
  return { ...found };
5009
5081
  }
5010
- async cancel(intentId) {
5082
+ async cancel(intentId, _opts) {
5011
5083
  const found = this.intents.get(intentId);
5012
5084
  if (!found) {
5013
5085
  throw new NeetruError("not_found", `Mock intent ${intentId} not found`);
@@ -5462,6 +5534,31 @@ var MockAuth = class {
5462
5534
  if (this._user) return this._user;
5463
5535
  throw Object.assign(new Error("Nenhum user mock dispon\xEDvel"), { code: "token_invalid_signature" });
5464
5536
  }
5537
+ /**
5538
+ * Mock de `handleRedirectCallback` (Bug 2 / 3.1.3) — não há redirect real no
5539
+ * mock, então retorna `null` por default. Use `__setPendingCallback(result)`
5540
+ * em testes pra simular um retorno do IdP.
5541
+ */
5542
+ async handleRedirectCallback(_currentUrl) {
5543
+ return this._pendingCallback;
5544
+ }
5545
+ /**
5546
+ * Mock de `getIdToken` (Bug 2 / 3.1.3). Retorna `null` por default; use
5547
+ * `__setIdToken(token)` pra injetar um token cru em testes.
5548
+ */
5549
+ getIdToken() {
5550
+ return this._idToken;
5551
+ }
5552
+ _pendingCallback = null;
5553
+ _idToken = null;
5554
+ /** Test helper — simula o material devolvido por um callback OIDC. */
5555
+ __setPendingCallback(result) {
5556
+ this._pendingCallback = result;
5557
+ }
5558
+ /** Test helper — injeta o id_token cru retornado por `getIdToken`. */
5559
+ __setIdToken(token) {
5560
+ this._idToken = token;
5561
+ }
5465
5562
  _verifyTokenOverride = null;
5466
5563
  /**
5467
5564
  * Injeta override pra `verifyToken` em testes — permite simular expiração,
@@ -5680,6 +5777,49 @@ function _jwtPayloadToUser(payload) {
5680
5777
  ...payload
5681
5778
  };
5682
5779
  }
5780
+ var OIDC_TX_KEY = "neetru_oauth_tx";
5781
+ function resolveIdpOrigin(baseUrl) {
5782
+ const overrideAuthUrl = typeof globalThis.NEETRU_AUTH_URL === "string" ? globalThis.NEETRU_AUTH_URL : null;
5783
+ return overrideAuthUrl ?? baseUrl.replace(/^https?:\/\/api\./, "https://auth.");
5784
+ }
5785
+ function getWebCrypto() {
5786
+ const c = globalThis.crypto;
5787
+ if (!c || typeof c.getRandomValues !== "function" || !c.subtle) {
5788
+ throw new NeetruError(
5789
+ "runtime_unsupported",
5790
+ "auth.signIn requer WebCrypto (crypto.subtle) \u2014 indispon\xEDvel neste runtime. Use HTTPS (ou http://localhost) num browser moderno."
5791
+ );
5792
+ }
5793
+ return c;
5794
+ }
5795
+ function base64UrlFromBytes(bytes) {
5796
+ let str = "";
5797
+ for (const b of bytes) str += String.fromCharCode(b);
5798
+ const b64 = typeof btoa === "function" ? btoa(str) : Buffer.from(str, "binary").toString("base64");
5799
+ return b64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
5800
+ }
5801
+ function randomUrlSafe(crypto2, byteLen) {
5802
+ const bytes = new Uint8Array(byteLen);
5803
+ crypto2.getRandomValues(bytes);
5804
+ return base64UrlFromBytes(bytes);
5805
+ }
5806
+ async function pkceChallengeS256(crypto2, verifier) {
5807
+ const data = new TextEncoder().encode(verifier);
5808
+ const digest = await crypto2.subtle.digest("SHA-256", data);
5809
+ return base64UrlFromBytes(new Uint8Array(digest));
5810
+ }
5811
+ function cleanCallbackUrl(parsed) {
5812
+ try {
5813
+ const history = globalThis.history;
5814
+ if (!history || typeof history.replaceState !== "function") return;
5815
+ const clean = new URL(parsed.toString());
5816
+ for (const p of ["code", "state", "error", "error_description", "iss", "session_state"]) {
5817
+ clean.searchParams.delete(p);
5818
+ }
5819
+ history.replaceState(history.state ?? null, "", clean.pathname + clean.search + clean.hash);
5820
+ } catch {
5821
+ }
5822
+ }
5683
5823
  function createOidcAuthNamespace(config) {
5684
5824
  const STORAGE_KEY = "neetru_id_token";
5685
5825
  const listeners = /* @__PURE__ */ new Set();
@@ -5692,6 +5832,13 @@ function createOidcAuthNamespace(config) {
5692
5832
  return null;
5693
5833
  }
5694
5834
  }
5835
+ function getSessionStorage() {
5836
+ try {
5837
+ return typeof globalThis.sessionStorage !== "undefined" ? globalThis.sessionStorage : null;
5838
+ } catch {
5839
+ return null;
5840
+ }
5841
+ }
5695
5842
  function decodeJwtPayload(token) {
5696
5843
  const parts = token.split(".");
5697
5844
  if (parts.length !== 3) return null;
@@ -5775,20 +5922,48 @@ function createOidcAuthNamespace(config) {
5775
5922
  return {
5776
5923
  async signIn(options) {
5777
5924
  if (typeof globalThis.location !== "undefined" && typeof globalThis.location.assign === "function") {
5925
+ const crypto2 = getWebCrypto();
5778
5926
  const redirectUri = options?.redirectUri ?? globalThis.location.origin;
5779
5927
  const scope = options?.scope ?? "openid profile email";
5780
- const state = options?.postLoginRedirect ?? globalThis.location.href;
5781
- const overrideAuthUrl = typeof globalThis.NEETRU_AUTH_URL === "string" ? globalThis.NEETRU_AUTH_URL : null;
5782
- const idpOrigin = overrideAuthUrl ?? config.baseUrl.replace(/^https?:\/\/api\./, "https://auth.");
5928
+ let clientId = config.oidcClientId;
5929
+ if (!clientId && config.apiKey) {
5930
+ clientId = parseApiKey(config.apiKey).keyId;
5931
+ }
5932
+ if (!clientId) {
5933
+ throw new NeetruError(
5934
+ "invalid_config",
5935
+ "auth.signIn requer `oidcClientId` (ou `apiKey`) configurado para montar o client_id."
5936
+ );
5937
+ }
5938
+ const state = randomUrlSafe(crypto2, 16);
5939
+ const nonce = randomUrlSafe(crypto2, 16);
5940
+ const codeVerifier = randomUrlSafe(crypto2, 32);
5941
+ const codeChallenge = await pkceChallengeS256(crypto2, codeVerifier);
5942
+ const session = getSessionStorage();
5943
+ if (!session) {
5944
+ throw new NeetruError(
5945
+ "runtime_unsupported",
5946
+ "auth.signIn requer sessionStorage para guardar a transa\xE7\xE3o PKCE entre o redirect e o callback."
5947
+ );
5948
+ }
5949
+ const tx = {
5950
+ state,
5951
+ nonce,
5952
+ codeVerifier,
5953
+ redirectUri,
5954
+ postLoginRedirect: options?.postLoginRedirect
5955
+ };
5956
+ session.setItem(OIDC_TX_KEY, JSON.stringify(tx));
5957
+ const idpOrigin = resolveIdpOrigin(config.baseUrl);
5783
5958
  const url = new URL("/api/v1/oauth/authorize", idpOrigin);
5784
5959
  url.searchParams.set("response_type", "code");
5960
+ url.searchParams.set("client_id", clientId);
5785
5961
  url.searchParams.set("redirect_uri", redirectUri);
5786
5962
  url.searchParams.set("scope", scope);
5787
5963
  url.searchParams.set("state", state);
5788
- if (config.apiKey) {
5789
- const { keyId } = parseApiKey(config.apiKey);
5790
- url.searchParams.set("client_id", keyId);
5791
- }
5964
+ url.searchParams.set("nonce", nonce);
5965
+ url.searchParams.set("code_challenge", codeChallenge);
5966
+ url.searchParams.set("code_challenge_method", "S256");
5792
5967
  globalThis.location.assign(url.toString());
5793
5968
  return;
5794
5969
  }
@@ -5797,6 +5972,67 @@ function createOidcAuthNamespace(config) {
5797
5972
  "auth.signIn requires a browser context or mocks. Use NEETRU_ENV=dev or pass mocks.auth."
5798
5973
  );
5799
5974
  },
5975
+ async handleRedirectCallback(currentUrl) {
5976
+ const href = currentUrl ?? (typeof globalThis.location !== "undefined" ? globalThis.location.href : void 0);
5977
+ if (!href) return null;
5978
+ let parsed;
5979
+ try {
5980
+ parsed = new URL(href);
5981
+ } catch {
5982
+ return null;
5983
+ }
5984
+ const params = parsed.searchParams;
5985
+ const idpError = params.get("error");
5986
+ if (idpError) {
5987
+ const desc = params.get("error_description") ?? idpError;
5988
+ getSessionStorage()?.removeItem(OIDC_TX_KEY);
5989
+ cleanCallbackUrl(parsed);
5990
+ throw new NeetruError(idpError, `OIDC authorize falhou: ${desc}`);
5991
+ }
5992
+ const code = params.get("code");
5993
+ if (!code) return null;
5994
+ const returnedState = params.get("state");
5995
+ const session = getSessionStorage();
5996
+ const txRaw = session?.getItem(OIDC_TX_KEY) ?? null;
5997
+ let tx = null;
5998
+ if (txRaw) {
5999
+ try {
6000
+ tx = JSON.parse(txRaw);
6001
+ } catch {
6002
+ tx = null;
6003
+ }
6004
+ }
6005
+ if (!tx) {
6006
+ throw new NeetruError(
6007
+ "oidc_state_mismatch",
6008
+ "Callback OIDC sem transa\xE7\xE3o pendente \u2014 `signIn` n\xE3o foi iniciado nesta sess\xE3o (ou j\xE1 consumido)."
6009
+ );
6010
+ }
6011
+ if (!returnedState || returnedState !== tx.state) {
6012
+ session?.removeItem(OIDC_TX_KEY);
6013
+ cleanCallbackUrl(parsed);
6014
+ throw new NeetruError(
6015
+ "oidc_state_mismatch",
6016
+ "state do callback OIDC n\xE3o confere com o da transa\xE7\xE3o (poss\xEDvel CSRF)."
6017
+ );
6018
+ }
6019
+ session?.removeItem(OIDC_TX_KEY);
6020
+ cleanCallbackUrl(parsed);
6021
+ return {
6022
+ code,
6023
+ codeVerifier: tx.codeVerifier,
6024
+ redirectUri: tx.redirectUri,
6025
+ nonce: tx.nonce,
6026
+ state: tx.state,
6027
+ postLoginRedirect: tx.postLoginRedirect
6028
+ };
6029
+ },
6030
+ getIdToken() {
6031
+ const storage = getStorage();
6032
+ const token = storage?.getItem(STORAGE_KEY) ?? null;
6033
+ if (!token) return null;
6034
+ return tokenToUser(token) ? token : null;
6035
+ },
5800
6036
  async signOut() {
5801
6037
  const storage = getStorage();
5802
6038
  const storedToken = storage?.getItem(STORAGE_KEY) ?? null;
@@ -5903,6 +6139,7 @@ function createNeetruClient(config = {}) {
5903
6139
  const env = resolveEnv(config.env);
5904
6140
  const resolved = Object.freeze({
5905
6141
  apiKey,
6142
+ oidcClientId: config.oidcClientId,
5906
6143
  baseUrl,
5907
6144
  fetch: fetchImpl.bind(globalThis),
5908
6145
  env,