tempest-react-sdk 0.45.1 → 0.47.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 (32) hide show
  1. package/dist/components/AIChat/AIChatTurn.cjs +1 -1
  2. package/dist/components/AIChat/AIChatTurn.js +4 -4
  3. package/dist/components/AppBar/use-sticky-body-warning.cjs +1 -1
  4. package/dist/components/AppBar/use-sticky-body-warning.cjs.map +1 -1
  5. package/dist/components/AppBar/use-sticky-body-warning.js +5 -8
  6. package/dist/components/AppBar/use-sticky-body-warning.js.map +1 -1
  7. package/dist/components/DataTable/DataTable.cjs +1 -1
  8. package/dist/components/DataTable/DataTable.js +11 -11
  9. package/dist/components/DataTable/use-dev-warnings.cjs +1 -1
  10. package/dist/components/DataTable/use-dev-warnings.cjs.map +1 -1
  11. package/dist/components/DataTable/use-dev-warnings.js +8 -7
  12. package/dist/components/DataTable/use-dev-warnings.js.map +1 -1
  13. package/dist/components/NotificationCenter/NotificationCenter.cjs +1 -1
  14. package/dist/components/NotificationCenter/NotificationCenter.js +21 -21
  15. package/dist/forms/masked-inputs.cjs +1 -1
  16. package/dist/forms/masked-inputs.js +12 -12
  17. package/dist/http/api-client.cjs +1 -1
  18. package/dist/http/api-client.cjs.map +1 -1
  19. package/dist/http/api-client.js +46 -21
  20. package/dist/http/api-client.js.map +1 -1
  21. package/dist/icons/Icon.cjs +1 -1
  22. package/dist/icons/Icon.cjs.map +1 -1
  23. package/dist/icons/Icon.js +10 -12
  24. package/dist/icons/Icon.js.map +1 -1
  25. package/dist/tempest-react-sdk.cjs +1 -1
  26. package/dist/tempest-react-sdk.d.ts +70 -0
  27. package/dist/tempest-react-sdk.js +330 -329
  28. package/dist/utils/dev-mode.cjs +2 -0
  29. package/dist/utils/dev-mode.cjs.map +1 -0
  30. package/dist/utils/dev-mode.js +12 -0
  31. package/dist/utils/dev-mode.js.map +1 -0
  32. package/package.json +1 -1
@@ -1,3 +1,3 @@
1
- const e=require("../../utils/cn.cjs"),t=require("../VisuallyHidden/VisuallyHidden.cjs"),n=require("../../utils/numbers.cjs"),r=require("../CopyButton/CopyButton.cjs"),i=require("../Collapsible/Collapsible.cjs"),a=require("./ai-chat-turns.cjs"),o=require("./AIChat.module.cjs"),s=require("../Markdown/Markdown.cjs");let c=require("react"),l=require("react/jsx-runtime"),u=require("lucide-react");function d({message:d,locale:f=`pt-BR`,canRegenerate:p=!1,onRegenerate:m,onFeedback:h,onEditSubmit:g,onEditError:_,onRetry:v,renderAvatar:y,renderContent:b,vote:x,defaultReasoningOpen:S=!1,showLineNumbers:C=!1}){let w=a.aiChatStrings(f),{role:T,content:E,reasoning:D,streaming:O,error:k,model:A,createdAt:j,attachments:M}=d,[N,P]=(0,c.useState)(null),[F,I]=(0,c.useState)(!1),[L,R]=(0,c.useState)(E),[z,B]=(0,c.useState)(!1),V=x??N,H=T===`user`,U=(0,c.useMemo)(()=>(0,l.jsx)(s.Markdown,{source:E,showLineNumbers:C,className:o.default.markdownRoot}),[E,C]),W=b?b(d):H?(0,l.jsx)(`div`,{className:o.default.plain,children:E}):E===``?null:U,G=async()=>{let e=L.trim();if(!(!e||z||!g)){B(!0);try{await g(d,e),I(!1)}catch(e){_?.(e)}finally{B(!1)}}},K=e=>{V!==e&&(P(e),h?.(d,e))},q=!O&&!F,J=h!==void 0||p&&m!==void 0;return(0,l.jsxs)(`article`,{className:e.cn(o.default.turn,H&&o.default.userTurn,T===`system`&&o.default.systemTurn,O&&o.default.streamingTurn),"aria-busy":O||void 0,"data-role":T,children:[(0,l.jsxs)(`header`,{className:o.default.turnHeader,children:[y&&(0,l.jsx)(`span`,{className:o.default.avatar,"aria-hidden":`true`,children:y(d)}),(0,l.jsx)(`span`,{className:e.cn(o.default.role,H&&o.default.visuallyHiddenRole),children:a.roleLabel(T,w)}),A&&!H&&(0,l.jsx)(`span`,{className:o.default.model,children:A}),j!==void 0&&(0,l.jsx)(`time`,{className:o.default.time,dateTime:new Date(j).toISOString(),children:a.turnTime(j,f)})]}),M&&M.length>0&&(0,l.jsx)(`ul`,{className:o.default.attachments,children:M.map(e=>(0,l.jsx)(`li`,{className:o.default.attachment,children:e.url?(0,l.jsx)(`img`,{className:o.default.thumb,src:e.url,alt:e.name}):(0,l.jsxs)(l.Fragment,{children:[(0,l.jsx)(`span`,{className:o.default.attachmentName,children:e.name}),(0,l.jsx)(`span`,{className:o.default.attachmentMeta,children:e.size===void 0?e.mimeType:n.formatBytes(e.size)})]})},e.id))}),D&&(0,l.jsx)(i.Collapsible,{className:o.default.reasoning,defaultOpen:S||O===!0&&E===``,trigger:(0,l.jsxs)(l.Fragment,{children:[(0,l.jsx)(u.ChevronRight,{className:o.default.reasoningChevron,size:14,"aria-hidden":!0}),w.reasoning]}),children:(0,l.jsx)(`div`,{className:o.default.reasoningBody,children:D})}),F?(0,l.jsxs)(`div`,{className:o.default.editor,children:[(0,l.jsx)(t.VisuallyHidden,{children:(0,l.jsx)(`label`,{htmlFor:`${d.id}-edit`,children:w.editing})}),(0,l.jsx)(`textarea`,{id:`${d.id}-edit`,className:o.default.editField,value:L,rows:Math.min(L.split(`
1
+ const e=require("../../utils/cn.cjs"),t=require("../../utils/numbers.cjs"),n=require("../VisuallyHidden/VisuallyHidden.cjs"),r=require("../CopyButton/CopyButton.cjs"),i=require("../Collapsible/Collapsible.cjs"),a=require("./ai-chat-turns.cjs"),o=require("./AIChat.module.cjs"),s=require("../Markdown/Markdown.cjs");let c=require("react"),l=require("react/jsx-runtime"),u=require("lucide-react");function d({message:d,locale:f=`pt-BR`,canRegenerate:p=!1,onRegenerate:m,onFeedback:h,onEditSubmit:g,onEditError:_,onRetry:v,renderAvatar:y,renderContent:b,vote:x,defaultReasoningOpen:S=!1,showLineNumbers:C=!1}){let w=a.aiChatStrings(f),{role:T,content:E,reasoning:D,streaming:O,error:k,model:A,createdAt:j,attachments:M}=d,[N,P]=(0,c.useState)(null),[F,I]=(0,c.useState)(!1),[L,R]=(0,c.useState)(E),[z,B]=(0,c.useState)(!1),V=x??N,H=T===`user`,U=(0,c.useMemo)(()=>(0,l.jsx)(s.Markdown,{source:E,showLineNumbers:C,className:o.default.markdownRoot}),[E,C]),W=b?b(d):H?(0,l.jsx)(`div`,{className:o.default.plain,children:E}):E===``?null:U,G=async()=>{let e=L.trim();if(!(!e||z||!g)){B(!0);try{await g(d,e),I(!1)}catch(e){_?.(e)}finally{B(!1)}}},K=e=>{V!==e&&(P(e),h?.(d,e))},q=!O&&!F,J=h!==void 0||p&&m!==void 0;return(0,l.jsxs)(`article`,{className:e.cn(o.default.turn,H&&o.default.userTurn,T===`system`&&o.default.systemTurn,O&&o.default.streamingTurn),"aria-busy":O||void 0,"data-role":T,children:[(0,l.jsxs)(`header`,{className:o.default.turnHeader,children:[y&&(0,l.jsx)(`span`,{className:o.default.avatar,"aria-hidden":`true`,children:y(d)}),(0,l.jsx)(`span`,{className:e.cn(o.default.role,H&&o.default.visuallyHiddenRole),children:a.roleLabel(T,w)}),A&&!H&&(0,l.jsx)(`span`,{className:o.default.model,children:A}),j!==void 0&&(0,l.jsx)(`time`,{className:o.default.time,dateTime:new Date(j).toISOString(),children:a.turnTime(j,f)})]}),M&&M.length>0&&(0,l.jsx)(`ul`,{className:o.default.attachments,children:M.map(e=>(0,l.jsx)(`li`,{className:o.default.attachment,children:e.url?(0,l.jsx)(`img`,{className:o.default.thumb,src:e.url,alt:e.name}):(0,l.jsxs)(l.Fragment,{children:[(0,l.jsx)(`span`,{className:o.default.attachmentName,children:e.name}),(0,l.jsx)(`span`,{className:o.default.attachmentMeta,children:e.size===void 0?e.mimeType:t.formatBytes(e.size)})]})},e.id))}),D&&(0,l.jsx)(i.Collapsible,{className:o.default.reasoning,defaultOpen:S||O===!0&&E===``,trigger:(0,l.jsxs)(l.Fragment,{children:[(0,l.jsx)(u.ChevronRight,{className:o.default.reasoningChevron,size:14,"aria-hidden":!0}),w.reasoning]}),children:(0,l.jsx)(`div`,{className:o.default.reasoningBody,children:D})}),F?(0,l.jsxs)(`div`,{className:o.default.editor,children:[(0,l.jsx)(n.VisuallyHidden,{children:(0,l.jsx)(`label`,{htmlFor:`${d.id}-edit`,children:w.editing})}),(0,l.jsx)(`textarea`,{id:`${d.id}-edit`,className:o.default.editField,value:L,rows:Math.min(L.split(`
2
2
  `).length+1,10),onChange:e=>R(e.target.value)}),(0,l.jsxs)(`div`,{className:o.default.editActions,children:[(0,l.jsx)(`button`,{type:`button`,className:o.default.ghost,onClick:()=>{R(E),I(!1)},children:w.cancel}),(0,l.jsx)(`button`,{type:`button`,className:o.default.primary,disabled:z||L.trim()===``,onClick:()=>void G(),children:w.save})]})]}):(0,l.jsxs)(`div`,{className:e.cn(o.default.body,O&&o.default.streamingBody),children:[W,O===!0&&E===``&&(0,l.jsxs)(`span`,{className:o.default.dots,"aria-hidden":`true`,children:[(0,l.jsx)(`span`,{}),(0,l.jsx)(`span`,{}),(0,l.jsx)(`span`,{})]})]}),k&&(0,l.jsxs)(`p`,{className:o.default.error,role:`alert`,children:[(0,l.jsx)(`span`,{children:k}),v&&(0,l.jsx)(`button`,{type:`button`,className:o.default.retry,onClick:()=>v(d),children:w.retry})]}),q&&(0,l.jsxs)(`div`,{className:o.default.actions,"aria-label":w.turnActions,role:`group`,children:[(0,l.jsx)(r.CopyButton,{className:o.default.action,value:E,"aria-label":w.copy,title:w.copy,children:(0,l.jsx)(u.Copy,{size:14,"aria-hidden":!0})}),H&&g&&(0,l.jsx)(`button`,{type:`button`,className:o.default.action,"aria-label":w.edit,title:w.edit,onClick:()=>{R(E),I(!0)},children:(0,l.jsx)(u.Pencil,{size:14,"aria-hidden":!0})}),!H&&J&&(0,l.jsxs)(l.Fragment,{children:[p&&m&&(0,l.jsx)(`button`,{type:`button`,className:o.default.action,"aria-label":w.regenerate,title:w.regenerate,onClick:()=>m(d),children:(0,l.jsx)(u.RefreshCw,{size:14,"aria-hidden":!0})}),h&&(0,l.jsxs)(l.Fragment,{children:[(0,l.jsx)(`button`,{type:`button`,className:e.cn(o.default.action,V===`up`&&o.default.actionActive),"aria-label":w.good,title:w.good,"aria-pressed":V===`up`,onClick:()=>K(`up`),children:(0,l.jsx)(u.ThumbsUp,{size:14,"aria-hidden":!0})}),(0,l.jsx)(`button`,{type:`button`,className:e.cn(o.default.action,V===`down`&&o.default.actionActive),"aria-label":w.bad,title:w.bad,"aria-pressed":V===`down`,onClick:()=>K(`down`),children:(0,l.jsx)(u.ThumbsDown,{size:14,"aria-hidden":!0})})]})]})]})]})}exports.AIChatTurn=d;
3
3
  //# sourceMappingURL=AIChatTurn.cjs.map
@@ -1,6 +1,6 @@
1
1
  import { cn as e } from "../../utils/cn.js";
2
- import { VisuallyHidden as t } from "../VisuallyHidden/VisuallyHidden.js";
3
- import { formatBytes as n } from "../../utils/numbers.js";
2
+ import { formatBytes as t } from "../../utils/numbers.js";
3
+ import { VisuallyHidden as n } from "../VisuallyHidden/VisuallyHidden.js";
4
4
  import { CopyButton as ee } from "../CopyButton/CopyButton.js";
5
5
  import { Collapsible as r } from "../Collapsible/Collapsible.js";
6
6
  import { aiChatStrings as i, roleLabel as a, turnTime as o } from "./ai-chat-turns.js";
@@ -74,7 +74,7 @@ function b({ message: b, locale: x = "pt-BR", canRegenerate: S = !1, onRegenerat
74
74
  children: e.name
75
75
  }), /* @__PURE__ */ f("span", {
76
76
  className: s.attachmentMeta,
77
- children: e.size === void 0 ? e.mimeType : n(e.size)
77
+ children: e.size === void 0 ? e.mimeType : t(e.size)
78
78
  })] })
79
79
  }, e.id))
80
80
  }),
@@ -94,7 +94,7 @@ function b({ message: b, locale: x = "pt-BR", canRegenerate: S = !1, onRegenerat
94
94
  W ? /* @__PURE__ */ p("div", {
95
95
  className: s.editor,
96
96
  children: [
97
- /* @__PURE__ */ f(t, { children: /* @__PURE__ */ f("label", {
97
+ /* @__PURE__ */ f(n, { children: /* @__PURE__ */ f("label", {
98
98
  htmlFor: `${b.id}-edit`,
99
99
  children: N.editing
100
100
  }) }),
@@ -1,2 +1,2 @@
1
- let e=require("react");var t="[tempest-react-sdk] <AppBar sticky /> will not stick: `document.body` has `overflow-x: hidden`, which forces its computed `overflow-y` to `auto` and makes the body a scroll container. The bar then pins to the body's scrollport instead of the viewport and scrolls off the top with the content — most visibly on Chrome Android. Fix: `html, body { overflow-x: clip }`, on both elements, since neither clamps alone. Pass `sticky={false}` if the bar is meant to scroll away.";function n(){return!1}var r=!1;function i(i){(0,e.useEffect)(()=>{!i||r||!n()||typeof document>`u`||!document.body||getComputedStyle(document.body).overflowX===`hidden`&&(r=!0,console.warn(t))},[i])}exports.useStickyBodyWarning=i;
1
+ const e=require("../../utils/dev-mode.cjs");let t=require("react");var n="[tempest-react-sdk] <AppBar sticky /> will not stick: `document.body` has `overflow-x: hidden`, which forces its computed `overflow-y` to `auto` and makes the body a scroll container. The bar then pins to the body's scrollport instead of the viewport and scrolls off the top with the content — most visibly on Chrome Android. Fix: `html, body { overflow-x: clip }`, on both elements, since neither clamps alone. Pass `sticky={false}` if the bar is meant to scroll away.",r=!1;function i(i){(0,t.useEffect)(()=>{!i||r||!e.isDevBuild()||typeof document>`u`||!document.body||getComputedStyle(document.body).overflowX===`hidden`&&(r=!0,console.warn(n))},[i])}exports.useStickyBodyWarning=i;
2
2
  //# sourceMappingURL=use-sticky-body-warning.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"use-sticky-body-warning.cjs","names":[],"sources":["../../../src/components/AppBar/use-sticky-body-warning.ts"],"sourcesContent":["import { useEffect } from \"react\";\n\n/**\n * The one line of the app's own CSS that silently defeats `sticky`.\n *\n * `body { overflow-x: hidden }` is how most apps stop a too-wide child from\n * panning the page sideways. It also, per CSS Overflow 3, forces the computed\n * `overflow-y` to `auto` — a value the author never wrote — which makes the\n * body a **scroll container**. Every sticky descendant then pins to the body's\n * scrollport instead of the viewport, and that scrollport travels with the\n * document: measured in Chromium at 390px with a long page, the bar sat at\n * `top: -900px` after scrolling 900px, i.e. off screen.\n *\n * Nothing about that reads as a CSS problem from the app's side. The bar still\n * reports `position: sticky` in DevTools, the markup is unchanged, and on\n * desktop the screens usually fit without scrolling so the bug never shows. It\n * surfaces on Chrome Android, where the collapsing URL bar makes every screen a\n * scrolling screen — and it gets reported as \"the app bar is broken\", because\n * that is what the user sees.\n *\n * Hence this warning: the SDK cannot fix it from inside the component (a sticky\n * element has no way to opt out of its scroll container, and a rule the SDK\n * ships in its reset loses to the app's own stylesheet, which is imported\n * after), but it can name the cause in one console line.\n *\n * `clip` is the fix because it clamps the same horizontal overflow without\n * creating a scroll container. It has to be on `html` **and** `body` — with it\n * on only one of them the document still panned to `x: 500` in the same\n * measurement.\n */\nconst MESSAGE =\n \"[tempest-react-sdk] <AppBar sticky /> will not stick: `document.body` has \" +\n \"`overflow-x: hidden`, which forces its computed `overflow-y` to `auto` and makes the \" +\n \"body a scroll container. The bar then pins to the body's scrollport instead of the \" +\n \"viewport and scrolls off the top with the content — most visibly on Chrome Android. \" +\n \"Fix: `html, body { overflow-x: clip }`, on both elements, since neither clamps alone. \" +\n \"Pass `sticky={false}` if the bar is meant to scroll away.\";\n\n/**\n * Whether the bundle was built for development.\n *\n * Guarded because the SDK also runs where `import.meta.env` does not exist — a\n * service-worker context, a Node test runner, a build-time script.\n */\nfunction isDev(): boolean {\n return Boolean(import.meta.env?.DEV);\n}\n\n/** Set once the warning has been printed, so re-renders do not bury the console. */\nlet warned = false;\n\n/**\n * Reset the warn-once latch. Test-only — the latch is module state, and a suite\n * that renders more than one bar would otherwise see the warning only once.\n */\nexport function resetStickyBodyWarning(): void {\n warned = false;\n}\n\n/**\n * Warn once, in development, when the page's own CSS will keep a sticky bar\n * from sticking.\n *\n * Reads the body's `overflow-x` rather than walking the ancestors, because that\n * single declaration is the whole failure mode: an app that genuinely wants the\n * body to scroll writes `overflow-y: auto` itself, and a sticky bar inside a\n * scroll container the app chose on purpose behaves exactly as intended. What\n * is never intentional is acquiring that scroll container as a side effect of\n * clamping the other axis.\n *\n * @param enabled - Whether the bar is actually sticky. A bar rendered with\n * `sticky={false}` has nothing to lose and stays quiet.\n */\nexport function useStickyBodyWarning(enabled: boolean): void {\n useEffect(() => {\n if (!enabled || warned || !isDev()) return;\n if (typeof document === \"undefined\" || !document.body) return;\n if (getComputedStyle(document.body).overflowX !== \"hidden\") return;\n warned = true;\n console.warn(MESSAGE);\n }, [enabled]);\n}\n"],"mappings":"uBA8BA,IAAM,EACF,wdAaJ,SAAS,GAAiB,CACtB,MAAO,CAAA,CACX,CAGA,IAAI,EAAS,GAwBb,SAAgB,EAAqB,EAAwB,EACzD,EAAA,EAAA,UAAA,KAAgB,CACR,CAAC,GAAW,GAAU,CAAC,EAAM,GAC7B,OAAO,SAAa,KAAe,CAAC,SAAS,MAC7C,iBAAiB,SAAS,IAAI,CAAC,CAAC,YAAc,WAClD,EAAS,GACT,QAAQ,KAAK,CAAO,EACxB,EAAG,CAAC,CAAO,CAAC,CAChB"}
1
+ {"version":3,"file":"use-sticky-body-warning.cjs","names":[],"sources":["../../../src/components/AppBar/use-sticky-body-warning.ts"],"sourcesContent":["import { useEffect } from \"react\";\n\nimport { isDevBuild } from \"../../utils\";\n\n/**\n * The one line of the app's own CSS that silently defeats `sticky`.\n *\n * `body { overflow-x: hidden }` is how most apps stop a too-wide child from\n * panning the page sideways. It also, per CSS Overflow 3, forces the computed\n * `overflow-y` to `auto` — a value the author never wrote — which makes the\n * body a **scroll container**. Every sticky descendant then pins to the body's\n * scrollport instead of the viewport, and that scrollport travels with the\n * document: measured in Chromium at 390px with a long page, the bar sat at\n * `top: -900px` after scrolling 900px, i.e. off screen.\n *\n * Nothing about that reads as a CSS problem from the app's side. The bar still\n * reports `position: sticky` in DevTools, the markup is unchanged, and on\n * desktop the screens usually fit without scrolling so the bug never shows. It\n * surfaces on Chrome Android, where the collapsing URL bar makes every screen a\n * scrolling screen — and it gets reported as \"the app bar is broken\", because\n * that is what the user sees.\n *\n * Hence this warning: the SDK cannot fix it from inside the component (a sticky\n * element has no way to opt out of its scroll container, and a rule the SDK\n * ships in its reset loses to the app's own stylesheet, which is imported\n * after), but it can name the cause in one console line.\n *\n * `clip` is the fix because it clamps the same horizontal overflow without\n * creating a scroll container. It has to be on `html` **and** `body` — with it\n * on only one of them the document still panned to `x: 500` in the same\n * measurement.\n */\nconst MESSAGE =\n \"[tempest-react-sdk] <AppBar sticky /> will not stick: `document.body` has \" +\n \"`overflow-x: hidden`, which forces its computed `overflow-y` to `auto` and makes the \" +\n \"body a scroll container. The bar then pins to the body's scrollport instead of the \" +\n \"viewport and scrolls off the top with the content — most visibly on Chrome Android. \" +\n \"Fix: `html, body { overflow-x: clip }`, on both elements, since neither clamps alone. \" +\n \"Pass `sticky={false}` if the bar is meant to scroll away.\";\n\n/** Set once the warning has been printed, so re-renders do not bury the console. */\nlet warned = false;\n\n/**\n * Reset the warn-once latch. Test-only — the latch is module state, and a suite\n * that renders more than one bar would otherwise see the warning only once.\n */\nexport function resetStickyBodyWarning(): void {\n warned = false;\n}\n\n/**\n * Warn once, in development, when the page's own CSS will keep a sticky bar\n * from sticking.\n *\n * Reads the body's `overflow-x` rather than walking the ancestors, because that\n * single declaration is the whole failure mode: an app that genuinely wants the\n * body to scroll writes `overflow-y: auto` itself, and a sticky bar inside a\n * scroll container the app chose on purpose behaves exactly as intended. What\n * is never intentional is acquiring that scroll container as a side effect of\n * clamping the other axis.\n *\n * @param enabled - Whether the bar is actually sticky. A bar rendered with\n * `sticky={false}` has nothing to lose and stays quiet.\n */\nexport function useStickyBodyWarning(enabled: boolean): void {\n useEffect(() => {\n if (!enabled || warned || !isDevBuild()) return;\n if (typeof document === \"undefined\" || !document.body) return;\n if (getComputedStyle(document.body).overflowX !== \"hidden\") return;\n warned = true;\n console.warn(MESSAGE);\n }, [enabled]);\n}\n"],"mappings":"mEAgCA,IAAM,EACF,wdAQA,EAAS,GAwBb,SAAgB,EAAqB,EAAwB,EACzD,EAAA,EAAA,UAAA,KAAgB,CACR,CAAC,GAAW,GAAU,CAAC,EAAA,WAAW,GAClC,OAAO,SAAa,KAAe,CAAC,SAAS,MAC7C,iBAAiB,SAAS,IAAI,CAAC,CAAC,YAAc,WAClD,EAAS,GACT,QAAQ,KAAK,CAAO,EACxB,EAAG,CAAC,CAAO,CAAC,CAChB"}
@@ -1,13 +1,10 @@
1
- import { useEffect as e } from "react";
1
+ import { isDevBuild as e } from "../../utils/dev-mode.js";
2
+ import { useEffect as t } from "react";
2
3
  //#region src/components/AppBar/use-sticky-body-warning.ts
3
- var t = "[tempest-react-sdk] <AppBar sticky /> will not stick: `document.body` has `overflow-x: hidden`, which forces its computed `overflow-y` to `auto` and makes the body a scroll container. The bar then pins to the body's scrollport instead of the viewport and scrolls off the top with the content — most visibly on Chrome Android. Fix: `html, body { overflow-x: clip }`, on both elements, since neither clamps alone. Pass `sticky={false}` if the bar is meant to scroll away.";
4
- function n() {
5
- return !1;
6
- }
7
- var r = !1;
4
+ var n = "[tempest-react-sdk] <AppBar sticky /> will not stick: `document.body` has `overflow-x: hidden`, which forces its computed `overflow-y` to `auto` and makes the body a scroll container. The bar then pins to the body's scrollport instead of the viewport and scrolls off the top with the content — most visibly on Chrome Android. Fix: `html, body { overflow-x: clip }`, on both elements, since neither clamps alone. Pass `sticky={false}` if the bar is meant to scroll away.", r = !1;
8
5
  function i(i) {
9
- e(() => {
10
- !i || r || !n() || typeof document > "u" || !document.body || getComputedStyle(document.body).overflowX === "hidden" && (r = !0, console.warn(t));
6
+ t(() => {
7
+ !i || r || !e() || typeof document > "u" || !document.body || getComputedStyle(document.body).overflowX === "hidden" && (r = !0, console.warn(n));
11
8
  }, [i]);
12
9
  }
13
10
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"use-sticky-body-warning.js","names":[],"sources":["../../../src/components/AppBar/use-sticky-body-warning.ts"],"sourcesContent":["import { useEffect } from \"react\";\n\n/**\n * The one line of the app's own CSS that silently defeats `sticky`.\n *\n * `body { overflow-x: hidden }` is how most apps stop a too-wide child from\n * panning the page sideways. It also, per CSS Overflow 3, forces the computed\n * `overflow-y` to `auto` — a value the author never wrote — which makes the\n * body a **scroll container**. Every sticky descendant then pins to the body's\n * scrollport instead of the viewport, and that scrollport travels with the\n * document: measured in Chromium at 390px with a long page, the bar sat at\n * `top: -900px` after scrolling 900px, i.e. off screen.\n *\n * Nothing about that reads as a CSS problem from the app's side. The bar still\n * reports `position: sticky` in DevTools, the markup is unchanged, and on\n * desktop the screens usually fit without scrolling so the bug never shows. It\n * surfaces on Chrome Android, where the collapsing URL bar makes every screen a\n * scrolling screen — and it gets reported as \"the app bar is broken\", because\n * that is what the user sees.\n *\n * Hence this warning: the SDK cannot fix it from inside the component (a sticky\n * element has no way to opt out of its scroll container, and a rule the SDK\n * ships in its reset loses to the app's own stylesheet, which is imported\n * after), but it can name the cause in one console line.\n *\n * `clip` is the fix because it clamps the same horizontal overflow without\n * creating a scroll container. It has to be on `html` **and** `body` — with it\n * on only one of them the document still panned to `x: 500` in the same\n * measurement.\n */\nconst MESSAGE =\n \"[tempest-react-sdk] <AppBar sticky /> will not stick: `document.body` has \" +\n \"`overflow-x: hidden`, which forces its computed `overflow-y` to `auto` and makes the \" +\n \"body a scroll container. The bar then pins to the body's scrollport instead of the \" +\n \"viewport and scrolls off the top with the content — most visibly on Chrome Android. \" +\n \"Fix: `html, body { overflow-x: clip }`, on both elements, since neither clamps alone. \" +\n \"Pass `sticky={false}` if the bar is meant to scroll away.\";\n\n/**\n * Whether the bundle was built for development.\n *\n * Guarded because the SDK also runs where `import.meta.env` does not exist — a\n * service-worker context, a Node test runner, a build-time script.\n */\nfunction isDev(): boolean {\n return Boolean(import.meta.env?.DEV);\n}\n\n/** Set once the warning has been printed, so re-renders do not bury the console. */\nlet warned = false;\n\n/**\n * Reset the warn-once latch. Test-only — the latch is module state, and a suite\n * that renders more than one bar would otherwise see the warning only once.\n */\nexport function resetStickyBodyWarning(): void {\n warned = false;\n}\n\n/**\n * Warn once, in development, when the page's own CSS will keep a sticky bar\n * from sticking.\n *\n * Reads the body's `overflow-x` rather than walking the ancestors, because that\n * single declaration is the whole failure mode: an app that genuinely wants the\n * body to scroll writes `overflow-y: auto` itself, and a sticky bar inside a\n * scroll container the app chose on purpose behaves exactly as intended. What\n * is never intentional is acquiring that scroll container as a side effect of\n * clamping the other axis.\n *\n * @param enabled - Whether the bar is actually sticky. A bar rendered with\n * `sticky={false}` has nothing to lose and stays quiet.\n */\nexport function useStickyBodyWarning(enabled: boolean): void {\n useEffect(() => {\n if (!enabled || warned || !isDev()) return;\n if (typeof document === \"undefined\" || !document.body) return;\n if (getComputedStyle(document.body).overflowX !== \"hidden\") return;\n warned = true;\n console.warn(MESSAGE);\n }, [enabled]);\n}\n"],"mappings":";;AA8BA,IAAM,IACF;AAaJ,SAAS,IAAiB;CACtB,OAAO,CAAA;AACX;AAGA,IAAI,IAAS;AAwBb,SAAgB,EAAqB,GAAwB;CACzD,QAAgB;EACR,CAAC,KAAW,KAAU,CAAC,EAAM,KAC7B,OAAO,WAAa,OAAe,CAAC,SAAS,QAC7C,iBAAiB,SAAS,IAAI,CAAC,CAAC,cAAc,aAClD,IAAS,IACT,QAAQ,KAAK,CAAO;CACxB,GAAG,CAAC,CAAO,CAAC;AAChB"}
1
+ {"version":3,"file":"use-sticky-body-warning.js","names":[],"sources":["../../../src/components/AppBar/use-sticky-body-warning.ts"],"sourcesContent":["import { useEffect } from \"react\";\n\nimport { isDevBuild } from \"../../utils\";\n\n/**\n * The one line of the app's own CSS that silently defeats `sticky`.\n *\n * `body { overflow-x: hidden }` is how most apps stop a too-wide child from\n * panning the page sideways. It also, per CSS Overflow 3, forces the computed\n * `overflow-y` to `auto` — a value the author never wrote — which makes the\n * body a **scroll container**. Every sticky descendant then pins to the body's\n * scrollport instead of the viewport, and that scrollport travels with the\n * document: measured in Chromium at 390px with a long page, the bar sat at\n * `top: -900px` after scrolling 900px, i.e. off screen.\n *\n * Nothing about that reads as a CSS problem from the app's side. The bar still\n * reports `position: sticky` in DevTools, the markup is unchanged, and on\n * desktop the screens usually fit without scrolling so the bug never shows. It\n * surfaces on Chrome Android, where the collapsing URL bar makes every screen a\n * scrolling screen — and it gets reported as \"the app bar is broken\", because\n * that is what the user sees.\n *\n * Hence this warning: the SDK cannot fix it from inside the component (a sticky\n * element has no way to opt out of its scroll container, and a rule the SDK\n * ships in its reset loses to the app's own stylesheet, which is imported\n * after), but it can name the cause in one console line.\n *\n * `clip` is the fix because it clamps the same horizontal overflow without\n * creating a scroll container. It has to be on `html` **and** `body` — with it\n * on only one of them the document still panned to `x: 500` in the same\n * measurement.\n */\nconst MESSAGE =\n \"[tempest-react-sdk] <AppBar sticky /> will not stick: `document.body` has \" +\n \"`overflow-x: hidden`, which forces its computed `overflow-y` to `auto` and makes the \" +\n \"body a scroll container. The bar then pins to the body's scrollport instead of the \" +\n \"viewport and scrolls off the top with the content — most visibly on Chrome Android. \" +\n \"Fix: `html, body { overflow-x: clip }`, on both elements, since neither clamps alone. \" +\n \"Pass `sticky={false}` if the bar is meant to scroll away.\";\n\n/** Set once the warning has been printed, so re-renders do not bury the console. */\nlet warned = false;\n\n/**\n * Reset the warn-once latch. Test-only — the latch is module state, and a suite\n * that renders more than one bar would otherwise see the warning only once.\n */\nexport function resetStickyBodyWarning(): void {\n warned = false;\n}\n\n/**\n * Warn once, in development, when the page's own CSS will keep a sticky bar\n * from sticking.\n *\n * Reads the body's `overflow-x` rather than walking the ancestors, because that\n * single declaration is the whole failure mode: an app that genuinely wants the\n * body to scroll writes `overflow-y: auto` itself, and a sticky bar inside a\n * scroll container the app chose on purpose behaves exactly as intended. What\n * is never intentional is acquiring that scroll container as a side effect of\n * clamping the other axis.\n *\n * @param enabled - Whether the bar is actually sticky. A bar rendered with\n * `sticky={false}` has nothing to lose and stays quiet.\n */\nexport function useStickyBodyWarning(enabled: boolean): void {\n useEffect(() => {\n if (!enabled || warned || !isDevBuild()) return;\n if (typeof document === \"undefined\" || !document.body) return;\n if (getComputedStyle(document.body).overflowX !== \"hidden\") return;\n warned = true;\n console.warn(MESSAGE);\n }, [enabled]);\n}\n"],"mappings":";;;AAgCA,IAAM,IACF,ydAQA,IAAS;AAwBb,SAAgB,EAAqB,GAAwB;CACzD,QAAgB;EACR,CAAC,KAAW,KAAU,CAAC,EAAW,KAClC,OAAO,WAAa,OAAe,CAAC,SAAS,QAC7C,iBAAiB,SAAS,IAAI,CAAC,CAAC,cAAc,aAClD,IAAS,IACT,QAAQ,KAAK,CAAO;CACxB,GAAG,CAAC,CAAO,CAAC;AAChB"}
@@ -1,2 +1,2 @@
1
- const e=require("../../utils/cn.cjs"),t=require("../../hooks/use-pagination.cjs"),n=require("../../hooks/use-announce.cjs"),r=require("../Pagination/Pagination.cjs"),i=require("../SearchBar/SearchBar.cjs"),a=require("../Table/Table.cjs"),o=require("../../utils/compare-values.cjs"),s=require("./DataTable.module.cjs"),c=require("./EditableCell.cjs"),ee=require("./LoadingRows.cjs"),te=require("./use-dev-warnings.cjs"),ne=require("./edit-labels.cjs");let l=require("react"),u=require("react/jsx-runtime");function d(e,t){return`${String(e)}::${String(t)}`}function f(e){return typeof e.header==`string`?e.header:String(e.key)}function p({data:p,columns:m,pageSize:h=10,searchable:g=!1,searchKeys:_,initialSort:re,rowKey:v=(e,t)=>t,emptyMessage:ie,onCellChange:y,editLabels:b,totalItems:ae,page:x,onPageChange:S,manualSort:oe,onSortChange:se,manualSearch:ce,onSearchChange:le,loading:C=!1,className:ue,...de}){let[w,fe]=(0,l.useState)(``),[T,pe]=(0,l.useState)(re??null),{page:me,setPage:E}=t.usePagination(1,h),D=n.useAnnounce(),O=ae!==void 0,k=oe??O,A=ce??O,j=x??me,M=(0,l.useCallback)(e=>{x===void 0&&E(e),S?.(e)},[x,E,S]);te.useDevWarnings({serverMode:O,controlledPage:x,onPageChange:S,sortIsManual:k,onSortChange:se});let[N,P]=(0,l.useState)(null),[F,I]=(0,l.useState)(null),[L,R]=(0,l.useState)({}),[z,B]=(0,l.useState)({}),[V,H]=(0,l.useState)({}),U=(0,l.useMemo)(()=>({...ne.DEFAULT_EDIT_LABELS,...b}),[b]),W=y!==void 0&&m.some(e=>e.editable),he=(0,l.useMemo)(()=>_&&_.length>0?_:m.filter(e=>{let t=p.find(t=>t[e.key]!=null),n=t?t[e.key]:void 0;return typeof n==`string`||typeof n==`number`}).map(e=>e.key),[_,m,p]),G=(0,l.useMemo)(()=>{let e=w.trim().toLowerCase();return!e||!g||A?p:p.filter(t=>he.some(n=>{let r=t[n];return r!=null&&String(r).toLowerCase().includes(e)}))},[p,w,g,A,he]),K=(0,l.useMemo)(()=>{if(!T||k)return G;let e=T.direction===`asc`?1:-1;return[...G].sort((t,n)=>o.compareValues(t[T.key],n[T.key])*e)},[G,T,k]),ge=ae??K.length,q=Math.max(1,Math.ceil(ge/h));(0,l.useEffect)(()=>{!O&&j>q&&M(q)},[O,j,q,M]);let J=O?j:Math.min(j,q),Y=(0,l.useMemo)(()=>{if(O)return K;let e=(J-1)*h;return K.slice(e,e+h)},[O,K,J,h]);function _e(e){let t=T,n=!t||t.key!==e?{key:e,direction:`asc`}:t.direction===`asc`?{key:e,direction:`desc`}:null;pe(n),se?.(n)}let X=(0,l.useCallback)(e=>(J-1)*h+e,[J,h]),Z=(0,l.useMemo)(()=>{if(!W)return[];let e=[];return Y.forEach((t,n)=>{let r=v(t,X(n));for(let t of m)t.editable&&e.push(d(r,t.key))}),e},[W,Y,m,X]),Q=(0,l.useCallback)((e,t,n)=>n in L?L[n]:e[t.key],[L]),ve=(0,l.useCallback)(async(e,t,n)=>{H(t=>({...t,[e]:!0}));try{await y?.(n),D(U.saved(f(t)))}catch(n){R(t=>{let n={...t};return delete n[e],n});let r=n instanceof Error&&n.message?n.message:U.saveFailed(f(t));B(t=>({...t,[e]:r}))}finally{H(t=>{let n={...t};return delete n[e],n})}},[y,D,U]),$=(0,l.useCallback)((e,t)=>{if(t===`none`){P(null),I(e);return}let n=Z.indexOf(e),r=Z[n+(t===`next`?1:-1)];if(r===void 0){P(null),I(e);return}P(r),I(null)},[Z]),ye=(0,l.useCallback)((e,t,n,r,i,a)=>{let o=Q(e,t,n);if(i===(t.formatEdit?t.formatEdit(e):String(o??``))){$(n,a);return}let s=t.parse?t.parse(i,e):t.editorType===`number`?Number(i):i.trim(),c=t.validate?.(s,e)??null;if(c){B(e=>({...e,[n]:c}));return}B(e=>{let t={...e};return delete t[n],t}),R(e=>({...e,[n]:s})),$(n,a),ve(n,t,{row:e,key:t.key,value:s,previous:o,rowIndex:X(r)})},[Q,$,ve,X]),be=(0,l.useMemo)(()=>m.map(e=>{let t=T?.key===e.key?T?.direction===`asc`?` ▲`:` ▼`:``,n=e.sortable?(0,u.jsxs)(`button`,{type:`button`,className:s.default.sortButton,onClick:()=>_e(e.key),"aria-label":`Ordenar por ${f(e)}`,children:[e.header,(0,u.jsx)(`span`,{className:s.default.sortIndicator,"aria-hidden":!0,children:t})]}):e.header,r=t=>e.render?e.render(t):t[e.key]??null,i=(t,n)=>{let r={...t,[e.key]:n};return e.render?e.render(r):r[e.key]??null},a=W&&e.editable===!0;return{key:String(e.key),header:n,align:e.align,priority:e.priority,width:e.width,render:a?(t,n)=>{let r=d(v(t,X(n)),e.key),a=Q(t,e,r),o=e.formatEdit?e.formatEdit(t):String(a??``);return(0,u.jsx)(c.EditableCell,{text:o,columnLabel:f(e),rowNumber:n+1,inputType:e.editorType??`text`,editing:N===r,refocus:F===r,saving:V[r]===!0,error:z[r]??null,errorId:`tempest-cell-error-${r.replace(/[^\w-]/g,`_`)}`,labels:U,onOpen:()=>{P(r),I(null)},onCommit:(i,a)=>ye(t,e,r,n,i,a),onCancel:()=>{P(null),I(r),B(e=>{let t={...e};return delete t[r],t})},children:i(t,a)})}:r}}),[m,T,W,N,F,V,z,U,L,X,ye]),xe=C&&Y.length===0;return(0,u.jsxs)(`div`,{className:e.cn(s.default.wrapper,ue),...de,children:[g&&(0,u.jsx)(i.SearchBar,{value:w,onChange:e=>{fe(e),M(1),le?.(e)},wrapperClassName:s.default.search}),(0,u.jsx)(`div`,{className:e.cn(C&&!xe&&s.default.pending),"aria-busy":C||void 0,"data-testid":`tempest-datatable-body`,children:xe?(0,u.jsx)(ee.LoadingRows,{columns:be.length,rows:Math.min(h,8)}):(0,u.jsx)(a.Table,{columns:be,data:Y,rowKey:(e,t)=>v(e,X(t)),emptyMessage:ie})}),q>1&&(0,u.jsx)(r.Pagination,{page:J,totalPages:q,onPageChange:M,totalItems:ge})]})}exports.DataTable=p;
1
+ const e=require("../../utils/cn.cjs"),t=require("../../utils/compare-values.cjs"),n=require("../../hooks/use-pagination.cjs"),r=require("../../hooks/use-announce.cjs"),i=require("../Pagination/Pagination.cjs"),a=require("../SearchBar/SearchBar.cjs"),o=require("../Table/Table.cjs"),s=require("./DataTable.module.cjs"),c=require("./EditableCell.cjs"),ee=require("./LoadingRows.cjs"),te=require("./use-dev-warnings.cjs"),ne=require("./edit-labels.cjs");let l=require("react"),u=require("react/jsx-runtime");function d(e,t){return`${String(e)}::${String(t)}`}function f(e){return typeof e.header==`string`?e.header:String(e.key)}function p({data:p,columns:m,pageSize:h=10,searchable:g=!1,searchKeys:_,initialSort:re,rowKey:v=(e,t)=>t,emptyMessage:ie,onCellChange:y,editLabels:b,totalItems:ae,page:x,onPageChange:S,manualSort:oe,onSortChange:se,manualSearch:ce,onSearchChange:le,loading:C=!1,className:ue,...de}){let[w,fe]=(0,l.useState)(``),[T,pe]=(0,l.useState)(re??null),{page:me,setPage:E}=n.usePagination(1,h),D=r.useAnnounce(),O=ae!==void 0,k=oe??O,A=ce??O,j=x??me,M=(0,l.useCallback)(e=>{x===void 0&&E(e),S?.(e)},[x,E,S]);te.useDevWarnings({serverMode:O,controlledPage:x,onPageChange:S,sortIsManual:k,onSortChange:se});let[N,P]=(0,l.useState)(null),[F,I]=(0,l.useState)(null),[L,R]=(0,l.useState)({}),[z,B]=(0,l.useState)({}),[V,H]=(0,l.useState)({}),U=(0,l.useMemo)(()=>({...ne.DEFAULT_EDIT_LABELS,...b}),[b]),W=y!==void 0&&m.some(e=>e.editable),he=(0,l.useMemo)(()=>_&&_.length>0?_:m.filter(e=>{let t=p.find(t=>t[e.key]!=null),n=t?t[e.key]:void 0;return typeof n==`string`||typeof n==`number`}).map(e=>e.key),[_,m,p]),G=(0,l.useMemo)(()=>{let e=w.trim().toLowerCase();return!e||!g||A?p:p.filter(t=>he.some(n=>{let r=t[n];return r!=null&&String(r).toLowerCase().includes(e)}))},[p,w,g,A,he]),K=(0,l.useMemo)(()=>{if(!T||k)return G;let e=T.direction===`asc`?1:-1;return[...G].sort((n,r)=>t.compareValues(n[T.key],r[T.key])*e)},[G,T,k]),ge=ae??K.length,q=Math.max(1,Math.ceil(ge/h));(0,l.useEffect)(()=>{!O&&j>q&&M(q)},[O,j,q,M]);let J=O?j:Math.min(j,q),Y=(0,l.useMemo)(()=>{if(O)return K;let e=(J-1)*h;return K.slice(e,e+h)},[O,K,J,h]);function _e(e){let t=T,n=!t||t.key!==e?{key:e,direction:`asc`}:t.direction===`asc`?{key:e,direction:`desc`}:null;pe(n),se?.(n)}let X=(0,l.useCallback)(e=>(J-1)*h+e,[J,h]),Z=(0,l.useMemo)(()=>{if(!W)return[];let e=[];return Y.forEach((t,n)=>{let r=v(t,X(n));for(let t of m)t.editable&&e.push(d(r,t.key))}),e},[W,Y,m,X]),Q=(0,l.useCallback)((e,t,n)=>n in L?L[n]:e[t.key],[L]),ve=(0,l.useCallback)(async(e,t,n)=>{H(t=>({...t,[e]:!0}));try{await y?.(n),D(U.saved(f(t)))}catch(n){R(t=>{let n={...t};return delete n[e],n});let r=n instanceof Error&&n.message?n.message:U.saveFailed(f(t));B(t=>({...t,[e]:r}))}finally{H(t=>{let n={...t};return delete n[e],n})}},[y,D,U]),$=(0,l.useCallback)((e,t)=>{if(t===`none`){P(null),I(e);return}let n=Z.indexOf(e),r=Z[n+(t===`next`?1:-1)];if(r===void 0){P(null),I(e);return}P(r),I(null)},[Z]),ye=(0,l.useCallback)((e,t,n,r,i,a)=>{let o=Q(e,t,n);if(i===(t.formatEdit?t.formatEdit(e):String(o??``))){$(n,a);return}let s=t.parse?t.parse(i,e):t.editorType===`number`?Number(i):i.trim(),c=t.validate?.(s,e)??null;if(c){B(e=>({...e,[n]:c}));return}B(e=>{let t={...e};return delete t[n],t}),R(e=>({...e,[n]:s})),$(n,a),ve(n,t,{row:e,key:t.key,value:s,previous:o,rowIndex:X(r)})},[Q,$,ve,X]),be=(0,l.useMemo)(()=>m.map(e=>{let t=T?.key===e.key?T?.direction===`asc`?` ▲`:` ▼`:``,n=e.sortable?(0,u.jsxs)(`button`,{type:`button`,className:s.default.sortButton,onClick:()=>_e(e.key),"aria-label":`Ordenar por ${f(e)}`,children:[e.header,(0,u.jsx)(`span`,{className:s.default.sortIndicator,"aria-hidden":!0,children:t})]}):e.header,r=t=>e.render?e.render(t):t[e.key]??null,i=(t,n)=>{let r={...t,[e.key]:n};return e.render?e.render(r):r[e.key]??null},a=W&&e.editable===!0;return{key:String(e.key),header:n,align:e.align,priority:e.priority,width:e.width,render:a?(t,n)=>{let r=d(v(t,X(n)),e.key),a=Q(t,e,r),o=e.formatEdit?e.formatEdit(t):String(a??``);return(0,u.jsx)(c.EditableCell,{text:o,columnLabel:f(e),rowNumber:n+1,inputType:e.editorType??`text`,editing:N===r,refocus:F===r,saving:V[r]===!0,error:z[r]??null,errorId:`tempest-cell-error-${r.replace(/[^\w-]/g,`_`)}`,labels:U,onOpen:()=>{P(r),I(null)},onCommit:(i,a)=>ye(t,e,r,n,i,a),onCancel:()=>{P(null),I(r),B(e=>{let t={...e};return delete t[r],t})},children:i(t,a)})}:r}}),[m,T,W,N,F,V,z,U,L,X,ye]),xe=C&&Y.length===0;return(0,u.jsxs)(`div`,{className:e.cn(s.default.wrapper,ue),...de,children:[g&&(0,u.jsx)(a.SearchBar,{value:w,onChange:e=>{fe(e),M(1),le?.(e)},wrapperClassName:s.default.search}),(0,u.jsx)(`div`,{className:e.cn(C&&!xe&&s.default.pending),"aria-busy":C||void 0,"data-testid":`tempest-datatable-body`,children:xe?(0,u.jsx)(ee.LoadingRows,{columns:be.length,rows:Math.min(h,8)}):(0,u.jsx)(o.Table,{columns:be,data:Y,rowKey:(e,t)=>v(e,X(t)),emptyMessage:ie})}),q>1&&(0,u.jsx)(i.Pagination,{page:J,totalPages:q,onPageChange:M,totalItems:ge})]})}exports.DataTable=p;
2
2
  //# sourceMappingURL=DataTable.cjs.map
@@ -1,10 +1,10 @@
1
1
  import { cn as e } from "../../utils/cn.js";
2
- import { usePagination as t } from "../../hooks/use-pagination.js";
3
- import { useAnnounce as n } from "../../hooks/use-announce.js";
4
- import { Pagination as r } from "../Pagination/Pagination.js";
5
- import { SearchBar as i } from "../SearchBar/SearchBar.js";
6
- import { Table as a } from "../Table/Table.js";
7
- import { compareValues as o } from "../../utils/compare-values.js";
2
+ import { compareValues as t } from "../../utils/compare-values.js";
3
+ import { usePagination as n } from "../../hooks/use-pagination.js";
4
+ import { useAnnounce as r } from "../../hooks/use-announce.js";
5
+ import { Pagination as i } from "../Pagination/Pagination.js";
6
+ import { SearchBar as a } from "../SearchBar/SearchBar.js";
7
+ import { Table as o } from "../Table/Table.js";
8
8
  import s from "./DataTable.module.js";
9
9
  import { EditableCell as c } from "./EditableCell.js";
10
10
  import { LoadingRows as ee } from "./LoadingRows.js";
@@ -20,7 +20,7 @@ function p(e) {
20
20
  return typeof e.header == "string" ? e.header : String(e.key);
21
21
  }
22
22
  function m({ data: m, columns: h, pageSize: g = 10, searchable: _ = !1, searchKeys: v, initialSort: oe, rowKey: y = (e, t) => t, emptyMessage: se, onCellChange: b, editLabels: ce, totalItems: le, page: x, onPageChange: S, manualSort: ue, onSortChange: C, manualSearch: de, onSearchChange: fe, loading: w = !1, className: pe, ...me }) {
23
- let [T, he] = d(""), [E, ge] = d(oe ?? null), { page: _e, setPage: ve } = t(1, g), ye = n(), D = le !== void 0, O = ue ?? D, k = de ?? D, A = x ?? _e, j = l((e) => {
23
+ let [T, he] = d(""), [E, ge] = d(oe ?? null), { page: _e, setPage: ve } = n(1, g), ye = r(), D = le !== void 0, O = ue ?? D, k = de ?? D, A = x ?? _e, j = l((e) => {
24
24
  x === void 0 && ve(e), S?.(e);
25
25
  }, [
26
26
  x,
@@ -59,7 +59,7 @@ function m({ data: m, columns: h, pageSize: g = 10, searchable: _ = !1, searchKe
59
59
  ]), G = u(() => {
60
60
  if (!E || O) return W;
61
61
  let e = E.direction === "asc" ? 1 : -1;
62
- return [...W].sort((t, n) => o(t[E.key], n[E.key]) * e);
62
+ return [...W].sort((n, r) => t(n[E.key], r[E.key]) * e);
63
63
  }, [
64
64
  W,
65
65
  E,
@@ -243,7 +243,7 @@ function m({ data: m, columns: h, pageSize: g = 10, searchable: _ = !1, searchKe
243
243
  className: e(s.wrapper, pe),
244
244
  ...me,
245
245
  children: [
246
- _ && /* @__PURE__ */ f(i, {
246
+ _ && /* @__PURE__ */ f(a, {
247
247
  value: T,
248
248
  onChange: (e) => {
249
249
  he(e), j(1), fe?.(e);
@@ -257,14 +257,14 @@ function m({ data: m, columns: h, pageSize: g = 10, searchable: _ = !1, searchKe
257
257
  children: $ ? /* @__PURE__ */ f(ee, {
258
258
  columns: Te.length,
259
259
  rows: Math.min(g, 8)
260
- }) : /* @__PURE__ */ f(a, {
260
+ }) : /* @__PURE__ */ f(o, {
261
261
  columns: Te,
262
262
  data: J,
263
263
  rowKey: (e, t) => y(e, Y(t)),
264
264
  emptyMessage: se
265
265
  })
266
266
  }),
267
- K > 1 && /* @__PURE__ */ f(r, {
267
+ K > 1 && /* @__PURE__ */ f(i, {
268
268
  page: q,
269
269
  totalPages: K,
270
270
  onPageChange: j,
@@ -1,2 +1,2 @@
1
- let e=require("react");function t({serverMode:t,controlledPage:n,onPageChange:r,sortIsManual:i,onSortChange:a}){(0,e.useEffect)(()=>{process.env.NODE_ENV!==`production`&&(t&&n===void 0&&console.warn("[tempest] <DataTable totalItems> is server mode, which needs a controlled `page`. Without it the pager moves the internal page while `data` keeps showing page 1."),n!==void 0&&!r&&console.warn("[tempest] <DataTable page> is controlled but `onPageChange` is missing, so the pager cannot do anything."),i&&!a&&console.warn("[tempest] <DataTable> is sorting manually but `onSortChange` is missing: clicking a sortable header changes the arrow and nothing else."))},[t,n,r,i,a])}exports.useDevWarnings=t;
1
+ const e=require("../../utils/dev-mode.cjs");let t=require("react");function n({serverMode:n,controlledPage:r,onPageChange:i,sortIsManual:a,onSortChange:o}){(0,t.useEffect)(()=>{e.isDevBuild()&&(n&&r===void 0&&console.warn("[tempest] <DataTable totalItems> is server mode, which needs a controlled `page`. Without it the pager moves the internal page while `data` keeps showing page 1."),r!==void 0&&!i&&console.warn("[tempest] <DataTable page> is controlled but `onPageChange` is missing, so the pager cannot do anything."),a&&!o&&console.warn("[tempest] <DataTable> is sorting manually but `onSortChange` is missing: clicking a sortable header changes the arrow and nothing else."))},[n,r,i,a,o])}exports.useDevWarnings=n;
2
2
  //# sourceMappingURL=use-dev-warnings.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"use-dev-warnings.cjs","names":[],"sources":["../../../src/components/DataTable/use-dev-warnings.ts"],"sourcesContent":["import { useEffect } from \"react\";\n\n/** What {@link useDevWarnings} needs to judge the prop combination. */\nexport interface DataTableDevWarningsInput {\n /** Whether `totalItems` was passed. */\n serverMode: boolean;\n /** The controlled `page`, if any. */\n controlledPage: number | undefined;\n /** The page callback, if any. */\n onPageChange: ((page: number) => void) | undefined;\n /** Whether sorting is delegated. */\n sortIsManual: boolean;\n /** The sort callback, if any. */\n onSortChange: ((sort: never) => void) | undefined;\n}\n\n/**\n * Warn, in development only, about prop combinations that render a table which\n * silently lies.\n *\n * Each of these produces a screen that looks like it works: the header sorts and\n * nothing moves, the pager renders and clicking it does nothing. They are not\n * type errors — every prop is optional on its own — so the only place to catch\n * them is at runtime, once, in dev.\n *\n * @param input - The resolved prop combination.\n */\nexport function useDevWarnings({\n serverMode,\n controlledPage,\n onPageChange,\n sortIsManual,\n onSortChange,\n}: DataTableDevWarningsInput): void {\n useEffect(() => {\n if (process.env.NODE_ENV === \"production\") return;\n\n if (serverMode && controlledPage === undefined) {\n console.warn(\n \"[tempest] <DataTable totalItems> is server mode, which needs a controlled `page`. \" +\n \"Without it the pager moves the internal page while `data` keeps showing page 1.\",\n );\n }\n if (controlledPage !== undefined && !onPageChange) {\n console.warn(\n \"[tempest] <DataTable page> is controlled but `onPageChange` is missing, so the pager cannot do anything.\",\n );\n }\n if (sortIsManual && !onSortChange) {\n console.warn(\n \"[tempest] <DataTable> is sorting manually but `onSortChange` is missing: clicking a sortable header changes the arrow and nothing else.\",\n );\n }\n }, [serverMode, controlledPage, onPageChange, sortIsManual, onSortChange]);\n}\n"],"mappings":"uBA2BA,SAAgB,EAAe,CAC3B,aACA,iBACA,eACA,eACA,gBACgC,EAChC,EAAA,EAAA,UAAA,KAAgB,CACZ,QAAA,IAAA,WAA6B,eAEzB,GAAc,IAAmB,IAAA,IACjC,QAAQ,KACJ,mKAEJ,EAEA,IAAmB,IAAA,IAAa,CAAC,GACjC,QAAQ,KACJ,0GACJ,EAEA,GAAgB,CAAC,GACjB,QAAQ,KACJ,yIACJ,EAER,EAAG,CAAC,EAAY,EAAgB,EAAc,EAAc,CAAY,CAAC,CAC7E"}
1
+ {"version":3,"file":"use-dev-warnings.cjs","names":[],"sources":["../../../src/components/DataTable/use-dev-warnings.ts"],"sourcesContent":["import { useEffect } from \"react\";\n\nimport { isDevBuild } from \"../../utils\";\n\n/** What {@link useDevWarnings} needs to judge the prop combination. */\nexport interface DataTableDevWarningsInput {\n /** Whether `totalItems` was passed. */\n serverMode: boolean;\n /** The controlled `page`, if any. */\n controlledPage: number | undefined;\n /** The page callback, if any. */\n onPageChange: ((page: number) => void) | undefined;\n /** Whether sorting is delegated. */\n sortIsManual: boolean;\n /** The sort callback, if any. */\n onSortChange: ((sort: never) => void) | undefined;\n}\n\n/**\n * Warn, in development only, about prop combinations that render a table which\n * silently lies.\n *\n * Each of these produces a screen that looks like it works: the header sorts and\n * nothing moves, the pager renders and clicking it does nothing. They are not\n * type errors — every prop is optional on its own — so the only place to catch\n * them is at runtime, once, in dev.\n *\n * @param input - The resolved prop combination.\n */\nexport function useDevWarnings({\n serverMode,\n controlledPage,\n onPageChange,\n sortIsManual,\n onSortChange,\n}: DataTableDevWarningsInput): void {\n useEffect(() => {\n if (!isDevBuild()) return;\n\n if (serverMode && controlledPage === undefined) {\n console.warn(\n \"[tempest] <DataTable totalItems> is server mode, which needs a controlled `page`. \" +\n \"Without it the pager moves the internal page while `data` keeps showing page 1.\",\n );\n }\n if (controlledPage !== undefined && !onPageChange) {\n console.warn(\n \"[tempest] <DataTable page> is controlled but `onPageChange` is missing, so the pager cannot do anything.\",\n );\n }\n if (sortIsManual && !onSortChange) {\n console.warn(\n \"[tempest] <DataTable> is sorting manually but `onSortChange` is missing: clicking a sortable header changes the arrow and nothing else.\",\n );\n }\n }, [serverMode, controlledPage, onPageChange, sortIsManual, onSortChange]);\n}\n"],"mappings":"mEA6BA,SAAgB,EAAe,CAC3B,aACA,iBACA,eACA,eACA,gBACgC,EAChC,EAAA,EAAA,UAAA,KAAgB,CACP,EAAA,WAAW,IAEZ,GAAc,IAAmB,IAAA,IACjC,QAAQ,KACJ,mKAEJ,EAEA,IAAmB,IAAA,IAAa,CAAC,GACjC,QAAQ,KACJ,0GACJ,EAEA,GAAgB,CAAC,GACjB,QAAQ,KACJ,yIACJ,EAER,EAAG,CAAC,EAAY,EAAgB,EAAc,EAAc,CAAY,CAAC,CAC7E"}
@@ -1,17 +1,18 @@
1
- import { useEffect as e } from "react";
1
+ import { isDevBuild as e } from "../../utils/dev-mode.js";
2
+ import { useEffect as t } from "react";
2
3
  //#region src/components/DataTable/use-dev-warnings.ts
3
- function t({ serverMode: t, controlledPage: n, onPageChange: r, sortIsManual: i, onSortChange: a }) {
4
- e(() => {
5
- process.env.NODE_ENV !== "production" && (t && n === void 0 && console.warn("[tempest] <DataTable totalItems> is server mode, which needs a controlled `page`. Without it the pager moves the internal page while `data` keeps showing page 1."), n !== void 0 && !r && console.warn("[tempest] <DataTable page> is controlled but `onPageChange` is missing, so the pager cannot do anything."), i && !a && console.warn("[tempest] <DataTable> is sorting manually but `onSortChange` is missing: clicking a sortable header changes the arrow and nothing else."));
4
+ function n({ serverMode: n, controlledPage: r, onPageChange: i, sortIsManual: a, onSortChange: o }) {
5
+ t(() => {
6
+ e() && (n && r === void 0 && console.warn("[tempest] <DataTable totalItems> is server mode, which needs a controlled `page`. Without it the pager moves the internal page while `data` keeps showing page 1."), r !== void 0 && !i && console.warn("[tempest] <DataTable page> is controlled but `onPageChange` is missing, so the pager cannot do anything."), a && !o && console.warn("[tempest] <DataTable> is sorting manually but `onSortChange` is missing: clicking a sortable header changes the arrow and nothing else."));
6
7
  }, [
7
- t,
8
8
  n,
9
9
  r,
10
10
  i,
11
- a
11
+ a,
12
+ o
12
13
  ]);
13
14
  }
14
15
  //#endregion
15
- export { t as useDevWarnings };
16
+ export { n as useDevWarnings };
16
17
 
17
18
  //# sourceMappingURL=use-dev-warnings.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"use-dev-warnings.js","names":[],"sources":["../../../src/components/DataTable/use-dev-warnings.ts"],"sourcesContent":["import { useEffect } from \"react\";\n\n/** What {@link useDevWarnings} needs to judge the prop combination. */\nexport interface DataTableDevWarningsInput {\n /** Whether `totalItems` was passed. */\n serverMode: boolean;\n /** The controlled `page`, if any. */\n controlledPage: number | undefined;\n /** The page callback, if any. */\n onPageChange: ((page: number) => void) | undefined;\n /** Whether sorting is delegated. */\n sortIsManual: boolean;\n /** The sort callback, if any. */\n onSortChange: ((sort: never) => void) | undefined;\n}\n\n/**\n * Warn, in development only, about prop combinations that render a table which\n * silently lies.\n *\n * Each of these produces a screen that looks like it works: the header sorts and\n * nothing moves, the pager renders and clicking it does nothing. They are not\n * type errors — every prop is optional on its own — so the only place to catch\n * them is at runtime, once, in dev.\n *\n * @param input - The resolved prop combination.\n */\nexport function useDevWarnings({\n serverMode,\n controlledPage,\n onPageChange,\n sortIsManual,\n onSortChange,\n}: DataTableDevWarningsInput): void {\n useEffect(() => {\n if (process.env.NODE_ENV === \"production\") return;\n\n if (serverMode && controlledPage === undefined) {\n console.warn(\n \"[tempest] <DataTable totalItems> is server mode, which needs a controlled `page`. \" +\n \"Without it the pager moves the internal page while `data` keeps showing page 1.\",\n );\n }\n if (controlledPage !== undefined && !onPageChange) {\n console.warn(\n \"[tempest] <DataTable page> is controlled but `onPageChange` is missing, so the pager cannot do anything.\",\n );\n }\n if (sortIsManual && !onSortChange) {\n console.warn(\n \"[tempest] <DataTable> is sorting manually but `onSortChange` is missing: clicking a sortable header changes the arrow and nothing else.\",\n );\n }\n }, [serverMode, controlledPage, onPageChange, sortIsManual, onSortChange]);\n}\n"],"mappings":";;AA2BA,SAAgB,EAAe,EAC3B,eACA,mBACA,iBACA,iBACA,mBACgC;CAChC,QAAgB;EACZ,QAAA,IAAA,aAA6B,iBAEzB,KAAc,MAAmB,KAAA,KACjC,QAAQ,KACJ,mKAEJ,GAEA,MAAmB,KAAA,KAAa,CAAC,KACjC,QAAQ,KACJ,0GACJ,GAEA,KAAgB,CAAC,KACjB,QAAQ,KACJ,yIACJ;CAER,GAAG;EAAC;EAAY;EAAgB;EAAc;EAAc;CAAY,CAAC;AAC7E"}
1
+ {"version":3,"file":"use-dev-warnings.js","names":[],"sources":["../../../src/components/DataTable/use-dev-warnings.ts"],"sourcesContent":["import { useEffect } from \"react\";\n\nimport { isDevBuild } from \"../../utils\";\n\n/** What {@link useDevWarnings} needs to judge the prop combination. */\nexport interface DataTableDevWarningsInput {\n /** Whether `totalItems` was passed. */\n serverMode: boolean;\n /** The controlled `page`, if any. */\n controlledPage: number | undefined;\n /** The page callback, if any. */\n onPageChange: ((page: number) => void) | undefined;\n /** Whether sorting is delegated. */\n sortIsManual: boolean;\n /** The sort callback, if any. */\n onSortChange: ((sort: never) => void) | undefined;\n}\n\n/**\n * Warn, in development only, about prop combinations that render a table which\n * silently lies.\n *\n * Each of these produces a screen that looks like it works: the header sorts and\n * nothing moves, the pager renders and clicking it does nothing. They are not\n * type errors — every prop is optional on its own — so the only place to catch\n * them is at runtime, once, in dev.\n *\n * @param input - The resolved prop combination.\n */\nexport function useDevWarnings({\n serverMode,\n controlledPage,\n onPageChange,\n sortIsManual,\n onSortChange,\n}: DataTableDevWarningsInput): void {\n useEffect(() => {\n if (!isDevBuild()) return;\n\n if (serverMode && controlledPage === undefined) {\n console.warn(\n \"[tempest] <DataTable totalItems> is server mode, which needs a controlled `page`. \" +\n \"Without it the pager moves the internal page while `data` keeps showing page 1.\",\n );\n }\n if (controlledPage !== undefined && !onPageChange) {\n console.warn(\n \"[tempest] <DataTable page> is controlled but `onPageChange` is missing, so the pager cannot do anything.\",\n );\n }\n if (sortIsManual && !onSortChange) {\n console.warn(\n \"[tempest] <DataTable> is sorting manually but `onSortChange` is missing: clicking a sortable header changes the arrow and nothing else.\",\n );\n }\n }, [serverMode, controlledPage, onPageChange, sortIsManual, onSortChange]);\n}\n"],"mappings":";;;AA6BA,SAAgB,EAAe,EAC3B,eACA,mBACA,iBACA,iBACA,mBACgC;CAChC,QAAgB;EACP,EAAW,MAEZ,KAAc,MAAmB,KAAA,KACjC,QAAQ,KACJ,mKAEJ,GAEA,MAAmB,KAAA,KAAa,CAAC,KACjC,QAAQ,KACJ,0GACJ,GAEA,KAAgB,CAAC,KACjB,QAAQ,KACJ,yIACJ;CAER,GAAG;EAAC;EAAY;EAAgB;EAAc;EAAc;CAAY,CAAC;AAC7E"}
@@ -1,2 +1,2 @@
1
- const e=require("../../utils/cn.cjs"),t=require("../Badge/Badge.cjs"),n=require("../EmptyState/EmptyState.cjs"),r=require("../../utils/relative-time.cjs"),i=require("./NotificationCenter.module.cjs");let a=require("react/jsx-runtime");function o({items:o,title:s=`Notificações`,onSelect:c,onMarkRead:l,onMarkAllRead:u,onDismiss:d,renderIcon:f,locale:p=`pt-BR`,emptyState:m,now:h,className:g,..._}){let v=o.filter(e=>!e.read).length,y=h??new Date().getTime(),b=e=>{e.read||l?.(e.id),c?.(e)},x=!!(c||l);return(0,a.jsxs)(`div`,{className:e.cn(i.default.panel,g),..._,children:[(s||u&&v>0)&&(0,a.jsxs)(`header`,{className:i.default.header,children:[s?(0,a.jsxs)(`h2`,{className:i.default.title,children:[s,v>0&&(0,a.jsx)(t.Badge,{variant:`primary`,"aria-label":`${v} não lidas`,children:v})]}):(0,a.jsx)(`span`,{}),u&&v>0&&(0,a.jsx)(`button`,{type:`button`,className:i.default.headerAction,onClick:u,children:`Marcar todas como lidas`})]}),o.length===0?m??(0,a.jsx)(n.EmptyState,{title:`Nenhuma notificação`}):(0,a.jsx)(`ul`,{className:i.default.list,children:o.map(t=>(0,a.jsxs)(`li`,{className:e.cn(i.default.item,!t.read&&i.default.unread),"aria-current":t.read?void 0:`true`,children:[f&&(0,a.jsx)(`span`,{className:i.default.icon,children:f(t)}),(0,a.jsxs)(`div`,{className:i.default.body,children:[x?(0,a.jsxs)(`button`,{type:`button`,className:i.default.trigger,onClick:()=>b(t),children:[(0,a.jsx)(`span`,{className:i.default.itemTitle,children:t.title}),t.body&&(0,a.jsx)(`span`,{className:i.default.itemBody,children:t.body})]}):(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(`span`,{className:i.default.itemTitle,children:t.title}),t.body&&(0,a.jsx)(`span`,{className:i.default.itemBody,children:t.body})]}),(0,a.jsx)(`time`,{className:i.default.time,dateTime:new Date(t.receivedAt).toISOString(),children:r.relativeTime(t.receivedAt,{locale:p,now:y})})]}),d&&(0,a.jsx)(`button`,{type:`button`,className:i.default.dismiss,onClick:()=>d(t.id),"aria-label":`Descartar: ${t.title}`,children:`×`})]},t.id))})]})}exports.NotificationCenter=o;
1
+ const e=require("../../utils/cn.cjs"),t=require("../../utils/relative-time.cjs"),n=require("../Badge/Badge.cjs"),r=require("../EmptyState/EmptyState.cjs"),i=require("./NotificationCenter.module.cjs");let a=require("react/jsx-runtime");function o({items:o,title:s=`Notificações`,onSelect:c,onMarkRead:l,onMarkAllRead:u,onDismiss:d,renderIcon:f,locale:p=`pt-BR`,emptyState:m,now:h,className:g,..._}){let v=o.filter(e=>!e.read).length,y=h??new Date().getTime(),b=e=>{e.read||l?.(e.id),c?.(e)},x=!!(c||l);return(0,a.jsxs)(`div`,{className:e.cn(i.default.panel,g),..._,children:[(s||u&&v>0)&&(0,a.jsxs)(`header`,{className:i.default.header,children:[s?(0,a.jsxs)(`h2`,{className:i.default.title,children:[s,v>0&&(0,a.jsx)(n.Badge,{variant:`primary`,"aria-label":`${v} não lidas`,children:v})]}):(0,a.jsx)(`span`,{}),u&&v>0&&(0,a.jsx)(`button`,{type:`button`,className:i.default.headerAction,onClick:u,children:`Marcar todas como lidas`})]}),o.length===0?m??(0,a.jsx)(r.EmptyState,{title:`Nenhuma notificação`}):(0,a.jsx)(`ul`,{className:i.default.list,children:o.map(n=>(0,a.jsxs)(`li`,{className:e.cn(i.default.item,!n.read&&i.default.unread),"aria-current":n.read?void 0:`true`,children:[f&&(0,a.jsx)(`span`,{className:i.default.icon,children:f(n)}),(0,a.jsxs)(`div`,{className:i.default.body,children:[x?(0,a.jsxs)(`button`,{type:`button`,className:i.default.trigger,onClick:()=>b(n),children:[(0,a.jsx)(`span`,{className:i.default.itemTitle,children:n.title}),n.body&&(0,a.jsx)(`span`,{className:i.default.itemBody,children:n.body})]}):(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(`span`,{className:i.default.itemTitle,children:n.title}),n.body&&(0,a.jsx)(`span`,{className:i.default.itemBody,children:n.body})]}),(0,a.jsx)(`time`,{className:i.default.time,dateTime:new Date(n.receivedAt).toISOString(),children:t.relativeTime(n.receivedAt,{locale:p,now:y})})]}),d&&(0,a.jsx)(`button`,{type:`button`,className:i.default.dismiss,onClick:()=>d(n.id),"aria-label":`Descartar: ${n.title}`,children:`×`})]},n.id))})]})}exports.NotificationCenter=o;
2
2
  //# sourceMappingURL=NotificationCenter.cjs.map
@@ -1,7 +1,7 @@
1
1
  import { cn as e } from "../../utils/cn.js";
2
- import { Badge as t } from "../Badge/Badge.js";
3
- import { EmptyState as n } from "../EmptyState/EmptyState.js";
4
- import { relativeTime as r } from "../../utils/relative-time.js";
2
+ import { relativeTime as t } from "../../utils/relative-time.js";
3
+ import { Badge as n } from "../Badge/Badge.js";
4
+ import { EmptyState as r } from "../EmptyState/EmptyState.js";
5
5
  import i from "./NotificationCenter.module.js";
6
6
  import { Fragment as a, jsx as o, jsxs as s } from "react/jsx-runtime";
7
7
  //#region src/components/NotificationCenter/NotificationCenter.tsx
@@ -16,7 +16,7 @@ function c({ items: c, title: l = "Notificações", onSelect: u, onMarkRead: d,
16
16
  className: i.header,
17
17
  children: [l ? /* @__PURE__ */ s("h2", {
18
18
  className: i.title,
19
- children: [l, b > 0 && /* @__PURE__ */ o(t, {
19
+ children: [l, b > 0 && /* @__PURE__ */ o(n, {
20
20
  variant: "primary",
21
21
  "aria-label": `${b} não lidas`,
22
22
  children: b
@@ -27,39 +27,39 @@ function c({ items: c, title: l = "Notificações", onSelect: u, onMarkRead: d,
27
27
  onClick: f,
28
28
  children: "Marcar todas como lidas"
29
29
  })]
30
- }), c.length === 0 ? g ?? /* @__PURE__ */ o(n, { title: "Nenhuma notificação" }) : /* @__PURE__ */ o("ul", {
30
+ }), c.length === 0 ? g ?? /* @__PURE__ */ o(r, { title: "Nenhuma notificação" }) : /* @__PURE__ */ o("ul", {
31
31
  className: i.list,
32
- children: c.map((t) => /* @__PURE__ */ s("li", {
33
- className: e(i.item, !t.read && i.unread),
34
- "aria-current": t.read ? void 0 : "true",
32
+ children: c.map((n) => /* @__PURE__ */ s("li", {
33
+ className: e(i.item, !n.read && i.unread),
34
+ "aria-current": n.read ? void 0 : "true",
35
35
  children: [
36
36
  m && /* @__PURE__ */ o("span", {
37
37
  className: i.icon,
38
- children: m(t)
38
+ children: m(n)
39
39
  }),
40
40
  /* @__PURE__ */ s("div", {
41
41
  className: i.body,
42
42
  children: [C ? /* @__PURE__ */ s("button", {
43
43
  type: "button",
44
44
  className: i.trigger,
45
- onClick: () => S(t),
45
+ onClick: () => S(n),
46
46
  children: [/* @__PURE__ */ o("span", {
47
47
  className: i.itemTitle,
48
- children: t.title
49
- }), t.body && /* @__PURE__ */ o("span", {
48
+ children: n.title
49
+ }), n.body && /* @__PURE__ */ o("span", {
50
50
  className: i.itemBody,
51
- children: t.body
51
+ children: n.body
52
52
  })]
53
53
  }) : /* @__PURE__ */ s(a, { children: [/* @__PURE__ */ o("span", {
54
54
  className: i.itemTitle,
55
- children: t.title
56
- }), t.body && /* @__PURE__ */ o("span", {
55
+ children: n.title
56
+ }), n.body && /* @__PURE__ */ o("span", {
57
57
  className: i.itemBody,
58
- children: t.body
58
+ children: n.body
59
59
  })] }), /* @__PURE__ */ o("time", {
60
60
  className: i.time,
61
- dateTime: new Date(t.receivedAt).toISOString(),
62
- children: r(t.receivedAt, {
61
+ dateTime: new Date(n.receivedAt).toISOString(),
62
+ children: t(n.receivedAt, {
63
63
  locale: h,
64
64
  now: x
65
65
  })
@@ -68,12 +68,12 @@ function c({ items: c, title: l = "Notificações", onSelect: u, onMarkRead: d,
68
68
  p && /* @__PURE__ */ o("button", {
69
69
  type: "button",
70
70
  className: i.dismiss,
71
- onClick: () => p(t.id),
72
- "aria-label": `Descartar: ${t.title}`,
71
+ onClick: () => p(n.id),
72
+ "aria-label": `Descartar: ${n.title}`,
73
73
  children: "×"
74
74
  })
75
75
  ]
76
- }, t.id))
76
+ }, n.id))
77
77
  })]
78
78
  });
79
79
  }
@@ -1,2 +1,2 @@
1
- const e=require("../components/Input/Input.cjs"),t=require("../utils/format.cjs"),n=require("./br-validators.cjs");let r=require("react"),i=require("react/jsx-runtime");function a(t,n=`numeric`){return(0,r.forwardRef)(function({value:r,onChange:a,...o},s){return(0,i.jsx)(e.Input,{...o,ref:s,value:t(r??``),inputMode:n,onChange:e=>a?.(t(e.target.value))})})}var o=a(t.formatCPF),s=a(n.formatCNPJ),c=a(t.formatPhone,`tel`),l=a(n.formatCEP);function u(e,t,n){return new Intl.NumberFormat(t,{style:`currency`,currency:n}).format(e/100)}function d(e){let t=e.replace(/\D/g,``);return t?Number.parseInt(t,10):0}var f=(0,r.forwardRef)(function({value:t,onChange:n,currency:r=`BRL`,locale:a=`pt-BR`,...o},s){return(0,i.jsx)(e.Input,{...o,ref:s,type:`text`,inputMode:`numeric`,value:u(t||0,a,r),onChange:e=>n(d(e.target.value))})});exports.CEPInput=l,exports.CNPJInput=s,exports.CPFInput=o,exports.MoneyInput=f,exports.PhoneInput=c;
1
+ const e=require("../utils/format.cjs"),t=require("../components/Input/Input.cjs"),n=require("./br-validators.cjs");let r=require("react"),i=require("react/jsx-runtime");function a(e,n=`numeric`){return(0,r.forwardRef)(function({value:r,onChange:a,...o},s){return(0,i.jsx)(t.Input,{...o,ref:s,value:e(r??``),inputMode:n,onChange:t=>a?.(e(t.target.value))})})}var o=a(e.formatCPF),s=a(n.formatCNPJ),c=a(e.formatPhone,`tel`),l=a(n.formatCEP);function u(e,t,n){return new Intl.NumberFormat(t,{style:`currency`,currency:n}).format(e/100)}function d(e){let t=e.replace(/\D/g,``);return t?Number.parseInt(t,10):0}var f=(0,r.forwardRef)(function({value:e,onChange:n,currency:r=`BRL`,locale:a=`pt-BR`,...o},s){return(0,i.jsx)(t.Input,{...o,ref:s,type:`text`,inputMode:`numeric`,value:u(e||0,a,r),onChange:e=>n(d(e.target.value))})});exports.CEPInput=l,exports.CNPJInput=s,exports.CPFInput=o,exports.MoneyInput=f,exports.PhoneInput=c;
2
2
  //# sourceMappingURL=masked-inputs.cjs.map
@@ -1,21 +1,21 @@
1
- import { Input as e } from "../components/Input/Input.js";
2
- import { formatCPF as t, formatPhone as n } from "../utils/format.js";
1
+ import { formatCPF as e, formatPhone as t } from "../utils/format.js";
2
+ import { Input as n } from "../components/Input/Input.js";
3
3
  import { formatCEP as r, formatCNPJ as i } from "./br-validators.js";
4
4
  import { forwardRef as a } from "react";
5
5
  import { jsx as o } from "react/jsx-runtime";
6
6
  //#region src/forms/masked-inputs.tsx
7
- function s(t, n = "numeric") {
7
+ function s(e, t = "numeric") {
8
8
  return a(function({ value: r, onChange: i, ...a }, s) {
9
- return /* @__PURE__ */ o(e, {
9
+ return /* @__PURE__ */ o(n, {
10
10
  ...a,
11
11
  ref: s,
12
- value: t(r ?? ""),
13
- inputMode: n,
14
- onChange: (e) => i?.(t(e.target.value))
12
+ value: e(r ?? ""),
13
+ inputMode: t,
14
+ onChange: (t) => i?.(e(t.target.value))
15
15
  });
16
16
  });
17
17
  }
18
- var c = s(t), l = s(i), u = s(n, "tel"), d = s(r);
18
+ var c = s(e), l = s(i), u = s(t, "tel"), d = s(r);
19
19
  function f(e, t, n) {
20
20
  return new Intl.NumberFormat(t, {
21
21
  style: "currency",
@@ -26,14 +26,14 @@ function p(e) {
26
26
  let t = e.replace(/\D/g, "");
27
27
  return t ? Number.parseInt(t, 10) : 0;
28
28
  }
29
- var m = a(function({ value: t, onChange: n, currency: r = "BRL", locale: i = "pt-BR", ...a }, s) {
30
- return /* @__PURE__ */ o(e, {
29
+ var m = a(function({ value: e, onChange: t, currency: r = "BRL", locale: i = "pt-BR", ...a }, s) {
30
+ return /* @__PURE__ */ o(n, {
31
31
  ...a,
32
32
  ref: s,
33
33
  type: "text",
34
34
  inputMode: "numeric",
35
- value: f(t || 0, i, r),
36
- onChange: (e) => n(p(e.target.value))
35
+ value: f(e || 0, i, r),
36
+ onChange: (e) => t(p(e.target.value))
37
37
  });
38
38
  });
39
39
  //#endregion
@@ -1,2 +1,2 @@
1
- const e=require("../utils/ids.cjs"),t=require("./build-url.cjs"),n=require("./errors.cjs"),r=require("./retry.cjs");var i=new Set([`GET`,`HEAD`,`OPTIONS`]),a=new Set([0,408,425,429]);function o(e,t){return!i.has(t)||!(e instanceof n.TempestApiError)?!1:a.has(e.status)||e.status>=500}function s(e){return e?e===!0?{}:e:null}function c(e){return typeof FormData<`u`&&e instanceof FormData}async function l(e,t){let r;try{r=await e.clone().json()}catch{try{r=await e.text()}catch{r=null}}return new n.TempestApiError(n.buildApiError(e.status,r,e.headers,t))}function u(n){let i=n.fetcher??globalThis.fetch.bind(globalThis);function a(){let e=n.getToken?.();return e?{Authorization:`Bearer ${e}`}:{}}async function u(e,r,o){let{body:s,params:l,headers:u,...d}=r,f=c(s),p={...f?{}:{"Content-Type":`application/json`},...o?{"X-Request-ID":o}:{},...n.headers,...a(),...u},m={...d,headers:p,credentials:n.withCredentials?`include`:d.credentials,body:s==null?void 0:f?s:JSON.stringify(s)};return i(t.buildApiUrl(n.baseURL,e,{prefix:n.prefix,params:l}),m)}async function d(t,r){let i=n.requestId?n.requestId():e.randomId(),a=await u(t,r,i);if(a.status===401){if(n.refresh){try{await n.refresh(),a=await u(t,r,i)}catch{throw await n.onUnauthorized?.(a),await l(a,i)}a.status===401&&await n.onUnauthorized?.(a)}else await n.onUnauthorized?.(a)}if(!a.ok)throw await l(a,i);if(a.status!==204)return(a.headers.get(`content-type`)??``).includes(`application/json`)?await a.json():await a.text()}async function f(e,t={}){let i=s(n.retry);if(!i)return d(e,t);let a=(t.method??`GET`).toUpperCase();return r.retry(()=>d(e,t),{...i,shouldRetry:i.shouldRetry??(e=>o(e,a))})}async function p(e,t,n=`POST`){return f(e,{method:n,body:t})}return{request:f,get:(e,t)=>f(e,{...t,method:`GET`}),post:(e,t)=>f(e,{...t,method:`POST`}),put:(e,t)=>f(e,{...t,method:`PUT`}),patch:(e,t)=>f(e,{...t,method:`PATCH`}),delete:(e,t)=>f(e,{...t,method:`DELETE`}),upload:p}}exports.createApiClient=u;
1
+ const e=require("../utils/ids.cjs"),t=require("./build-url.cjs"),n=require("./errors.cjs"),r=require("./retry.cjs");var i=new Set([`GET`,`HEAD`,`OPTIONS`]),a=new Set([0,408,425,429]);function o(e,t){return!i.has(t)||!(e instanceof n.TempestApiError)?!1:a.has(e.status)||e.status>=500}function s(e){return e?e===!0?{}:e:null}function c(e){return typeof FormData<`u`&&e instanceof FormData}async function l(e,t){let r;try{r=await e.clone().json()}catch{try{r=await e.text()}catch{r=null}}return new n.TempestApiError(n.buildApiError(e.status,r,e.headers,t))}function u(n){let i=n.fetcher??globalThis.fetch.bind(globalThis);function a(){let e=n.getToken?.();return e?{Authorization:`Bearer ${e}`}:{}}async function u(e,r,o){let{body:s,params:l,headers:u,...d}=r,f=c(s),p={...f?{}:{"Content-Type":`application/json`},...o?{"X-Request-ID":o}:{},...n.headers,...a(),...u},m={...d,headers:p,credentials:n.withCredentials?`include`:d.credentials,body:s==null?void 0:f?s:JSON.stringify(s)};return i(t.buildApiUrl(n.baseURL,e,{prefix:n.prefix,params:l}),m)}async function d(e,t,r,i){let a=n.logger;if(!a)return u(e,t,r);let o=Date.now();try{let n=await u(e,t,r),s={requestId:r,status:n.status,ms:Date.now()-o},c=`${i} ${e} → ${n.status}`;return n.status>=400?a.warn(c,s):a.debug(c,s),n}catch(t){throw a.warn(`${i} ${e} → no response`,{requestId:r,ms:Date.now()-o,error:t}),t}}async function f(e,t){n.logger?.warn(`unauthorized — calling onUnauthorized`,{requestId:t,status:e.status}),await n.onUnauthorized?.(e)}async function p(t,r){let i=n.requestId?n.requestId():e.randomId(),a=(r.method??`GET`).toUpperCase(),o=await d(t,r,i,a);if(o.status===401){if(n.refresh){try{await n.refresh(),o=await d(t,r,i,a)}catch{throw await f(o,i),await l(o,i)}o.status===401&&await f(o,i)}else await f(o,i)}if(!o.ok)throw await l(o,i);if(o.status!==204)return(o.headers.get(`content-type`)??``).includes(`application/json`)?await o.json():await o.text()}async function m(e,t={}){let i=s(n.retry);if(!i)return p(e,t);let a=(t.method??`GET`).toUpperCase();return r.retry(()=>p(e,t),{...i,shouldRetry:i.shouldRetry??(e=>o(e,a))})}async function h(e,t,n=`POST`){return m(e,{method:n,body:t})}return{request:m,get:(e,t)=>m(e,{...t,method:`GET`}),post:(e,t)=>m(e,{...t,method:`POST`}),put:(e,t)=>m(e,{...t,method:`PUT`}),patch:(e,t)=>m(e,{...t,method:`PATCH`}),delete:(e,t)=>m(e,{...t,method:`DELETE`}),upload:h}}exports.createApiClient=u;
2
2
  //# sourceMappingURL=api-client.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"api-client.cjs","names":[],"sources":["../../src/http/api-client.ts"],"sourcesContent":["/**\n * @tempest-limits function-lines — createApiClient is over the limit and every line\n * is a request-lifecycle concern the client cannot delegate: base URL joining, the\n * auth header, the 401 refresh-and-replay, the opt-in retry wrapper and the response\n * parsing that turns a failure into a typed error.\n */\nimport { randomId } from \"../utils\";\nimport { buildApiUrl } from \"./build-url\";\nimport { buildApiError, TempestApiError } from \"./errors\";\nimport { retry as retryWithBackoff } from \"./retry\";\nimport type { RetryOptions } from \"./retry\";\nimport type { ApiClient, ApiClientConfig, RequestOptions } from \"./types\";\n\n/**\n * Methods the built-in retry policy will replay.\n *\n * `PUT` and `DELETE` are idempotent on paper but stay out: a backend that logs,\n * bills, or fires a webhook per call still sees two, so replaying them is a\n * decision the caller makes through `shouldRetry`, not a default.\n */\nconst IDEMPOTENT_METHODS: ReadonlySet<string> = new Set([\"GET\", \"HEAD\", \"OPTIONS\"]);\n\n/**\n * Sub-500 statuses worth a second attempt: a network failure (status `0`), a\n * request timeout, a too-early replay, and a rate limit — which usually carries\n * the `Retry-After` the backoff already honours.\n */\nconst RETRIABLE_STATUSES: ReadonlySet<number> = new Set([0, 408, 425, 429]);\n\n/**\n * The built-in retry policy, used when `retry` is `true` or is options carrying\n * no `shouldRetry` of their own.\n *\n * Conservative on purpose. Replaying a write can duplicate it, and replaying a\n * `400` or a `403` cannot fix a bad payload or a permission the caller does not\n * have — it only spends the user's time before showing the same error.\n *\n * @param error - Whatever the attempt threw.\n * @param method - The upper-cased HTTP method of the request.\n * @returns Whether the client should try again.\n */\nfunction isRetriableFailure(error: unknown, method: string): boolean {\n if (!IDEMPOTENT_METHODS.has(method)) return false;\n if (!(error instanceof TempestApiError)) return false;\n return RETRIABLE_STATUSES.has(error.status) || error.status >= 500;\n}\n\n/**\n * Normalize the `retry` config into options, or `null` when retrying is off.\n *\n * @param config - The `retry` field as the caller wrote it.\n * @returns Retry options to use, or `null` to run a single attempt.\n */\nfunction resolveRetry(config: boolean | RetryOptions | undefined): RetryOptions | null {\n if (!config) return null;\n return config === true ? {} : config;\n}\n\nfunction isFormData(body: unknown): body is FormData {\n return typeof FormData !== \"undefined\" && body instanceof FormData;\n}\n\nasync function parseError(response: Response, sentRequestId?: string): Promise<TempestApiError> {\n let body: unknown;\n try {\n body = await response.clone().json();\n } catch {\n try {\n body = await response.text();\n } catch {\n body = null;\n }\n }\n return new TempestApiError(\n buildApiError(response.status, body, response.headers, sentRequestId),\n );\n}\n\n/**\n * Create a typed HTTP client backed by `fetch`.\n *\n * Handles JSON serialization, query params, bearer auth via `getToken`, uploads\n * via `FormData`, and throws a typed `ApiError` on any non-2xx response.\n *\n * **Expired sessions.** A `401` with `refresh` configured awaits the refresh and\n * replays the request once. `onUnauthorized` fires whenever that path ends\n * unauthorized anyway — the refresh threw, or the replay came back `401` — which\n * is the signal to clear the session. Without `refresh`, the first `401` calls\n * it directly.\n *\n * **Retries** are off unless you set `retry`. See {@link ApiClientConfig.retry}\n * for the built-in policy; it never replays a write.\n *\n * @example\n * const api = createApiClient({\n * baseURL: import.meta.env.VITE_API_URL,\n * getToken: () => useAuthStore.getState().token,\n * refresh,\n * onUnauthorized: () => useAuthStore.getState().logout(),\n * retry: true,\n * });\n *\n * @param config - Base URL plus the optional auth, retry and fetch hooks.\n * @returns A client with `request`/`get`/`post`/`put`/`patch`/`delete`/`upload`.\n */\nexport function createApiClient(config: ApiClientConfig): ApiClient {\n const fetcher = config.fetcher ?? globalThis.fetch.bind(globalThis);\n\n function authHeaders(): Record<string, string> {\n const token = config.getToken?.();\n return token ? { Authorization: `Bearer ${token}` } : {};\n }\n\n async function rawRequest(\n path: string,\n options: RequestOptions,\n requestId?: string,\n ): Promise<Response> {\n const { body, params, headers, ...rest } = options;\n const isForm = isFormData(body);\n\n const finalHeaders: Record<string, string> = {\n ...(isForm ? {} : { \"Content-Type\": \"application/json\" }),\n ...(requestId ? { \"X-Request-ID\": requestId } : {}),\n ...config.headers,\n ...authHeaders(),\n ...(headers as Record<string, string> | undefined),\n };\n\n const init: RequestInit = {\n ...rest,\n headers: finalHeaders,\n credentials: config.withCredentials ? \"include\" : rest.credentials,\n body:\n body === undefined || body === null\n ? undefined\n : isForm\n ? (body as FormData)\n : JSON.stringify(body),\n };\n\n return fetcher(buildApiUrl(config.baseURL, path, { prefix: config.prefix, params }), init);\n }\n\n async function attempt<T>(path: string, options: RequestOptions): Promise<T> {\n const requestId = config.requestId ? config.requestId() : randomId();\n let response = await rawRequest(path, options, requestId);\n\n if (response.status === 401) {\n if (config.refresh) {\n try {\n await config.refresh();\n response = await rawRequest(path, options, requestId);\n } catch {\n await config.onUnauthorized?.(response);\n throw await parseError(response, requestId);\n }\n if (response.status === 401) {\n await config.onUnauthorized?.(response);\n }\n } else {\n await config.onUnauthorized?.(response);\n }\n }\n\n if (!response.ok) {\n throw await parseError(response, requestId);\n }\n\n if (response.status === 204) {\n return undefined as T;\n }\n\n const contentType = response.headers.get(\"content-type\") ?? \"\";\n if (contentType.includes(\"application/json\")) {\n return (await response.json()) as T;\n }\n return (await response.text()) as unknown as T;\n }\n\n async function request<T>(path: string, options: RequestOptions = {}): Promise<T> {\n const retryOptions = resolveRetry(config.retry);\n if (!retryOptions) return attempt<T>(path, options);\n\n const method = (options.method ?? \"GET\").toUpperCase();\n return retryWithBackoff(() => attempt<T>(path, options), {\n ...retryOptions,\n shouldRetry:\n retryOptions.shouldRetry ?? ((error: unknown) => isRetriableFailure(error, method)),\n });\n }\n\n async function upload<T>(\n path: string,\n formData: FormData,\n method: \"POST\" | \"PUT\" | \"PATCH\" = \"POST\",\n ): Promise<T> {\n return request<T>(path, { method, body: formData });\n }\n\n return {\n request,\n get: <T>(path: string, options?: RequestOptions) =>\n request<T>(path, { ...options, method: \"GET\" }),\n post: <T>(path: string, options?: RequestOptions) =>\n request<T>(path, { ...options, method: \"POST\" }),\n put: <T>(path: string, options?: RequestOptions) =>\n request<T>(path, { ...options, method: \"PUT\" }),\n patch: <T>(path: string, options?: RequestOptions) =>\n request<T>(path, { ...options, method: \"PATCH\" }),\n delete: <T>(path: string, options?: RequestOptions) =>\n request<T>(path, { ...options, method: \"DELETE\" }),\n upload,\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"api-client.cjs","names":[],"sources":["../../src/http/api-client.ts"],"sourcesContent":["/**\n * @tempest-limits function-lines — createApiClient is over the limit and every line\n * is a request-lifecycle concern the client cannot delegate: base URL joining, the\n * auth header, the 401 refresh-and-replay, the opt-in retry wrapper and the response\n * parsing that turns a failure into a typed error.\n */\nimport { randomId } from \"../utils\";\nimport { buildApiUrl } from \"./build-url\";\nimport { buildApiError, TempestApiError } from \"./errors\";\nimport { retry as retryWithBackoff } from \"./retry\";\nimport type { RetryOptions } from \"./retry\";\nimport type { ApiClient, ApiClientConfig, RequestOptions } from \"./types\";\n\n/**\n * Methods the built-in retry policy will replay.\n *\n * `PUT` and `DELETE` are idempotent on paper but stay out: a backend that logs,\n * bills, or fires a webhook per call still sees two, so replaying them is a\n * decision the caller makes through `shouldRetry`, not a default.\n */\nconst IDEMPOTENT_METHODS: ReadonlySet<string> = new Set([\"GET\", \"HEAD\", \"OPTIONS\"]);\n\n/**\n * Sub-500 statuses worth a second attempt: a network failure (status `0`), a\n * request timeout, a too-early replay, and a rate limit — which usually carries\n * the `Retry-After` the backoff already honours.\n */\nconst RETRIABLE_STATUSES: ReadonlySet<number> = new Set([0, 408, 425, 429]);\n\n/**\n * The built-in retry policy, used when `retry` is `true` or is options carrying\n * no `shouldRetry` of their own.\n *\n * Conservative on purpose. Replaying a write can duplicate it, and replaying a\n * `400` or a `403` cannot fix a bad payload or a permission the caller does not\n * have — it only spends the user's time before showing the same error.\n *\n * @param error - Whatever the attempt threw.\n * @param method - The upper-cased HTTP method of the request.\n * @returns Whether the client should try again.\n */\nfunction isRetriableFailure(error: unknown, method: string): boolean {\n if (!IDEMPOTENT_METHODS.has(method)) return false;\n if (!(error instanceof TempestApiError)) return false;\n return RETRIABLE_STATUSES.has(error.status) || error.status >= 500;\n}\n\n/**\n * Normalize the `retry` config into options, or `null` when retrying is off.\n *\n * @param config - The `retry` field as the caller wrote it.\n * @returns Retry options to use, or `null` to run a single attempt.\n */\nfunction resolveRetry(config: boolean | RetryOptions | undefined): RetryOptions | null {\n if (!config) return null;\n return config === true ? {} : config;\n}\n\nfunction isFormData(body: unknown): body is FormData {\n return typeof FormData !== \"undefined\" && body instanceof FormData;\n}\n\nasync function parseError(response: Response, sentRequestId?: string): Promise<TempestApiError> {\n let body: unknown;\n try {\n body = await response.clone().json();\n } catch {\n try {\n body = await response.text();\n } catch {\n body = null;\n }\n }\n return new TempestApiError(\n buildApiError(response.status, body, response.headers, sentRequestId),\n );\n}\n\n/**\n * Create a typed HTTP client backed by `fetch`.\n *\n * Handles JSON serialization, query params, bearer auth via `getToken`, uploads\n * via `FormData`, and throws a typed `ApiError` on any non-2xx response.\n *\n * **Expired sessions.** A `401` with `refresh` configured awaits the refresh and\n * replays the request once. `onUnauthorized` fires whenever that path ends\n * unauthorized anyway — the refresh threw, or the replay came back `401` — which\n * is the signal to clear the session. Without `refresh`, the first `401` calls\n * it directly.\n *\n * **Retries** are off unless you set `retry`. See {@link ApiClientConfig.retry}\n * for the built-in policy; it never replays a write.\n *\n * **Logging** is off unless you pass a `logger`. With one, every finished attempt\n * writes a line — `debug` under 400, `warn` from 400 up, plus a `warn` when\n * `onUnauthorized` fires — carrying `requestId`, `status` and elapsed `ms`, and\n * never a body, header or query string. The level and the destination belong to\n * the logger, not to a boolean here.\n *\n * @example\n * const api = createApiClient({\n * baseURL: import.meta.env.VITE_API_URL,\n * getToken: () => useAuthStore.getState().token,\n * refresh,\n * onUnauthorized: () => useAuthStore.getState().logout(),\n * logger: createLogger({ level: import.meta.env.DEV ? \"debug\" : \"warn\" }).child(\"http\"),\n * retry: true,\n * });\n *\n * @param config - Base URL plus the optional auth, retry and fetch hooks.\n * @returns A client with `request`/`get`/`post`/`put`/`patch`/`delete`/`upload`.\n */\nexport function createApiClient(config: ApiClientConfig): ApiClient {\n const fetcher = config.fetcher ?? globalThis.fetch.bind(globalThis);\n\n function authHeaders(): Record<string, string> {\n const token = config.getToken?.();\n return token ? { Authorization: `Bearer ${token}` } : {};\n }\n\n async function rawRequest(\n path: string,\n options: RequestOptions,\n requestId?: string,\n ): Promise<Response> {\n const { body, params, headers, ...rest } = options;\n const isForm = isFormData(body);\n\n const finalHeaders: Record<string, string> = {\n ...(isForm ? {} : { \"Content-Type\": \"application/json\" }),\n ...(requestId ? { \"X-Request-ID\": requestId } : {}),\n ...config.headers,\n ...authHeaders(),\n ...(headers as Record<string, string> | undefined),\n };\n\n const init: RequestInit = {\n ...rest,\n headers: finalHeaders,\n credentials: config.withCredentials ? \"include\" : rest.credentials,\n body:\n body === undefined || body === null\n ? undefined\n : isForm\n ? (body as FormData)\n : JSON.stringify(body),\n };\n\n return fetcher(buildApiUrl(config.baseURL, path, { prefix: config.prefix, params }), init);\n }\n\n async function send(\n path: string,\n options: RequestOptions,\n requestId: string,\n method: string,\n ): Promise<Response> {\n const log = config.logger;\n if (!log) return rawRequest(path, options, requestId);\n\n const startedAt = Date.now();\n try {\n const response = await rawRequest(path, options, requestId);\n const entry = { requestId, status: response.status, ms: Date.now() - startedAt };\n const line = `${method} ${path} → ${response.status}`;\n if (response.status >= 400) log.warn(line, entry);\n else log.debug(line, entry);\n return response;\n } catch (error) {\n log.warn(`${method} ${path} → no response`, {\n requestId,\n ms: Date.now() - startedAt,\n error,\n });\n throw error;\n }\n }\n\n async function endSession(response: Response, requestId: string): Promise<void> {\n config.logger?.warn(`unauthorized — calling onUnauthorized`, {\n requestId,\n status: response.status,\n });\n await config.onUnauthorized?.(response);\n }\n\n async function attempt<T>(path: string, options: RequestOptions): Promise<T> {\n const requestId = config.requestId ? config.requestId() : randomId();\n const method = (options.method ?? \"GET\").toUpperCase();\n let response = await send(path, options, requestId, method);\n\n if (response.status === 401) {\n if (config.refresh) {\n try {\n await config.refresh();\n response = await send(path, options, requestId, method);\n } catch {\n await endSession(response, requestId);\n throw await parseError(response, requestId);\n }\n if (response.status === 401) {\n await endSession(response, requestId);\n }\n } else {\n await endSession(response, requestId);\n }\n }\n\n if (!response.ok) {\n throw await parseError(response, requestId);\n }\n\n if (response.status === 204) {\n return undefined as T;\n }\n\n const contentType = response.headers.get(\"content-type\") ?? \"\";\n if (contentType.includes(\"application/json\")) {\n return (await response.json()) as T;\n }\n return (await response.text()) as unknown as T;\n }\n\n async function request<T>(path: string, options: RequestOptions = {}): Promise<T> {\n const retryOptions = resolveRetry(config.retry);\n if (!retryOptions) return attempt<T>(path, options);\n\n const method = (options.method ?? \"GET\").toUpperCase();\n return retryWithBackoff(() => attempt<T>(path, options), {\n ...retryOptions,\n shouldRetry:\n retryOptions.shouldRetry ?? ((error: unknown) => isRetriableFailure(error, method)),\n });\n }\n\n async function upload<T>(\n path: string,\n formData: FormData,\n method: \"POST\" | \"PUT\" | \"PATCH\" = \"POST\",\n ): Promise<T> {\n return request<T>(path, { method, body: formData });\n }\n\n return {\n request,\n get: <T>(path: string, options?: RequestOptions) =>\n request<T>(path, { ...options, method: \"GET\" }),\n post: <T>(path: string, options?: RequestOptions) =>\n request<T>(path, { ...options, method: \"POST\" }),\n put: <T>(path: string, options?: RequestOptions) =>\n request<T>(path, { ...options, method: \"PUT\" }),\n patch: <T>(path: string, options?: RequestOptions) =>\n request<T>(path, { ...options, method: \"PATCH\" }),\n delete: <T>(path: string, options?: RequestOptions) =>\n request<T>(path, { ...options, method: \"DELETE\" }),\n upload,\n };\n}\n"],"mappings":"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"}