@neetru/sdk 3.3.0 → 3.4.0

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 (56) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/dist/ai.d.cts +1 -1
  3. package/dist/ai.d.ts +1 -1
  4. package/dist/auth.cjs +179 -2
  5. package/dist/auth.cjs.map +1 -1
  6. package/dist/auth.d.cts +1 -1
  7. package/dist/auth.d.ts +1 -1
  8. package/dist/auth.mjs +179 -2
  9. package/dist/auth.mjs.map +1 -1
  10. package/dist/catalog.d.cts +1 -1
  11. package/dist/catalog.d.ts +1 -1
  12. package/dist/checkout.d.cts +1 -1
  13. package/dist/checkout.d.ts +1 -1
  14. package/dist/db-react.d.cts +1 -1
  15. package/dist/db-react.d.ts +1 -1
  16. package/dist/db.cjs +17 -0
  17. package/dist/db.cjs.map +1 -1
  18. package/dist/db.d.cts +1 -1
  19. package/dist/db.d.ts +1 -1
  20. package/dist/db.mjs +17 -1
  21. package/dist/db.mjs.map +1 -1
  22. package/dist/entitlements.d.cts +1 -1
  23. package/dist/entitlements.d.ts +1 -1
  24. package/dist/firestore-compat.d.cts +1 -1
  25. package/dist/firestore-compat.d.ts +1 -1
  26. package/dist/index.cjs +180 -3
  27. package/dist/index.cjs.map +1 -1
  28. package/dist/index.d.cts +2 -2
  29. package/dist/index.d.ts +2 -2
  30. package/dist/index.mjs +180 -3
  31. package/dist/index.mjs.map +1 -1
  32. package/dist/mocks.cjs +68 -1
  33. package/dist/mocks.cjs.map +1 -1
  34. package/dist/mocks.d.cts +17 -1
  35. package/dist/mocks.d.ts +17 -1
  36. package/dist/mocks.mjs +68 -1
  37. package/dist/mocks.mjs.map +1 -1
  38. package/dist/notifications.d.cts +1 -1
  39. package/dist/notifications.d.ts +1 -1
  40. package/dist/react.d.cts +1 -1
  41. package/dist/react.d.ts +1 -1
  42. package/dist/support.cjs +98 -1
  43. package/dist/support.cjs.map +1 -1
  44. package/dist/support.d.cts +1 -1
  45. package/dist/support.d.ts +1 -1
  46. package/dist/support.mjs +98 -1
  47. package/dist/support.mjs.map +1 -1
  48. package/dist/telemetry.d.cts +1 -1
  49. package/dist/telemetry.d.ts +1 -1
  50. package/dist/{types-DQplLXIk.d.cts → types-BoFSCHF_.d.cts} +117 -2
  51. package/dist/{types-DslphKIM.d.ts → types-DdJyqBy-.d.ts} +117 -2
  52. package/dist/usage.d.cts +1 -1
  53. package/dist/usage.d.ts +1 -1
  54. package/dist/webhooks.d.cts +1 -1
  55. package/dist/webhooks.d.ts +1 -1
  56. package/package.json +1 -1
package/dist/auth.d.cts CHANGED
@@ -1,4 +1,4 @@
1
- import { X as NeetruClientConfig, W as NeetruClient } from './types-DQplLXIk.cjs';
1
+ import { X as NeetruClientConfig, W as NeetruClient } from './types-BoFSCHF_.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 { X as NeetruClientConfig, W as NeetruClient } from './types-DslphKIM.js';
1
+ import { X as NeetruClientConfig, W as NeetruClient } from './types-DdJyqBy-.js';
2
2
  import 'drizzle-orm/node-postgres';
3
3
  import './errors.js';
4
4
 
package/dist/auth.mjs CHANGED
@@ -1582,6 +1582,7 @@ function toTicket(raw) {
1582
1582
  if (typeof r.id !== "string") {
1583
1583
  throw new NeetruError("invalid_response", "Ticket missing id");
1584
1584
  }
1585
+ const firstAttachment = Array.isArray(r.attachments) && typeof r.attachments[0] === "string" ? r.attachments[0] : void 0;
1585
1586
  return {
1586
1587
  id: r.id,
1587
1588
  subject: typeof r.subject === "string" ? r.subject : "",
@@ -1590,9 +1591,28 @@ function toTicket(raw) {
1590
1591
  status: VALID_STATUSES.includes(r.status) ? r.status : "open",
1591
1592
  createdAt: typeof r.createdAt === "string" ? r.createdAt : (/* @__PURE__ */ new Date()).toISOString(),
1592
1593
  updatedAt: typeof r.updatedAt === "string" ? r.updatedAt : void 0,
1593
- productSlug: typeof r.productId === "string" ? r.productId : typeof r.productSlug === "string" ? r.productSlug : void 0
1594
+ productSlug: typeof r.productId === "string" ? r.productId : typeof r.productSlug === "string" ? r.productSlug : void 0,
1595
+ pageUrl: typeof r.pageUrl === "string" ? r.pageUrl : null,
1596
+ screenshotUrl: typeof r.screenshotUrl === "string" ? r.screenshotUrl : firstAttachment ?? null,
1597
+ resolvedAt: typeof r.resolvedAt === "string" ? r.resolvedAt : null,
1598
+ resolvedNotifiedAt: typeof r.resolvedNotifiedAt === "string" ? r.resolvedNotifiedAt : null,
1599
+ satisfactionAttempts: typeof r.satisfactionAttempts === "number" && Number.isFinite(r.satisfactionAttempts) ? r.satisfactionAttempts : 0,
1600
+ lastSatisfactionAttemptAt: typeof r.lastSatisfactionAttemptAt === "string" ? r.lastSatisfactionAttemptAt : null,
1601
+ satisfiedAt: typeof r.satisfiedAt === "string" ? r.satisfiedAt : null,
1602
+ rating: typeof r.rating === "number" && Number.isFinite(r.rating) ? r.rating : null,
1603
+ satisfactionComment: typeof r.satisfactionComment === "string" ? r.satisfactionComment : null
1594
1604
  };
1595
1605
  }
1606
+ function assertTicketId(ticketId) {
1607
+ if (!ticketId || typeof ticketId !== "string") {
1608
+ throw new NeetruError("validation_failed", "ticketId is required");
1609
+ }
1610
+ }
1611
+ function assertValidComment(comment) {
1612
+ if (typeof comment === "string" && comment.length > 2e3) {
1613
+ throw new NeetruError("validation_failed", "comment max 2000 chars");
1614
+ }
1615
+ }
1596
1616
  function resolveProductId(config, ...candidates) {
1597
1617
  for (const c of candidates) {
1598
1618
  if (typeof c === "string" && c.length > 0) {
@@ -1649,6 +1669,15 @@ function createSupportNamespace(config) {
1649
1669
  if (input.metadata && typeof input.metadata === "object") {
1650
1670
  body.metadata = input.metadata;
1651
1671
  }
1672
+ if (typeof input.pageUrl === "string") {
1673
+ body.pageUrl = input.pageUrl;
1674
+ }
1675
+ if (typeof input.screenshotBase64 === "string") {
1676
+ body.screenshotBase64 = input.screenshotBase64;
1677
+ }
1678
+ if (typeof input.consoleLogs === "string") {
1679
+ body.consoleLogs = input.consoleLogs;
1680
+ }
1652
1681
  const raw = await httpRequest(config, {
1653
1682
  method: "POST",
1654
1683
  path: "/api/sdk/v1/support/tickets",
@@ -1674,6 +1703,74 @@ function createSupportNamespace(config) {
1674
1703
  });
1675
1704
  const list = Array.isArray(raw) ? raw : raw && typeof raw === "object" && "tickets" in raw ? raw.tickets ?? [] : [];
1676
1705
  return list.map(toTicket);
1706
+ },
1707
+ /**
1708
+ * 3.4: marca a notificação de resolução como vista (`resolvedNotifiedAt`,
1709
+ * idempotente no Core). Endpoint canonical:
1710
+ * `POST /api/sdk/v1/support/tickets/{id}/resolved-seen`.
1711
+ */
1712
+ async markResolvedSeen(ticketId) {
1713
+ assertTicketId(ticketId);
1714
+ await httpRequest(config, {
1715
+ method: "POST",
1716
+ path: `/api/sdk/v1/support/tickets/${encodeURIComponent(ticketId)}/resolved-seen`,
1717
+ requireAuth: true
1718
+ });
1719
+ },
1720
+ /**
1721
+ * 3.4: registra uma tentativa de exibição do modal de satisfação
1722
+ * (`satisfactionAttempts += 1`). Chamar no SHOW do modal, não na
1723
+ * resposta. Endpoint canonical:
1724
+ * `POST /api/sdk/v1/support/tickets/{id}/satisfaction-attempt`.
1725
+ */
1726
+ async recordSatisfactionAttempt(ticketId) {
1727
+ assertTicketId(ticketId);
1728
+ await httpRequest(config, {
1729
+ method: "POST",
1730
+ path: `/api/sdk/v1/support/tickets/${encodeURIComponent(ticketId)}/satisfaction-attempt`,
1731
+ requireAuth: true
1732
+ });
1733
+ },
1734
+ /**
1735
+ * 3.4: envia (ou pula) a avaliação de satisfação (CSAT 1-5). Endpoint
1736
+ * canonical: `POST /api/sdk/v1/support/tickets/{id}/satisfaction`.
1737
+ */
1738
+ async submitSatisfaction(input) {
1739
+ if (!input || typeof input !== "object") {
1740
+ throw new NeetruError("validation_failed", "input is required");
1741
+ }
1742
+ assertTicketId(input.ticketId);
1743
+ if (input.rating !== null && input.rating !== void 0) {
1744
+ if (!Number.isInteger(input.rating) || input.rating < 1 || input.rating > 5) {
1745
+ throw new NeetruError("validation_failed", "rating must be an integer 1-5 or null");
1746
+ }
1747
+ }
1748
+ assertValidComment(input.comment);
1749
+ await httpRequest(config, {
1750
+ method: "POST",
1751
+ path: `/api/sdk/v1/support/tickets/${encodeURIComponent(input.ticketId)}/satisfaction`,
1752
+ body: {
1753
+ rating: input.rating ?? null,
1754
+ comment: input.comment ?? null,
1755
+ skipped: input.skipped
1756
+ },
1757
+ requireAuth: true
1758
+ });
1759
+ },
1760
+ /**
1761
+ * 3.4: contesta um ticket resolvido — reabre e zera o ciclo de
1762
+ * satisfação/notificação. Endpoint canonical:
1763
+ * `POST /api/sdk/v1/support/tickets/{id}/contest`.
1764
+ */
1765
+ async contestTicket(ticketId, opts) {
1766
+ assertTicketId(ticketId);
1767
+ assertValidComment(opts?.comment);
1768
+ await httpRequest(config, {
1769
+ method: "POST",
1770
+ path: `/api/sdk/v1/support/tickets/${encodeURIComponent(ticketId)}/contest`,
1771
+ body: { comment: opts?.comment ?? void 0 },
1772
+ requireAuth: true
1773
+ });
1677
1774
  }
1678
1775
  };
1679
1776
  }
@@ -5094,7 +5191,20 @@ function buildTicketProvider(config, opts) {
5094
5191
  return ticket;
5095
5192
  };
5096
5193
  }
5194
+ var _hasWarnedMissingDbId = false;
5195
+ function warnMissingDbIdOnce(config, dbOpts) {
5196
+ if (_hasWarnedMissingDbId) return;
5197
+ if (dbOpts.dbId) return;
5198
+ if (config.env === "prod") return;
5199
+ if (typeof window === "undefined") return;
5200
+ if (typeof console === "undefined" || typeof console.warn !== "function") return;
5201
+ _hasWarnedMissingDbId = true;
5202
+ console.warn(
5203
+ "[neetru/sdk] client.db sem `dbId` expl\xEDcito: escritas client-side (add/set/update/remove/list) caem no datastore gen\xE9rico do Core, escopado por tenant/produto (header x-neetru-tenant) \u2014 `dbId` s\xF3 nomeia o cache offline LOCAL (IndexedDB), n\xE3o roteia nem escopa nada no servidor. Veja o JSDoc de NeetruDbOptions.dbId."
5204
+ );
5205
+ }
5097
5206
  function createNeetruDb(config, dbOpts = {}) {
5207
+ warnMissingDbIdOnce(config, dbOpts);
5098
5208
  let resolvedTransport;
5099
5209
  if (dbOpts.transport !== void 0) {
5100
5210
  resolvedTransport = dbOpts.transport;
@@ -6517,7 +6627,18 @@ var MockSupport = class {
6517
6627
  severity: input.severity ?? "normal",
6518
6628
  status: "open",
6519
6629
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
6520
- productSlug: input.productSlug
6630
+ productSlug: input.productSlug,
6631
+ // 3.4 (product-feedback): espelha o contrato — campos novos nullable,
6632
+ // populados a partir do input quando fornecidos.
6633
+ pageUrl: typeof input.pageUrl === "string" ? input.pageUrl : null,
6634
+ screenshotUrl: typeof input.screenshotBase64 === "string" ? input.screenshotBase64 : null,
6635
+ resolvedAt: null,
6636
+ resolvedNotifiedAt: null,
6637
+ satisfactionAttempts: 0,
6638
+ lastSatisfactionAttemptAt: null,
6639
+ satisfiedAt: null,
6640
+ rating: null,
6641
+ satisfactionComment: null
6521
6642
  };
6522
6643
  this._tickets.push(ticket);
6523
6644
  return ticket;
@@ -6528,6 +6649,62 @@ var MockSupport = class {
6528
6649
  }
6529
6650
  return [...this._tickets];
6530
6651
  }
6652
+ _find(ticketId) {
6653
+ if (!ticketId || typeof ticketId !== "string") {
6654
+ throw new NeetruError("validation_failed", "ticketId is required");
6655
+ }
6656
+ const ticket = this._tickets.find((t) => t.id === ticketId);
6657
+ if (!ticket) {
6658
+ throw new NeetruError("not_found", `ticket ${ticketId} not found`);
6659
+ }
6660
+ return ticket;
6661
+ }
6662
+ /** 3.4: espelha `markResolvedSeen` — idempotente (só seta se ainda null). */
6663
+ async markResolvedSeen(ticketId) {
6664
+ const ticket = this._find(ticketId);
6665
+ if (ticket.resolvedNotifiedAt === null) {
6666
+ ticket.resolvedNotifiedAt = (/* @__PURE__ */ new Date()).toISOString();
6667
+ }
6668
+ }
6669
+ /** 3.4: espelha `recordSatisfactionAttempt` — incrementa contador. */
6670
+ async recordSatisfactionAttempt(ticketId) {
6671
+ const ticket = this._find(ticketId);
6672
+ ticket.satisfactionAttempts += 1;
6673
+ ticket.lastSatisfactionAttemptAt = (/* @__PURE__ */ new Date()).toISOString();
6674
+ }
6675
+ /** 3.4: espelha `submitSatisfaction` — terminal, seta `satisfiedAt`. */
6676
+ async submitSatisfaction(input) {
6677
+ if (!input || typeof input !== "object") {
6678
+ throw new NeetruError("validation_failed", "input is required");
6679
+ }
6680
+ if (input.rating !== null && input.rating !== void 0) {
6681
+ if (!Number.isInteger(input.rating) || input.rating < 1 || input.rating > 5) {
6682
+ throw new NeetruError("validation_failed", "rating must be an integer 1-5 or null");
6683
+ }
6684
+ }
6685
+ if (typeof input.comment === "string" && input.comment.length > 2e3) {
6686
+ throw new NeetruError("validation_failed", "comment max 2000 chars");
6687
+ }
6688
+ const ticket = this._find(input.ticketId);
6689
+ ticket.satisfiedAt = (/* @__PURE__ */ new Date()).toISOString();
6690
+ ticket.rating = input.rating ?? null;
6691
+ ticket.satisfactionComment = input.comment ?? null;
6692
+ }
6693
+ /** 3.4: espelha `contestTicket` — reabre e zera o ciclo de satisfação. */
6694
+ async contestTicket(ticketId, opts) {
6695
+ if (typeof opts?.comment === "string" && opts.comment.length > 2e3) {
6696
+ throw new NeetruError("validation_failed", "comment max 2000 chars");
6697
+ }
6698
+ const ticket = this._find(ticketId);
6699
+ ticket.status = "open";
6700
+ ticket.resolvedAt = null;
6701
+ ticket.resolvedNotifiedAt = null;
6702
+ ticket.satisfactionAttempts = 0;
6703
+ ticket.lastSatisfactionAttemptAt = null;
6704
+ ticket.satisfiedAt = null;
6705
+ ticket.rating = null;
6706
+ ticket.satisfactionComment = null;
6707
+ }
6531
6708
  /** Test helper. */
6532
6709
  __reset() {
6533
6710
  this._tickets = [];