tempest-react-sdk 0.81.0 → 0.83.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 (194) hide show
  1. package/README.md +9 -10
  2. package/bin/create-tempest-app.e2e.test.mjs +31 -0
  3. package/bin/create-tempest-app.mjs +11 -1
  4. package/bin/lib/doctor/doctor.e2e.test.mjs +29 -0
  5. package/bin/lib/doctor/vitest.mjs +120 -0
  6. package/bin/lib/doctor/vitest.test.mjs +139 -0
  7. package/bin/lib/fs/contain.mjs +7 -0
  8. package/bin/lib/openapi/escape.mjs +68 -4
  9. package/bin/lib/openapi/generate.hostile.test.mjs +228 -0
  10. package/bin/lib/openapi/generate.mjs +119 -15
  11. package/bin/lib/openapi/pattern-risk.mjs +134 -0
  12. package/bin/lib/openapi/pattern-risk.test.mjs +45 -0
  13. package/bin/tempest-gen.test.mjs +100 -0
  14. package/bin/tempest.mjs +24 -8
  15. package/dist/access/use-can.cjs +1 -1
  16. package/dist/access/use-can.cjs.map +1 -1
  17. package/dist/access/use-can.js +25 -28
  18. package/dist/access/use-can.js.map +1 -1
  19. package/dist/auth/create-tempest-auth.cjs +1 -1
  20. package/dist/auth/create-tempest-auth.cjs.map +1 -1
  21. package/dist/auth/create-tempest-auth.js +189 -143
  22. package/dist/auth/create-tempest-auth.js.map +1 -1
  23. package/dist/auth/session-identity.cjs +2 -0
  24. package/dist/auth/session-identity.cjs.map +1 -0
  25. package/dist/auth/session-identity.js +12 -0
  26. package/dist/auth/session-identity.js.map +1 -0
  27. package/dist/br/boleto.cjs.map +1 -1
  28. package/dist/br/boleto.js.map +1 -1
  29. package/dist/components/AuthForm/AuthLinks.cjs +1 -1
  30. package/dist/components/AuthForm/AuthLinks.cjs.map +1 -1
  31. package/dist/components/AuthForm/AuthLinks.js +18 -15
  32. package/dist/components/AuthForm/AuthLinks.js.map +1 -1
  33. package/dist/components/AuthPage/AuthPage.cjs +1 -1
  34. package/dist/components/AuthPage/AuthPage.cjs.map +1 -1
  35. package/dist/components/AuthPage/AuthPage.js +58 -52
  36. package/dist/components/AuthPage/AuthPage.js.map +1 -1
  37. package/dist/components/Breadcrumbs/Breadcrumbs.cjs +1 -1
  38. package/dist/components/Breadcrumbs/Breadcrumbs.cjs.map +1 -1
  39. package/dist/components/Breadcrumbs/Breadcrumbs.js +22 -21
  40. package/dist/components/Breadcrumbs/Breadcrumbs.js.map +1 -1
  41. package/dist/components/Button/Button.cjs +1 -1
  42. package/dist/components/Button/Button.cjs.map +1 -1
  43. package/dist/components/Button/Button.js +41 -40
  44. package/dist/components/Button/Button.js.map +1 -1
  45. package/dist/components/Chat/ChatAttachments.cjs +1 -1
  46. package/dist/components/Chat/ChatAttachments.cjs.map +1 -1
  47. package/dist/components/Chat/ChatAttachments.js +38 -38
  48. package/dist/components/Form/Form.cjs +1 -1
  49. package/dist/components/Form/Form.cjs.map +1 -1
  50. package/dist/components/Form/Form.js +30 -32
  51. package/dist/components/Form/Form.js.map +1 -1
  52. package/dist/components/Layout/Layout.cjs +1 -1
  53. package/dist/components/Layout/Layout.cjs.map +1 -1
  54. package/dist/components/Layout/Layout.js +25 -24
  55. package/dist/components/Layout/Layout.js.map +1 -1
  56. package/dist/components/Layout/gap.cjs +2 -0
  57. package/dist/components/Layout/gap.cjs.map +1 -0
  58. package/dist/components/Layout/gap.js +15 -0
  59. package/dist/components/Layout/gap.js.map +1 -0
  60. package/dist/components/NavigationMenu/NavigationMenu.cjs +1 -1
  61. package/dist/components/NavigationMenu/NavigationMenu.cjs.map +1 -1
  62. package/dist/components/NavigationMenu/NavigationMenu.js +71 -70
  63. package/dist/components/NavigationMenu/NavigationMenu.js.map +1 -1
  64. package/dist/components/Sidebar/Sidebar.cjs.map +1 -1
  65. package/dist/components/Sidebar/Sidebar.js.map +1 -1
  66. package/dist/components/Sidebar/SidebarEntry.cjs +1 -1
  67. package/dist/components/Sidebar/SidebarEntry.cjs.map +1 -1
  68. package/dist/components/Sidebar/SidebarEntry.js +30 -29
  69. package/dist/components/Sidebar/SidebarEntry.js.map +1 -1
  70. package/dist/data/create-data-provider.cjs +1 -1
  71. package/dist/data/create-data-provider.cjs.map +1 -1
  72. package/dist/data/create-data-provider.js +1 -2
  73. package/dist/data/create-data-provider.js.map +1 -1
  74. package/dist/forms/validate-form.cjs +1 -1
  75. package/dist/forms/validate-form.cjs.map +1 -1
  76. package/dist/forms/validate-form.js +6 -1
  77. package/dist/forms/validate-form.js.map +1 -1
  78. package/dist/forms/zod-resolver.cjs +1 -1
  79. package/dist/forms/zod-resolver.cjs.map +1 -1
  80. package/dist/forms/zod-resolver.js +8 -3
  81. package/dist/forms/zod-resolver.js.map +1 -1
  82. package/dist/http/api-client.cjs +1 -1
  83. package/dist/http/api-client.cjs.map +1 -1
  84. package/dist/http/api-client.js +90 -72
  85. package/dist/http/api-client.js.map +1 -1
  86. package/dist/http/credential-scope.cjs +1 -1
  87. package/dist/http/credential-scope.cjs.map +1 -1
  88. package/dist/http/credential-scope.js +33 -9
  89. package/dist/http/credential-scope.js.map +1 -1
  90. package/dist/http/presigned-post.cjs.map +1 -1
  91. package/dist/http/presigned-post.js.map +1 -1
  92. package/dist/http/resumable-upload.cjs +1 -1
  93. package/dist/http/resumable-upload.cjs.map +1 -1
  94. package/dist/http/resumable-upload.js +167 -112
  95. package/dist/http/resumable-upload.js.map +1 -1
  96. package/dist/http/upload-with-progress.cjs +1 -1
  97. package/dist/http/upload-with-progress.cjs.map +1 -1
  98. package/dist/http/upload-with-progress.js +100 -44
  99. package/dist/http/upload-with-progress.js.map +1 -1
  100. package/dist/oauth/GoogleSignIn.cjs +1 -1
  101. package/dist/oauth/GoogleSignIn.cjs.map +1 -1
  102. package/dist/oauth/GoogleSignIn.js +5 -4
  103. package/dist/oauth/GoogleSignIn.js.map +1 -1
  104. package/dist/offline/create-offline-sync.cjs +1 -1
  105. package/dist/offline/create-offline-sync.cjs.map +1 -1
  106. package/dist/offline/create-offline-sync.js +87 -69
  107. package/dist/offline/create-offline-sync.js.map +1 -1
  108. package/dist/query/offline-persistence.cjs +1 -1
  109. package/dist/query/offline-persistence.cjs.map +1 -1
  110. package/dist/query/offline-persistence.js +67 -44
  111. package/dist/query/offline-persistence.js.map +1 -1
  112. package/dist/sse/create-event-stream.cjs +1 -1
  113. package/dist/sse/create-event-stream.cjs.map +1 -1
  114. package/dist/sse/create-event-stream.js +48 -41
  115. package/dist/sse/create-event-stream.js.map +1 -1
  116. package/dist/sse/ticket-event-stream.cjs +1 -1
  117. package/dist/sse/ticket-event-stream.cjs.map +1 -1
  118. package/dist/sse/ticket-event-stream.js +48 -19
  119. package/dist/sse/ticket-event-stream.js.map +1 -1
  120. package/dist/sse/use-event-stream.cjs +1 -1
  121. package/dist/sse/use-event-stream.cjs.map +1 -1
  122. package/dist/sse/use-event-stream.js +19 -14
  123. package/dist/sse/use-event-stream.js.map +1 -1
  124. package/dist/sw/background-sync.cjs +1 -1
  125. package/dist/sw/background-sync.cjs.map +1 -1
  126. package/dist/sw/background-sync.js +10 -8
  127. package/dist/sw/background-sync.js.map +1 -1
  128. package/dist/sw/cache.cjs +1 -1
  129. package/dist/sw/cache.cjs.map +1 -1
  130. package/dist/sw/cache.js +111 -55
  131. package/dist/sw/cache.js.map +1 -1
  132. package/dist/sw/create-push-handler.cjs +1 -1
  133. package/dist/sw/create-push-handler.cjs.map +1 -1
  134. package/dist/sw/create-push-handler.js +20 -10
  135. package/dist/sw/create-push-handler.js.map +1 -1
  136. package/dist/sw.d.ts +65 -14
  137. package/dist/tabular/compact.cjs +1 -1
  138. package/dist/tabular/compact.cjs.map +1 -1
  139. package/dist/tabular/compact.js +35 -16
  140. package/dist/tabular/compact.js.map +1 -1
  141. package/dist/tabular/manifest.cjs +1 -1
  142. package/dist/tabular/manifest.cjs.map +1 -1
  143. package/dist/tabular/manifest.js +33 -15
  144. package/dist/tabular/manifest.js.map +1 -1
  145. package/dist/tabular.d.ts +11 -1
  146. package/dist/tempest-react-sdk.cjs +1 -1
  147. package/dist/tempest-react-sdk.d.ts +543 -70
  148. package/dist/tempest-react-sdk.js +224 -224
  149. package/dist/theme/initial-theme.cjs +4 -4
  150. package/dist/theme/initial-theme.cjs.map +1 -1
  151. package/dist/theme/initial-theme.js +8 -5
  152. package/dist/theme/initial-theme.js.map +1 -1
  153. package/dist/utils/compressed-storage.cjs +1 -1
  154. package/dist/utils/compressed-storage.cjs.map +1 -1
  155. package/dist/utils/compressed-storage.js +13 -7
  156. package/dist/utils/compressed-storage.js.map +1 -1
  157. package/dist/utils/objects.cjs +1 -1
  158. package/dist/utils/objects.cjs.map +1 -1
  159. package/dist/utils/objects.js +13 -7
  160. package/dist/utils/objects.js.map +1 -1
  161. package/dist/utils/safe-url.cjs +1 -1
  162. package/dist/utils/safe-url.cjs.map +1 -1
  163. package/dist/utils/safe-url.js +4 -1
  164. package/dist/utils/safe-url.js.map +1 -1
  165. package/dist/vite/base-url.cjs +1 -1
  166. package/dist/vite/base-url.cjs.map +1 -1
  167. package/dist/vite/base-url.js +5 -1
  168. package/dist/vite/base-url.js.map +1 -1
  169. package/dist/vite/tempest-pwa-dev-sw.cjs +1 -1
  170. package/dist/vite/tempest-pwa-dev-sw.cjs.map +1 -1
  171. package/dist/vite/tempest-pwa-dev-sw.js +96 -31
  172. package/dist/vite/tempest-pwa-dev-sw.js.map +1 -1
  173. package/dist/vite/tempest-pwa-manifest.cjs +2 -2
  174. package/dist/vite/tempest-pwa-manifest.cjs.map +1 -1
  175. package/dist/vite/tempest-pwa-manifest.js +32 -16
  176. package/dist/vite/tempest-pwa-manifest.js.map +1 -1
  177. package/dist/vite.cjs +1 -1
  178. package/dist/vite.d.ts +63 -12
  179. package/dist/vite.js +4 -4
  180. package/dist/webrtc/peer-mesh.cjs +1 -1
  181. package/dist/webrtc/peer-mesh.cjs.map +1 -1
  182. package/dist/webrtc/peer-mesh.js +7 -1
  183. package/dist/webrtc/peer-mesh.js.map +1 -1
  184. package/dist/ws/create-web-socket.cjs +1 -1
  185. package/dist/ws/create-web-socket.cjs.map +1 -1
  186. package/dist/ws/create-web-socket.js +77 -61
  187. package/dist/ws/create-web-socket.js.map +1 -1
  188. package/dist/ws/use-web-socket.cjs +1 -1
  189. package/dist/ws/use-web-socket.cjs.map +1 -1
  190. package/dist/ws/use-web-socket.js +4 -3
  191. package/dist/ws/use-web-socket.js.map +1 -1
  192. package/package.json +1 -1
  193. package/template-pwa/src/pwa.tsx +42 -0
  194. package/template-pwa/src/sw.ts +5 -4
@@ -1 +1 @@
1
- {"version":3,"file":"create-data-provider.js","names":["DOT_SEGMENTS","encodeIdSegment","id","raw","defaultBuildPath","resource","createDataProvider","options","provider","params","query","pagination","pageParam","sizeParam","sort","order","sortFieldParam","sortOrderParam","sortOrderAsBoolean","filters","key","value","requestOptions","client","buildPath","ids","data","path","updateMethod"],"sources":["../../src/data/create-data-provider.ts"],"sourcesContent":["import type { ApiClient, RequestOptions } from \"@/http/types\";\nimport type { OffsetPage } from \"@/query/pagination\";\n\n/** Query-param values accepted by the API client for a list request. */\ntype ParamValue = string | number | boolean | undefined | null;\n\n/** Filters passed to {@link DataProvider.getList}, spread verbatim into the query string. */\nexport type DataFilters = Record<string, ParamValue>;\n\n/** Parameters for a paginated, sorted, filtered list request. */\nexport interface GetListParams {\n /** Offset pagination — 1-based `page` and `pageSize`. */\n pagination?: { page?: number; pageSize?: number };\n /** Single-field sort. `order` defaults to `\"asc\"`. */\n sort?: { field: string; order?: \"asc\" | \"desc\" };\n /** Arbitrary filter query params, spread onto the request. */\n filters?: DataFilters;\n}\n\n/**\n * Refine-style data provider over the Tempest FastAPI SDK CRUD/pagination\n * conventions. Implementations are stateless wrappers around an {@link ApiClient}.\n */\nexport interface DataProvider {\n /**\n * Fetch a paginated list of a resource.\n *\n * @param resource - The resource name (e.g. `\"users\"`).\n * @param params - Pagination, sort and filter parameters.\n * @returns The offset-paginated envelope.\n */\n getList<T>(resource: string, params?: GetListParams): Promise<OffsetPage<T>>;\n /**\n * Fetch a single record by id.\n *\n * @param resource - The resource name.\n * @param id - The record id.\n * @returns The record.\n */\n getOne<T>(resource: string, id: string | number): Promise<T>;\n /**\n * Fetch many records by id (default: parallel {@link DataProvider.getOne}).\n *\n * @param resource - The resource name.\n * @param ids - The record ids.\n * @returns The records, in the same order as `ids`.\n */\n getMany<T>(resource: string, ids: (string | number)[]): Promise<T[]>;\n /**\n * Create a record.\n *\n * @param resource - The resource name.\n * @param data - The creation payload.\n * @returns The created record.\n */\n create<T>(resource: string, data: unknown): Promise<T>;\n /**\n * Update a record (PATCH by default, PUT when configured).\n *\n * @param resource - The resource name.\n * @param id - The record id.\n * @param data - The update payload.\n * @returns The updated record.\n */\n update<T>(resource: string, id: string | number, data: unknown): Promise<T>;\n /**\n * Delete a record by id.\n *\n * @param resource - The resource name.\n * @param id - The record id.\n * @returns The delete response (often the deleted record).\n */\n deleteOne<T>(resource: string, id: string | number): Promise<T>;\n}\n\n/** Options to tailor {@link createDataProvider} to a backend's conventions. */\nexport interface DataProviderOptions {\n /** Query-param name for the page number. Default: `\"page\"`. */\n pageParam?: string;\n /** Query-param name for the page size. Default: `\"size\"`. */\n sizeParam?: string;\n /** Query-param name for the sort field. Default: `\"order_by\"`. */\n sortFieldParam?: string;\n /** Query-param name for the sort order. Default: `\"ascending\"`. */\n sortOrderParam?: string;\n /**\n * When `true` (default), emit the sort order as a boolean\n * (`order:\"asc\"` → `true`). When `false`, emit the literal `\"asc\"`/`\"desc\"`.\n */\n sortOrderAsBoolean?: boolean;\n /** HTTP method used by {@link DataProvider.update}. Default: `\"patch\"`. */\n updateMethod?: \"patch\" | \"put\";\n /**\n * Build the request path for a resource (and optional id).\n * Default: `id == null ? \"/\" + resource : \"/\" + resource + \"/\" + id`.\n *\n * Since 0.77.0 `id` arrives already passed through {@link encodeIdSegment}:\n * percent-encoded as one path segment, and never `\"\"`, `\".\"` or `\"..\"`. An\n * override therefore interpolates it as-is — encoding it again would turn\n * `%2F` into `%252F`.\n */\n buildPath?: (resource: string, id?: string | number) => string;\n}\n\nconst DOT_SEGMENTS: ReadonlySet<string> = new Set([\"\", \".\", \"..\"]);\n\n/**\n * Encode a record id as exactly one URL path segment.\n *\n * The data provider calls this on every id before building a path, because the\n * id is the one part of the URL that may come from outside the app — a search\n * param, a hash, a decoded route param, a value another user stored. Left raw,\n * `\"../../admin/users\"` walks the authenticated request out of the resource,\n * `\"7?role=admin\"` injects query params and `\"7#x\"` truncates the path, all\n * while the request still carries the bearer token (#438).\n *\n * `encodeURIComponent` neutralises `/`, `?`, `#` and `%`. It leaves `.` alone,\n * so `\"\"`, `\".\"` and `\"..\"` are rejected instead: the URL parser resolves them\n * as dot-segments whatever the encoding, and none of them names a record.\n * `null` and `undefined` — what an untyped caller or a not-yet-loaded route\n * param hands over — are rejected too, since `String()` would turn them into\n * the literal segments `\"null\"` and `\"undefined\"`.\n *\n * @param id - The record id, as handed to the provider.\n * @returns The percent-encoded segment, safe to interpolate between two `/`.\n * @throws {TypeError} When the id is `null`, `undefined`, `\"\"`, `\".\"` or `\"..\"`.\n *\n * @example\n * encodeIdSegment(7); // \"7\"\n * encodeIdSegment(\"a/b?x=1\"); // \"a%2Fb%3Fx%3D1\"\n * encodeIdSegment(\"..\"); // throws TypeError\n */\nexport function encodeIdSegment(id: string | number): string {\n if (id == null) {\n throw new TypeError(`Invalid record id ${String(id)}: a record id is required.`);\n }\n const raw = String(id);\n if (DOT_SEGMENTS.has(raw)) {\n throw new TypeError(\n `Invalid record id ${JSON.stringify(raw)}: it would resolve as a path dot-segment.`,\n );\n }\n return encodeURIComponent(raw);\n}\n\nconst defaultBuildPath = (resource: string, id?: string | number): string =>\n id == null ? `/${resource}` : `/${resource}/${id}`;\n\n/**\n * Create a {@link DataProvider} bound to an {@link ApiClient}.\n *\n * Maps Refine-style calls to the Tempest FastAPI SDK conventions: list →\n * `GET /{resource}?page=&size=&order_by=&ascending=&...filters`, one →\n * `GET /{resource}/{id}`, create → `POST`, update → `PATCH`/`PUT`,\n * delete → `DELETE`.\n *\n * Every id goes through {@link encodeIdSegment} before reaching `buildPath`, so\n * an id can never leave its own path segment — not with the default builder,\n * and not with an override that interpolates it.\n *\n * @param client - The HTTP client created by `createApiClient`.\n * @param options - Optional overrides for param names, sort encoding,\n * update method and path building.\n * @returns A stateless data provider.\n *\n * @example\n * const dataProvider = createDataProvider(apiClient);\n * const page = await dataProvider.getList<User>(\"users\", {\n * pagination: { page: 1, pageSize: 20 },\n * sort: { field: \"created_at\", order: \"desc\" },\n * filters: { active: true },\n * });\n */\nexport function createDataProvider(\n client: ApiClient,\n options: DataProviderOptions = {},\n): DataProvider {\n const {\n pageParam = \"page\",\n sizeParam = \"size\",\n sortFieldParam = \"order_by\",\n sortOrderParam = \"ascending\",\n sortOrderAsBoolean = true,\n updateMethod = \"patch\",\n buildPath = defaultBuildPath,\n } = options;\n\n const provider: DataProvider = {\n getList<T>(resource: string, params: GetListParams = {}): Promise<OffsetPage<T>> {\n const { pagination, sort, filters } = params;\n const query: Record<string, ParamValue> = {};\n\n if (pagination?.page != null) query[pageParam] = pagination.page;\n if (pagination?.pageSize != null) query[sizeParam] = pagination.pageSize;\n\n if (sort?.field) {\n const order = sort.order ?? \"asc\";\n query[sortFieldParam] = sort.field;\n query[sortOrderParam] = sortOrderAsBoolean ? order === \"asc\" : order;\n }\n\n if (filters) {\n for (const [key, value] of Object.entries(filters)) {\n query[key] = value;\n }\n }\n\n const requestOptions: RequestOptions = { params: query };\n return client.get<OffsetPage<T>>(buildPath(resource), requestOptions);\n },\n\n async getOne<T>(resource: string, id: string | number): Promise<T> {\n return client.get<T>(buildPath(resource, encodeIdSegment(id)));\n },\n\n getMany<T>(resource: string, ids: (string | number)[]): Promise<T[]> {\n return Promise.all(ids.map((id) => provider.getOne<T>(resource, id)));\n },\n\n create<T>(resource: string, data: unknown): Promise<T> {\n return client.post<T>(buildPath(resource), { body: data });\n },\n\n async update<T>(resource: string, id: string | number, data: unknown): Promise<T> {\n const path = buildPath(resource, encodeIdSegment(id));\n return updateMethod === \"put\"\n ? client.put<T>(path, { body: data })\n : client.patch<T>(path, { body: data });\n },\n\n async deleteOne<T>(resource: string, id: string | number): Promise<T> {\n return client.delete<T>(buildPath(resource, encodeIdSegment(id)));\n },\n };\n\n return provider;\n}\n"],"mappings":";AAwGA,IAAMA,oBAAoC,IAAI,IAAI;CAAC;CAAI;CAAK;AAAI,CAAC;AA4BjE,SAAgBC,EAAgB,GAA6B;CACzD,IAAIC,KAAM,MACN,MAAU,UAAU,qBAAqB,OAAOA,CAAE,EAAE,2BAA2B;CAEnF,IAAMC,IAAM,OAAOD,CAAE;CACrB,IAAIF,EAAa,IAAIG,CAAG,GACpB,MAAU,UACN,qBAAqB,KAAK,UAAUA,CAAG,EAAE,0CAC7C;CAEJ,OAAO,mBAAmBA,CAAG;AACjC;AAEA,IAAMC,KAAoB,GAAkB,MACxCF,KAAM,OAAO,IAAIG,MAAa,IAAIA,EAAS,GAAGH;AA2BlD,SAAgBI,EACZ,GACA,IAA+B,CAAC,GACpB;CACZ,IAAM,EACF,eAAY,QACZ,eAAY,QACZ,oBAAiB,YACjB,oBAAiB,aACjB,wBAAqB,IACrB,kBAAe,SACf,eAAYF,MACZG,GAEEC,IAAyB;EAC3B,QAAW,GAAkB,IAAwB,CAAC,GAA2B;GAC7E,IAAM,EAAE,eAAY,SAAM,eAAYC,GAChCC,IAAoC,CAAC;GAK3C,IAHIC,GAAY,QAAQ,SAAM,EAAMC,KAAaD,EAAW,OACxDA,GAAY,YAAY,SAAM,EAAME,KAAaF,EAAW,WAE5DG,GAAM,OAAO;IACb,IAAMC,IAAQD,EAAK,SAAS;IAE5B,AADA,EAAME,KAAkBF,EAAK,OAC7B,EAAMG,KAAkBC,IAAqBH,MAAU,QAAQA;GACnE;GAEA,IAAII,GACA,KAAK,IAAM,CAACC,GAAKC,MAAU,OAAO,QAAQF,CAAO,GAC7C,EAAMC,KAAOC;GAIrB,IAAMC,IAAiC,EAAE,QAAQZ,EAAM;GACvD,OAAOa,EAAO,IAAmBC,EAAUnB,CAAQ,GAAGiB,CAAc;EACxE;EAEA,MAAM,OAAU,GAAkB,GAAiC;GAC/D,OAAOC,EAAO,IAAOC,EAAUnB,GAAUJ,EAAgBC,CAAE,CAAC,CAAC;EACjE;EAEA,QAAW,GAAkB,GAAwC;GACjE,OAAO,QAAQ,IAAIuB,EAAI,KAAK,MAAOjB,EAAS,OAAUH,GAAUH,CAAE,CAAC,CAAC;EACxE;EAEA,OAAU,GAAkB,GAA2B;GACnD,OAAOqB,EAAO,KAAQC,EAAUnB,CAAQ,GAAG,EAAE,MAAMqB,EAAK,CAAC;EAC7D;EAEA,MAAM,OAAU,GAAkB,GAAqB,GAA2B;GAC9E,IAAMC,IAAOH,EAAUnB,GAAUJ,EAAgBC,CAAE,CAAC;GACpD,OAAO0B,MAAiB,QAClBL,EAAO,IAAOI,GAAM,EAAE,MAAMD,EAAK,CAAC,IAClCH,EAAO,MAASI,GAAM,EAAE,MAAMD,EAAK,CAAC;EAC9C;EAEA,MAAM,UAAa,GAAkB,GAAiC;GAClE,OAAOH,EAAO,OAAUC,EAAUnB,GAAUJ,EAAgBC,CAAE,CAAC,CAAC;EACpE;CACJ;CAEA,OAAOM;AACX"}
1
+ {"version":3,"file":"create-data-provider.js","names":["DOT_SEGMENTS","encodeIdSegment","id","raw","defaultBuildPath","resource","createDataProvider","options","provider","params","query","filters","pagination","pageParam","sizeParam","sort","order","sortFieldParam","sortOrderParam","sortOrderAsBoolean","requestOptions","client","buildPath","ids","data","path","updateMethod"],"sources":["../../src/data/create-data-provider.ts"],"sourcesContent":["import type { ApiClient, RequestOptions } from \"@/http/types\";\nimport type { OffsetPage } from \"@/query/pagination\";\n\n/** Query-param values accepted by the API client for a list request. */\ntype ParamValue = string | number | boolean | undefined | null;\n\n/** Filters passed to {@link DataProvider.getList}, spread verbatim into the query string. */\nexport type DataFilters = Record<string, ParamValue>;\n\n/** Parameters for a paginated, sorted, filtered list request. */\nexport interface GetListParams {\n /** Offset pagination — 1-based `page` and `pageSize`. */\n pagination?: { page?: number; pageSize?: number };\n /** Single-field sort. `order` defaults to `\"asc\"`. */\n sort?: { field: string; order?: \"asc\" | \"desc\" };\n /**\n * Arbitrary filter query params, spread onto the request **before**\n * `pagination` and `sort`. A filter whose key equals a pagination or sort\n * param name (`page`, `size`, `order_by`, `ascending` by default) is\n * overridden by the structured value when that one is set: until 0.81.0 the\n * filter won, so `filters: { page: 1 }` from a table's filter state silently\n * pinned every request to page 1.\n */\n filters?: DataFilters;\n}\n\n/**\n * Refine-style data provider over the Tempest FastAPI SDK CRUD/pagination\n * conventions. Implementations are stateless wrappers around an {@link ApiClient}.\n */\nexport interface DataProvider {\n /**\n * Fetch a paginated list of a resource.\n *\n * @param resource - The resource name (e.g. `\"users\"`).\n * @param params - Pagination, sort and filter parameters.\n * @returns The offset-paginated envelope.\n */\n getList<T>(resource: string, params?: GetListParams): Promise<OffsetPage<T>>;\n /**\n * Fetch a single record by id.\n *\n * @param resource - The resource name.\n * @param id - The record id.\n * @returns The record.\n */\n getOne<T>(resource: string, id: string | number): Promise<T>;\n /**\n * Fetch many records by id (default: parallel {@link DataProvider.getOne}).\n *\n * @param resource - The resource name.\n * @param ids - The record ids.\n * @returns The records, in the same order as `ids`.\n */\n getMany<T>(resource: string, ids: (string | number)[]): Promise<T[]>;\n /**\n * Create a record.\n *\n * @param resource - The resource name.\n * @param data - The creation payload.\n * @returns The created record.\n */\n create<T>(resource: string, data: unknown): Promise<T>;\n /**\n * Update a record (PATCH by default, PUT when configured).\n *\n * @param resource - The resource name.\n * @param id - The record id.\n * @param data - The update payload.\n * @returns The updated record.\n */\n update<T>(resource: string, id: string | number, data: unknown): Promise<T>;\n /**\n * Delete a record by id.\n *\n * @param resource - The resource name.\n * @param id - The record id.\n * @returns The delete response (often the deleted record).\n */\n deleteOne<T>(resource: string, id: string | number): Promise<T>;\n}\n\n/** Options to tailor {@link createDataProvider} to a backend's conventions. */\nexport interface DataProviderOptions {\n /** Query-param name for the page number. Default: `\"page\"`. */\n pageParam?: string;\n /** Query-param name for the page size. Default: `\"size\"`. */\n sizeParam?: string;\n /** Query-param name for the sort field. Default: `\"order_by\"`. */\n sortFieldParam?: string;\n /** Query-param name for the sort order. Default: `\"ascending\"`. */\n sortOrderParam?: string;\n /**\n * When `true` (default), emit the sort order as a boolean\n * (`order:\"asc\"` → `true`). When `false`, emit the literal `\"asc\"`/`\"desc\"`.\n */\n sortOrderAsBoolean?: boolean;\n /** HTTP method used by {@link DataProvider.update}. Default: `\"patch\"`. */\n updateMethod?: \"patch\" | \"put\";\n /**\n * Build the request path for a resource (and optional id).\n * Default: `id == null ? \"/\" + resource : \"/\" + resource + \"/\" + id`.\n *\n * Since 0.77.0 `id` arrives already passed through {@link encodeIdSegment}:\n * percent-encoded as one path segment, and never `\"\"`, `\".\"` or `\"..\"`. An\n * override therefore interpolates it as-is — encoding it again would turn\n * `%2F` into `%252F`.\n */\n buildPath?: (resource: string, id?: string | number) => string;\n}\n\nconst DOT_SEGMENTS: ReadonlySet<string> = new Set([\"\", \".\", \"..\"]);\n\n/**\n * Encode a record id as exactly one URL path segment.\n *\n * The data provider calls this on every id before building a path, because the\n * id is the one part of the URL that may come from outside the app — a search\n * param, a hash, a decoded route param, a value another user stored. Left raw,\n * `\"../../admin/users\"` walks the authenticated request out of the resource,\n * `\"7?role=admin\"` injects query params and `\"7#x\"` truncates the path, all\n * while the request still carries the bearer token (#438).\n *\n * `encodeURIComponent` neutralises `/`, `?`, `#` and `%`. It leaves `.` alone,\n * so `\"\"`, `\".\"` and `\"..\"` are rejected instead: the URL parser resolves them\n * as dot-segments whatever the encoding, and none of them names a record.\n * `null` and `undefined` — what an untyped caller or a not-yet-loaded route\n * param hands over — are rejected too, since `String()` would turn them into\n * the literal segments `\"null\"` and `\"undefined\"`.\n *\n * @param id - The record id, as handed to the provider.\n * @returns The percent-encoded segment, safe to interpolate between two `/`.\n * @throws {TypeError} When the id is `null`, `undefined`, `\"\"`, `\".\"` or `\"..\"`.\n *\n * @example\n * encodeIdSegment(7); // \"7\"\n * encodeIdSegment(\"a/b?x=1\"); // \"a%2Fb%3Fx%3D1\"\n * encodeIdSegment(\"..\"); // throws TypeError\n */\nexport function encodeIdSegment(id: string | number): string {\n if (id == null) {\n throw new TypeError(`Invalid record id ${String(id)}: a record id is required.`);\n }\n const raw = String(id);\n if (DOT_SEGMENTS.has(raw)) {\n throw new TypeError(\n `Invalid record id ${JSON.stringify(raw)}: it would resolve as a path dot-segment.`,\n );\n }\n return encodeURIComponent(raw);\n}\n\nconst defaultBuildPath = (resource: string, id?: string | number): string =>\n id == null ? `/${resource}` : `/${resource}/${id}`;\n\n/**\n * Create a {@link DataProvider} bound to an {@link ApiClient}.\n *\n * Maps Refine-style calls to the Tempest FastAPI SDK conventions: list →\n * `GET /{resource}?page=&size=&order_by=&ascending=&...filters`, one →\n * `GET /{resource}/{id}`, create → `POST`, update → `PATCH`/`PUT`,\n * delete → `DELETE`.\n *\n * Every id goes through {@link encodeIdSegment} before reaching `buildPath`, so\n * an id can never leave its own path segment — not with the default builder,\n * and not with an override that interpolates it.\n *\n * @param client - The HTTP client created by `createApiClient`.\n * @param options - Optional overrides for param names, sort encoding,\n * update method and path building.\n * @returns A stateless data provider.\n *\n * @example\n * const dataProvider = createDataProvider(apiClient);\n * const page = await dataProvider.getList<User>(\"users\", {\n * pagination: { page: 1, pageSize: 20 },\n * sort: { field: \"created_at\", order: \"desc\" },\n * filters: { active: true },\n * });\n */\nexport function createDataProvider(\n client: ApiClient,\n options: DataProviderOptions = {},\n): DataProvider {\n const {\n pageParam = \"page\",\n sizeParam = \"size\",\n sortFieldParam = \"order_by\",\n sortOrderParam = \"ascending\",\n sortOrderAsBoolean = true,\n updateMethod = \"patch\",\n buildPath = defaultBuildPath,\n } = options;\n\n const provider: DataProvider = {\n getList<T>(resource: string, params: GetListParams = {}): Promise<OffsetPage<T>> {\n const { pagination, sort, filters } = params;\n const query: Record<string, ParamValue> = { ...filters };\n\n if (pagination?.page != null) query[pageParam] = pagination.page;\n if (pagination?.pageSize != null) query[sizeParam] = pagination.pageSize;\n\n if (sort?.field) {\n const order = sort.order ?? \"asc\";\n query[sortFieldParam] = sort.field;\n query[sortOrderParam] = sortOrderAsBoolean ? order === \"asc\" : order;\n }\n\n const requestOptions: RequestOptions = { params: query };\n return client.get<OffsetPage<T>>(buildPath(resource), requestOptions);\n },\n\n async getOne<T>(resource: string, id: string | number): Promise<T> {\n return client.get<T>(buildPath(resource, encodeIdSegment(id)));\n },\n\n getMany<T>(resource: string, ids: (string | number)[]): Promise<T[]> {\n return Promise.all(ids.map((id) => provider.getOne<T>(resource, id)));\n },\n\n create<T>(resource: string, data: unknown): Promise<T> {\n return client.post<T>(buildPath(resource), { body: data });\n },\n\n async update<T>(resource: string, id: string | number, data: unknown): Promise<T> {\n const path = buildPath(resource, encodeIdSegment(id));\n return updateMethod === \"put\"\n ? client.put<T>(path, { body: data })\n : client.patch<T>(path, { body: data });\n },\n\n async deleteOne<T>(resource: string, id: string | number): Promise<T> {\n return client.delete<T>(buildPath(resource, encodeIdSegment(id)));\n },\n };\n\n return provider;\n}\n"],"mappings":";AA+GA,IAAMA,oBAAoC,IAAI,IAAI;CAAC;CAAI;CAAK;AAAI,CAAC;AA4BjE,SAAgBC,EAAgB,GAA6B;CACzD,IAAIC,KAAM,MACN,MAAU,UAAU,qBAAqB,OAAOA,CAAE,EAAE,2BAA2B;CAEnF,IAAMC,IAAM,OAAOD,CAAE;CACrB,IAAIF,EAAa,IAAIG,CAAG,GACpB,MAAU,UACN,qBAAqB,KAAK,UAAUA,CAAG,EAAE,0CAC7C;CAEJ,OAAO,mBAAmBA,CAAG;AACjC;AAEA,IAAMC,KAAoB,GAAkB,MACxCF,KAAM,OAAO,IAAIG,MAAa,IAAIA,EAAS,GAAGH;AA2BlD,SAAgBI,EACZ,GACA,IAA+B,CAAC,GACpB;CACZ,IAAM,EACF,eAAY,QACZ,eAAY,QACZ,oBAAiB,YACjB,oBAAiB,aACjB,wBAAqB,IACrB,kBAAe,SACf,eAAYF,MACZG,GAEEC,IAAyB;EAC3B,QAAW,GAAkB,IAAwB,CAAC,GAA2B;GAC7E,IAAM,EAAE,eAAY,SAAM,eAAYC,GAChCC,IAAoC,EAAE,GAAGC,EAAQ;GAKvD,IAHIC,GAAY,QAAQ,SAAM,EAAMC,KAAaD,EAAW,OACxDA,GAAY,YAAY,SAAM,EAAME,KAAaF,EAAW,WAE5DG,GAAM,OAAO;IACb,IAAMC,IAAQD,EAAK,SAAS;IAE5B,AADA,EAAME,KAAkBF,EAAK,OAC7B,EAAMG,KAAkBC,IAAqBH,MAAU,QAAQA;GACnE;GAEA,IAAMI,IAAiC,EAAE,QAAQV,EAAM;GACvD,OAAOW,EAAO,IAAmBC,EAAUjB,CAAQ,GAAGe,CAAc;EACxE;EAEA,MAAM,OAAU,GAAkB,GAAiC;GAC/D,OAAOC,EAAO,IAAOC,EAAUjB,GAAUJ,EAAgBC,CAAE,CAAC,CAAC;EACjE;EAEA,QAAW,GAAkB,GAAwC;GACjE,OAAO,QAAQ,IAAIqB,EAAI,KAAK,MAAOf,EAAS,OAAUH,GAAUH,CAAE,CAAC,CAAC;EACxE;EAEA,OAAU,GAAkB,GAA2B;GACnD,OAAOmB,EAAO,KAAQC,EAAUjB,CAAQ,GAAG,EAAE,MAAMmB,EAAK,CAAC;EAC7D;EAEA,MAAM,OAAU,GAAkB,GAAqB,GAA2B;GAC9E,IAAMC,IAAOH,EAAUjB,GAAUJ,EAAgBC,CAAE,CAAC;GACpD,OAAOwB,MAAiB,QAClBL,EAAO,IAAOI,GAAM,EAAE,MAAMD,EAAK,CAAC,IAClCH,EAAO,MAASI,GAAM,EAAE,MAAMD,EAAK,CAAC;EAC9C;EAEA,MAAM,UAAa,GAAkB,GAAiC;GAClE,OAAOH,EAAO,OAAUC,EAAUjB,GAAUJ,EAAgBC,CAAE,CAAC,CAAC;EACpE;CACJ;CAEA,OAAOM;AACX"}
@@ -1,2 +1,2 @@
1
- function e(e,t){let n=e.safeParse(t);if(n.success)return{success:!0,data:n.data,errors:{}};let r={};for(let e of n.error.issues){let t=e.path.length===0?`_root`:e.path.join(`.`);t in r||(r[t]=e.message)}return{success:!1,errors:r}}exports.validateForm=e;
1
+ function e(e,t){let n=e.safeParse(t);if(n.success)return{success:!0,data:n.data,errors:{}};let r={};for(let e of n.error.issues){let t=e.path.length===0?`_root`:e.path.join(`.`);Object.prototype.hasOwnProperty.call(r,t)||Object.defineProperty(r,t,{value:e.message,enumerable:!0,writable:!0,configurable:!0})}return{success:!1,errors:r}}exports.validateForm=e;
2
2
  //# sourceMappingURL=validate-form.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"validate-form.cjs","names":["validateForm","result","schema","values","errors","issue","path"],"sources":["../../src/forms/validate-form.ts"],"sourcesContent":["import type { z } from \"zod\";\n\nexport interface ValidateFormSuccess<T> {\n success: true;\n data: T;\n errors: Record<string, string>;\n}\n\nexport interface ValidateFormFailure {\n success: false;\n errors: Record<string, string>;\n}\n\nexport type ValidateFormResult<T> = ValidateFormSuccess<T> | ValidateFormFailure;\n\n/**\n * Validate `values` against a zod schema and return a per-field error map\n * compatible with most form libraries.\n *\n * Field paths follow zod's `issue.path.join(\".\")` convention — nested fields\n * become `\"address.city\"`, array entries `\"items.0.name\"`. The first issue\n * per path wins (subsequent ones are dropped to keep error UIs tidy).\n *\n * @param schema - zod schema describing the form shape.\n * @param values - Raw form values (typed as `unknown`).\n * @returns Either `{ success: true, data, errors: {} }` or `{ success: false, errors }`.\n */\nexport function validateForm<TSchema extends z.ZodTypeAny>(\n schema: TSchema,\n values: unknown,\n): ValidateFormResult<z.infer<TSchema>> {\n const result = schema.safeParse(values);\n if (result.success) {\n return { success: true, data: result.data, errors: {} };\n }\n const errors: Record<string, string> = {};\n for (const issue of result.error.issues) {\n const path = issue.path.length === 0 ? \"_root\" : issue.path.join(\".\");\n if (!(path in errors)) errors[path] = issue.message;\n }\n return { success: false, errors };\n}\n"],"mappings":"AA2BA,SAAgBA,EACZ,EACA,EACoC,CACpC,IAAMC,EAASC,EAAO,UAAUC,CAAM,EACtC,GAAIF,EAAO,QACP,MAAO,CAAE,QAAS,GAAM,KAAMA,EAAO,KAAM,OAAQ,CAAC,CAAE,EAE1D,IAAMG,EAAiC,CAAC,EACxC,IAAK,IAAMC,KAASJ,EAAO,MAAM,OAAQ,CACrC,IAAMK,EAAOD,EAAM,KAAK,SAAW,EAAI,QAAUA,EAAM,KAAK,KAAK,GAAG,EAC9DC,KAAQF,IAAS,EAAOE,GAAQD,EAAM,QAChD,CACA,MAAO,CAAE,QAAS,GAAO,QAAO,CACpC,C"}
1
+ {"version":3,"file":"validate-form.cjs","names":["validateForm","result","schema","values","errors","issue","path"],"sources":["../../src/forms/validate-form.ts"],"sourcesContent":["import type { z } from \"zod\";\n\nexport interface ValidateFormSuccess<T> {\n success: true;\n data: T;\n errors: Record<string, string>;\n}\n\nexport interface ValidateFormFailure {\n success: false;\n errors: Record<string, string>;\n}\n\nexport type ValidateFormResult<T> = ValidateFormSuccess<T> | ValidateFormFailure;\n\n/**\n * Validate `values` against a zod schema and return a per-field error map\n * compatible with most form libraries.\n *\n * Field paths follow zod's `issue.path.join(\".\")` convention — nested fields\n * become `\"address.city\"`, array entries `\"items.0.name\"`. The first issue\n * per path wins (subsequent ones are dropped to keep error UIs tidy).\n *\n * The map is a plain object, but every key is written as an **own** property\n * and looked up with `hasOwnProperty`: with `path in errors` and `errors[path] =`\n * a field named `constructor` or `toString` read as already failed through\n * `Object.prototype` and lost its message, and an issue a `superRefine` files\n * under `__proto__` replaced the map's prototype instead of becoming a key.\n *\n * @param schema - zod schema describing the form shape.\n * @param values - Raw form values (typed as `unknown`).\n * @returns Either `{ success: true, data, errors: {} }` or `{ success: false, errors }`.\n */\nexport function validateForm<TSchema extends z.ZodTypeAny>(\n schema: TSchema,\n values: unknown,\n): ValidateFormResult<z.infer<TSchema>> {\n const result = schema.safeParse(values);\n if (result.success) {\n return { success: true, data: result.data, errors: {} };\n }\n const errors: Record<string, string> = {};\n for (const issue of result.error.issues) {\n const path = issue.path.length === 0 ? \"_root\" : issue.path.join(\".\");\n if (!Object.prototype.hasOwnProperty.call(errors, path)) {\n Object.defineProperty(errors, path, {\n value: issue.message,\n enumerable: true,\n writable: true,\n configurable: true,\n });\n }\n }\n return { success: false, errors };\n}\n"],"mappings":"AAiCA,SAAgBA,EACZ,EACA,EACoC,CACpC,IAAMC,EAASC,EAAO,UAAUC,CAAM,EACtC,GAAIF,EAAO,QACP,MAAO,CAAE,QAAS,GAAM,KAAMA,EAAO,KAAM,OAAQ,CAAC,CAAE,EAE1D,IAAMG,EAAiC,CAAC,EACxC,IAAK,IAAMC,KAASJ,EAAO,MAAM,OAAQ,CACrC,IAAMK,EAAOD,EAAM,KAAK,SAAW,EAAI,QAAUA,EAAM,KAAK,KAAK,GAAG,EAC/D,OAAO,UAAU,eAAe,KAAKD,EAAQE,CAAI,GAClD,OAAO,eAAeF,EAAQE,EAAM,CAChC,MAAOD,EAAM,QACb,WAAY,GACZ,SAAU,GACV,aAAc,EAClB,CAAC,CAET,CACA,MAAO,CAAE,QAAS,GAAO,QAAO,CACpC,C"}
@@ -9,7 +9,12 @@ function e(e, t) {
9
9
  let r = {};
10
10
  for (let e of n.error.issues) {
11
11
  let t = e.path.length === 0 ? "_root" : e.path.join(".");
12
- t in r || (r[t] = e.message);
12
+ Object.prototype.hasOwnProperty.call(r, t) || Object.defineProperty(r, t, {
13
+ value: e.message,
14
+ enumerable: !0,
15
+ writable: !0,
16
+ configurable: !0
17
+ });
13
18
  }
14
19
  return {
15
20
  success: !1,
@@ -1 +1 @@
1
- {"version":3,"file":"validate-form.js","names":["validateForm","result","schema","values","errors","issue","path"],"sources":["../../src/forms/validate-form.ts"],"sourcesContent":["import type { z } from \"zod\";\n\nexport interface ValidateFormSuccess<T> {\n success: true;\n data: T;\n errors: Record<string, string>;\n}\n\nexport interface ValidateFormFailure {\n success: false;\n errors: Record<string, string>;\n}\n\nexport type ValidateFormResult<T> = ValidateFormSuccess<T> | ValidateFormFailure;\n\n/**\n * Validate `values` against a zod schema and return a per-field error map\n * compatible with most form libraries.\n *\n * Field paths follow zod's `issue.path.join(\".\")` convention — nested fields\n * become `\"address.city\"`, array entries `\"items.0.name\"`. The first issue\n * per path wins (subsequent ones are dropped to keep error UIs tidy).\n *\n * @param schema - zod schema describing the form shape.\n * @param values - Raw form values (typed as `unknown`).\n * @returns Either `{ success: true, data, errors: {} }` or `{ success: false, errors }`.\n */\nexport function validateForm<TSchema extends z.ZodTypeAny>(\n schema: TSchema,\n values: unknown,\n): ValidateFormResult<z.infer<TSchema>> {\n const result = schema.safeParse(values);\n if (result.success) {\n return { success: true, data: result.data, errors: {} };\n }\n const errors: Record<string, string> = {};\n for (const issue of result.error.issues) {\n const path = issue.path.length === 0 ? \"_root\" : issue.path.join(\".\");\n if (!(path in errors)) errors[path] = issue.message;\n }\n return { success: false, errors };\n}\n"],"mappings":";AA2BA,SAAgBA,EACZ,GACA,GACoC;CACpC,IAAMC,IAASC,EAAO,UAAUC,CAAM;CACtC,IAAIF,EAAO,SACP,OAAO;EAAE,SAAS;EAAM,MAAMA,EAAO;EAAM,QAAQ,CAAC;CAAE;CAE1D,IAAMG,IAAiC,CAAC;CACxC,KAAK,IAAMC,KAASJ,EAAO,MAAM,QAAQ;EACrC,IAAMK,IAAOD,EAAM,KAAK,WAAW,IAAI,UAAUA,EAAM,KAAK,KAAK,GAAG;EACpE,AAAMC,KAAQF,MAAS,EAAOE,KAAQD,EAAM;CAChD;CACA,OAAO;EAAE,SAAS;EAAO;CAAO;AACpC"}
1
+ {"version":3,"file":"validate-form.js","names":["validateForm","result","schema","values","errors","issue","path"],"sources":["../../src/forms/validate-form.ts"],"sourcesContent":["import type { z } from \"zod\";\n\nexport interface ValidateFormSuccess<T> {\n success: true;\n data: T;\n errors: Record<string, string>;\n}\n\nexport interface ValidateFormFailure {\n success: false;\n errors: Record<string, string>;\n}\n\nexport type ValidateFormResult<T> = ValidateFormSuccess<T> | ValidateFormFailure;\n\n/**\n * Validate `values` against a zod schema and return a per-field error map\n * compatible with most form libraries.\n *\n * Field paths follow zod's `issue.path.join(\".\")` convention — nested fields\n * become `\"address.city\"`, array entries `\"items.0.name\"`. The first issue\n * per path wins (subsequent ones are dropped to keep error UIs tidy).\n *\n * The map is a plain object, but every key is written as an **own** property\n * and looked up with `hasOwnProperty`: with `path in errors` and `errors[path] =`\n * a field named `constructor` or `toString` read as already failed through\n * `Object.prototype` and lost its message, and an issue a `superRefine` files\n * under `__proto__` replaced the map's prototype instead of becoming a key.\n *\n * @param schema - zod schema describing the form shape.\n * @param values - Raw form values (typed as `unknown`).\n * @returns Either `{ success: true, data, errors: {} }` or `{ success: false, errors }`.\n */\nexport function validateForm<TSchema extends z.ZodTypeAny>(\n schema: TSchema,\n values: unknown,\n): ValidateFormResult<z.infer<TSchema>> {\n const result = schema.safeParse(values);\n if (result.success) {\n return { success: true, data: result.data, errors: {} };\n }\n const errors: Record<string, string> = {};\n for (const issue of result.error.issues) {\n const path = issue.path.length === 0 ? \"_root\" : issue.path.join(\".\");\n if (!Object.prototype.hasOwnProperty.call(errors, path)) {\n Object.defineProperty(errors, path, {\n value: issue.message,\n enumerable: true,\n writable: true,\n configurable: true,\n });\n }\n }\n return { success: false, errors };\n}\n"],"mappings":";AAiCA,SAAgBA,EACZ,GACA,GACoC;CACpC,IAAMC,IAASC,EAAO,UAAUC,CAAM;CACtC,IAAIF,EAAO,SACP,OAAO;EAAE,SAAS;EAAM,MAAMA,EAAO;EAAM,QAAQ,CAAC;CAAE;CAE1D,IAAMG,IAAiC,CAAC;CACxC,KAAK,IAAMC,KAASJ,EAAO,MAAM,QAAQ;EACrC,IAAMK,IAAOD,EAAM,KAAK,WAAW,IAAI,UAAUA,EAAM,KAAK,KAAK,GAAG;EACpE,AAAK,OAAO,UAAU,eAAe,KAAKD,GAAQE,CAAI,KAClD,OAAO,eAAeF,GAAQE,GAAM;GAChC,OAAOD,EAAM;GACb,YAAY;GACZ,UAAU;GACV,cAAc;EAClB,CAAC;CAET;CACA,OAAO;EAAE,SAAS;EAAO;CAAO;AACpC"}
@@ -1,2 +1,2 @@
1
- function e(e){return async(t,n,r)=>{let i=e.safeParse(t);if(i.success)return{values:i.data,errors:{}};let a={},o=r.criteriaMode??`firstError`;for(let e of i.error.issues){let t=e.path.length===0?`_root`:e.path.join(`.`);o===`firstError`&&a[t]||(a[t]={type:e.code,message:e.message})}return{values:{},errors:a}}}exports.zodResolver=e;
1
+ function e(e){return async(t,n,r)=>{let i=e.safeParse(t);if(i.success)return{values:i.data,errors:{}};let a={},o=r.criteriaMode??`firstError`;for(let e of i.error.issues){let t=e.path.length===0?`_root`:e.path.join(`.`);o===`firstError`&&Object.prototype.hasOwnProperty.call(a,t)||Object.defineProperty(a,t,{value:{type:e.code,message:e.message},enumerable:!0,writable:!0,configurable:!0})}return{values:{},errors:a}}}exports.zodResolver=e;
2
2
  //# sourceMappingURL=zod-resolver.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"zod-resolver.cjs","names":["zodResolver","result","schema","values","errors","criteriaMode","options","issue","path"],"sources":["../../src/forms/zod-resolver.ts"],"sourcesContent":["import type {\n FieldErrors,\n FieldValues,\n Resolver,\n ResolverOptions,\n ResolverResult,\n} from \"react-hook-form\";\nimport type { z } from \"zod\";\n\ninterface ResolverError {\n type: string;\n message: string;\n}\n\n/**\n * Minimal `react-hook-form` resolver built on top of zod. Mirrors the shape\n * produced by `@hookform/resolvers/zod` so it can be passed straight to\n * `useForm({ resolver })`.\n *\n * Typed with react-hook-form's own `Resolver`, not a local look-alike: a\n * structurally similar type of our own compiled fine here and was then rejected\n * at the `useForm({ resolver })` call site — the only place a resolver is ever\n * used — which is why `useZodForm` had to cast its way past it.\n *\n * Of the resolver options only `criteriaMode` is read; the rest of\n * `ResolverOptions` is accepted and ignored, because react-hook-form always\n * passes the whole object.\n *\n * @example\n * const form = useForm<LoginForm>({ resolver: zodResolver(loginSchema) });\n */\nexport function zodResolver<TSchema extends z.ZodTypeAny>(\n schema: TSchema,\n): Resolver<z.infer<TSchema> & FieldValues> {\n type Values = z.infer<TSchema> & FieldValues;\n\n return async (\n values: Values,\n _context: unknown,\n options: ResolverOptions<Values>,\n ): Promise<ResolverResult<Values>> => {\n const result = schema.safeParse(values);\n if (result.success) {\n return { values: result.data as Values, errors: {} };\n }\n\n const errors: Record<string, ResolverError | object> = {};\n const criteriaMode = options.criteriaMode ?? \"firstError\";\n\n for (const issue of result.error.issues) {\n const path = issue.path.length === 0 ? \"_root\" : issue.path.join(\".\");\n if (criteriaMode === \"firstError\" && errors[path]) continue;\n errors[path] = { type: issue.code, message: issue.message };\n }\n\n return { values: {}, errors: errors as FieldErrors<Values> };\n };\n}\n"],"mappings":"AA+BA,SAAgBA,EACZ,EACwC,CAGxC,OAAO,MACH,EACA,EACA,IACkC,CAClC,IAAMC,EAASC,EAAO,UAAUC,CAAM,EACtC,GAAIF,EAAO,QACP,MAAO,CAAE,OAAQA,EAAO,KAAgB,OAAQ,CAAC,CAAE,EAGvD,IAAMG,EAAiD,CAAC,EAClDC,EAAeC,EAAQ,cAAgB,aAE7C,IAAK,IAAMC,KAASN,EAAO,MAAM,OAAQ,CACrC,IAAMO,EAAOD,EAAM,KAAK,SAAW,EAAI,QAAUA,EAAM,KAAK,KAAK,GAAG,EAChEF,IAAiB,cAAgBD,EAAOI,KAC5C,EAAOA,GAAQ,CAAE,KAAMD,EAAM,KAAM,QAASA,EAAM,OAAQ,EAC9D,CAEA,MAAO,CAAE,OAAQ,CAAC,EAAW,QAA8B,CAC/D,CACJ,C"}
1
+ {"version":3,"file":"zod-resolver.cjs","names":["zodResolver","result","schema","values","errors","criteriaMode","options","issue","path"],"sources":["../../src/forms/zod-resolver.ts"],"sourcesContent":["import type {\n FieldErrors,\n FieldValues,\n Resolver,\n ResolverOptions,\n ResolverResult,\n} from \"react-hook-form\";\nimport type { z } from \"zod\";\n\ninterface ResolverError {\n type: string;\n message: string;\n}\n\n/**\n * Minimal `react-hook-form` resolver built on top of zod. Mirrors the shape\n * produced by `@hookform/resolvers/zod` so it can be passed straight to\n * `useForm({ resolver })`.\n *\n * Typed with react-hook-form's own `Resolver`, not a local look-alike: a\n * structurally similar type of our own compiled fine here and was then rejected\n * at the `useForm({ resolver })` call site — the only place a resolver is ever\n * used — which is why `useZodForm` had to cast its way past it.\n *\n * Of the resolver options only `criteriaMode` is read; the rest of\n * `ResolverOptions` is accepted and ignored, because react-hook-form always\n * passes the whole object.\n *\n * Errors are written as **own** properties and checked with `hasOwnProperty`.\n * A plain `errors[path]` read saw `Object.prototype.constructor` for a field\n * named `constructor`, so `firstError` mode kept the inherited function instead\n * of the message, and an issue a `superRefine` files under `__proto__` replaced\n * the map's prototype instead of adding the field.\n *\n * @example\n * const form = useForm<LoginForm>({ resolver: zodResolver(loginSchema) });\n */\nexport function zodResolver<TSchema extends z.ZodTypeAny>(\n schema: TSchema,\n): Resolver<z.infer<TSchema> & FieldValues> {\n type Values = z.infer<TSchema> & FieldValues;\n\n return async (\n values: Values,\n _context: unknown,\n options: ResolverOptions<Values>,\n ): Promise<ResolverResult<Values>> => {\n const result = schema.safeParse(values);\n if (result.success) {\n return { values: result.data as Values, errors: {} };\n }\n\n const errors: Record<string, ResolverError | object> = {};\n const criteriaMode = options.criteriaMode ?? \"firstError\";\n\n for (const issue of result.error.issues) {\n const path = issue.path.length === 0 ? \"_root\" : issue.path.join(\".\");\n if (\n criteriaMode === \"firstError\" &&\n Object.prototype.hasOwnProperty.call(errors, path)\n ) {\n continue;\n }\n Object.defineProperty(errors, path, {\n value: { type: issue.code, message: issue.message },\n enumerable: true,\n writable: true,\n configurable: true,\n });\n }\n\n return { values: {}, errors: errors as FieldErrors<Values> };\n };\n}\n"],"mappings":"AAqCA,SAAgBA,EACZ,EACwC,CAGxC,OAAO,MACH,EACA,EACA,IACkC,CAClC,IAAMC,EAASC,EAAO,UAAUC,CAAM,EACtC,GAAIF,EAAO,QACP,MAAO,CAAE,OAAQA,EAAO,KAAgB,OAAQ,CAAC,CAAE,EAGvD,IAAMG,EAAiD,CAAC,EAClDC,EAAeC,EAAQ,cAAgB,aAE7C,IAAK,IAAMC,KAASN,EAAO,MAAM,OAAQ,CACrC,IAAMO,EAAOD,EAAM,KAAK,SAAW,EAAI,QAAUA,EAAM,KAAK,KAAK,GAAG,EAEhEF,IAAiB,cACjB,OAAO,UAAU,eAAe,KAAKD,EAAQI,CAAI,GAIrD,OAAO,eAAeJ,EAAQI,EAAM,CAChC,MAAO,CAAE,KAAMD,EAAM,KAAM,QAASA,EAAM,OAAQ,EAClD,WAAY,GACZ,SAAU,GACV,aAAc,EAClB,CAAC,CACL,CAEA,MAAO,CAAE,OAAQ,CAAC,EAAW,QAA8B,CAC/D,CACJ,C"}
@@ -9,9 +9,14 @@ function e(e) {
9
9
  let a = {}, o = r.criteriaMode ?? "firstError";
10
10
  for (let e of i.error.issues) {
11
11
  let t = e.path.length === 0 ? "_root" : e.path.join(".");
12
- o === "firstError" && a[t] || (a[t] = {
13
- type: e.code,
14
- message: e.message
12
+ o === "firstError" && Object.prototype.hasOwnProperty.call(a, t) || Object.defineProperty(a, t, {
13
+ value: {
14
+ type: e.code,
15
+ message: e.message
16
+ },
17
+ enumerable: !0,
18
+ writable: !0,
19
+ configurable: !0
15
20
  });
16
21
  }
17
22
  return {
@@ -1 +1 @@
1
- {"version":3,"file":"zod-resolver.js","names":["zodResolver","result","schema","values","errors","criteriaMode","options","issue","path"],"sources":["../../src/forms/zod-resolver.ts"],"sourcesContent":["import type {\n FieldErrors,\n FieldValues,\n Resolver,\n ResolverOptions,\n ResolverResult,\n} from \"react-hook-form\";\nimport type { z } from \"zod\";\n\ninterface ResolverError {\n type: string;\n message: string;\n}\n\n/**\n * Minimal `react-hook-form` resolver built on top of zod. Mirrors the shape\n * produced by `@hookform/resolvers/zod` so it can be passed straight to\n * `useForm({ resolver })`.\n *\n * Typed with react-hook-form's own `Resolver`, not a local look-alike: a\n * structurally similar type of our own compiled fine here and was then rejected\n * at the `useForm({ resolver })` call site — the only place a resolver is ever\n * used — which is why `useZodForm` had to cast its way past it.\n *\n * Of the resolver options only `criteriaMode` is read; the rest of\n * `ResolverOptions` is accepted and ignored, because react-hook-form always\n * passes the whole object.\n *\n * @example\n * const form = useForm<LoginForm>({ resolver: zodResolver(loginSchema) });\n */\nexport function zodResolver<TSchema extends z.ZodTypeAny>(\n schema: TSchema,\n): Resolver<z.infer<TSchema> & FieldValues> {\n type Values = z.infer<TSchema> & FieldValues;\n\n return async (\n values: Values,\n _context: unknown,\n options: ResolverOptions<Values>,\n ): Promise<ResolverResult<Values>> => {\n const result = schema.safeParse(values);\n if (result.success) {\n return { values: result.data as Values, errors: {} };\n }\n\n const errors: Record<string, ResolverError | object> = {};\n const criteriaMode = options.criteriaMode ?? \"firstError\";\n\n for (const issue of result.error.issues) {\n const path = issue.path.length === 0 ? \"_root\" : issue.path.join(\".\");\n if (criteriaMode === \"firstError\" && errors[path]) continue;\n errors[path] = { type: issue.code, message: issue.message };\n }\n\n return { values: {}, errors: errors as FieldErrors<Values> };\n };\n}\n"],"mappings":";AA+BA,SAAgBA,EACZ,GACwC;CAGxC,OAAO,OACH,GACA,GACA,MACkC;EAClC,IAAMC,IAASC,EAAO,UAAUC,CAAM;EACtC,IAAIF,EAAO,SACP,OAAO;GAAE,QAAQA,EAAO;GAAgB,QAAQ,CAAC;EAAE;EAGvD,IAAMG,IAAiD,CAAC,GAClDC,IAAeC,EAAQ,gBAAgB;EAE7C,KAAK,IAAMC,KAASN,EAAO,MAAM,QAAQ;GACrC,IAAMO,IAAOD,EAAM,KAAK,WAAW,IAAI,UAAUA,EAAM,KAAK,KAAK,GAAG;GACpE,AAAIF,MAAiB,gBAAgBD,EAAOI,OAC5C,EAAOA,KAAQ;IAAE,MAAMD,EAAM;IAAM,SAASA,EAAM;GAAQ;EAC9D;EAEA,OAAO;GAAE,QAAQ,CAAC;GAAW;EAA8B;CAC/D;AACJ"}
1
+ {"version":3,"file":"zod-resolver.js","names":["zodResolver","result","schema","values","errors","criteriaMode","options","issue","path"],"sources":["../../src/forms/zod-resolver.ts"],"sourcesContent":["import type {\n FieldErrors,\n FieldValues,\n Resolver,\n ResolverOptions,\n ResolverResult,\n} from \"react-hook-form\";\nimport type { z } from \"zod\";\n\ninterface ResolverError {\n type: string;\n message: string;\n}\n\n/**\n * Minimal `react-hook-form` resolver built on top of zod. Mirrors the shape\n * produced by `@hookform/resolvers/zod` so it can be passed straight to\n * `useForm({ resolver })`.\n *\n * Typed with react-hook-form's own `Resolver`, not a local look-alike: a\n * structurally similar type of our own compiled fine here and was then rejected\n * at the `useForm({ resolver })` call site — the only place a resolver is ever\n * used — which is why `useZodForm` had to cast its way past it.\n *\n * Of the resolver options only `criteriaMode` is read; the rest of\n * `ResolverOptions` is accepted and ignored, because react-hook-form always\n * passes the whole object.\n *\n * Errors are written as **own** properties and checked with `hasOwnProperty`.\n * A plain `errors[path]` read saw `Object.prototype.constructor` for a field\n * named `constructor`, so `firstError` mode kept the inherited function instead\n * of the message, and an issue a `superRefine` files under `__proto__` replaced\n * the map's prototype instead of adding the field.\n *\n * @example\n * const form = useForm<LoginForm>({ resolver: zodResolver(loginSchema) });\n */\nexport function zodResolver<TSchema extends z.ZodTypeAny>(\n schema: TSchema,\n): Resolver<z.infer<TSchema> & FieldValues> {\n type Values = z.infer<TSchema> & FieldValues;\n\n return async (\n values: Values,\n _context: unknown,\n options: ResolverOptions<Values>,\n ): Promise<ResolverResult<Values>> => {\n const result = schema.safeParse(values);\n if (result.success) {\n return { values: result.data as Values, errors: {} };\n }\n\n const errors: Record<string, ResolverError | object> = {};\n const criteriaMode = options.criteriaMode ?? \"firstError\";\n\n for (const issue of result.error.issues) {\n const path = issue.path.length === 0 ? \"_root\" : issue.path.join(\".\");\n if (\n criteriaMode === \"firstError\" &&\n Object.prototype.hasOwnProperty.call(errors, path)\n ) {\n continue;\n }\n Object.defineProperty(errors, path, {\n value: { type: issue.code, message: issue.message },\n enumerable: true,\n writable: true,\n configurable: true,\n });\n }\n\n return { values: {}, errors: errors as FieldErrors<Values> };\n };\n}\n"],"mappings":";AAqCA,SAAgBA,EACZ,GACwC;CAGxC,OAAO,OACH,GACA,GACA,MACkC;EAClC,IAAMC,IAASC,EAAO,UAAUC,CAAM;EACtC,IAAIF,EAAO,SACP,OAAO;GAAE,QAAQA,EAAO;GAAgB,QAAQ,CAAC;EAAE;EAGvD,IAAMG,IAAiD,CAAC,GAClDC,IAAeC,EAAQ,gBAAgB;EAE7C,KAAK,IAAMC,KAASN,EAAO,MAAM,QAAQ;GACrC,IAAMO,IAAOD,EAAM,KAAK,WAAW,IAAI,UAAUA,EAAM,KAAK,KAAK,GAAG;GACpE,AACIF,MAAiB,gBACjB,OAAO,UAAU,eAAe,KAAKD,GAAQI,CAAI,KAIrD,OAAO,eAAeJ,GAAQI,GAAM;IAChC,OAAO;KAAE,MAAMD,EAAM;KAAM,SAASA,EAAM;IAAQ;IAClD,YAAY;IACZ,UAAU;IACV,cAAc;GAClB,CAAC;EACL;EAEA,OAAO;GAAE,QAAQ,CAAC;GAAW;EAA8B;CAC/D;AACJ"}
@@ -1,2 +1,2 @@
1
- const e=require("./errors.cjs"),t=require("../utils/ids.cjs"),n=require("./build-url.cjs"),r=require("./credential-scope.cjs"),i=require("./csrf.cjs"),a=require("./decode-response.cjs"),o=require("./retry.cjs"),s=require("./timeout.cjs");var c=new Set([`GET`,`HEAD`,`OPTIONS`]);function l(t,n){return!c.has(n)||!(t instanceof e.TempestApiError)?!1:e.isRetriableStatus(t.status)}function u(e){return e?e===!0?{}:e:null}var d=15e3,f=3e5;function p(e){return typeof FormData<`u`&&e instanceof FormData}function m(e){return Math.round(performance.now()-e)}function h(e){return e.replace(/[?#][^]*/,``).replace(/^([^/]*\/\/)[^/]*@/,`$1`)}async function g(t,n){let r;try{r=await t.clone().json()}catch{try{r=await t.text()}catch{r=null}}return new e.TempestApiError(e.buildApiError(t.status,r,t.headers,n))}function _(c){let _=c.fetcher??globalThis.fetch.bind(globalThis);function v(e,t){if(t)return{};let n=c.getToken?.();return n?r.isTrustedCredentialTarget(e,c.baseURL,c.trustedOrigins)?{Authorization:`Bearer ${n}`}:(r.reportSuppressedCredential(e,c.baseURL),{}):{}}async function y(t,r,a,o){let{body:l,params:u,headers:m,signal:h,timeout:g,...y}=r,b=p(l),x=b?c.uploadTimeout:c.timeout,S=g===void 0?x===void 0?b?f:d:x:g,C=n.buildApiUrl(c.baseURL,t,{prefix:c.prefix,params:u}),w=m,T={...b?{}:{"Content-Type":`application/json`},...a?{"X-Request-ID":a}:{},...c.headers,...v(C,o),...i.csrfHeaders({method:y.method??`GET`,url:C,reference:c.baseURL,csrf:c.csrf,trustedOrigins:c.trustedOrigins,headers:{...c.headers,...w}}),...w},E=s.withTimeout(h,S),D={...y,signal:E.signal,headers:T,credentials:c.withCredentials?`include`:y.credentials,body:l==null?void 0:b?l:JSON.stringify(l)};try{return await _(C,D)}catch(t){throw E.timedOut()?new e.TempestApiError({status:0,detail:`A requisição excedeu ${S}ms e foi abandonada.`}):t}finally{E.dispose()}}async function b(e,t,n,r,i){let a=c.logger;if(!a)return y(e,t,n,i);let o=`${r} ${h(e)}`,s=performance.now();try{let r=await y(e,t,n,i),c={requestId:n,status:r.status,ms:m(s)},l=`${o} → ${r.status}`;return r.status>=400?a.warn(l,c):a.debug(l,c),r}catch(e){throw a.warn(`${o} → no response`,{requestId:n,ms:m(s),error:e}),e}}async function x(t,n,r){if(c.onUnauthorized){c.logger?.warn(`unauthorized — calling onUnauthorized`,{requestId:n,status:t.status});try{await c.onUnauthorized(t,r)}catch(r){if(r instanceof e.SessionSupersededError)throw r;c.logger?.warn(`onUnauthorized threw — keeping the original response error`,{requestId:n,status:t.status,error:r})}}}function S(e){return{token:e?null:c.getToken?.()??null,epoch:c.getSessionEpoch?.()??null}}async function C(n,r,i){let{skipAuth:a=!1,skipAuthRetry:o=!1,...s}=r,l=c.requestId?c.requestId():t.randomId(),u=(s.method??`GET`).toUpperCase(),d=S(a),f=await b(n,s,l,u,a);if(f.status===401&&!a&&!o){if(c.refresh){let t;try{await c.refresh(d),t=S(a),f=await b(n,s,l,u,a)}catch(t){throw t instanceof e.SessionSupersededError?t:(await x(f,l,d),await g(f,l))}f.status===401&&await x(f,l,t)}else await x(f,l,d)}if(!f.ok)throw await g(f,l);return i(f)}async function w(e,t,n){let{retry:r,...i}=t,a=u(r===void 0?c.retry:r);if(!a)return C(e,i,n);let s=(i.method??`GET`).toUpperCase();return o.retry(()=>C(e,i,n),{...a,shouldRetry:a.shouldRetry??(e=>l(e,s))})}async function T(e,t={}){return w(e,t,a.decodeByContentType)}async function E(e,t,n=`POST`,r){return T(e,{...r,method:n,body:t})}return{request:T,get:(e,t)=>T(e,{...t,method:`GET`}),post:(e,t)=>T(e,{...t,method:`POST`}),put:(e,t)=>T(e,{...t,method:`PUT`}),patch:(e,t)=>T(e,{...t,method:`PATCH`}),delete:(e,t)=>T(e,{...t,method:`DELETE`}),blob:(e,t)=>w(e,{...t},e=>e.blob()),arrayBuffer:(e,t)=>w(e,{...t},e=>e.arrayBuffer()),upload:E,url:(e,t)=>n.buildApiUrl(c.baseURL,e,{prefix:c.prefix,params:t})}}exports.createApiClient=_;
1
+ const e=require("./errors.cjs"),t=require("../utils/ids.cjs"),n=require("./build-url.cjs"),r=require("./credential-scope.cjs"),i=require("./csrf.cjs"),a=require("./decode-response.cjs"),o=require("./retry.cjs"),s=require("./timeout.cjs");var c=new Set([`GET`,`HEAD`,`OPTIONS`]);function l(t,n){return!c.has(n)||!(t instanceof e.TempestApiError)||t.code===d?!1:e.isRetriableStatus(t.status)}function u(e){return e?e===!0?{}:e:null}var d=`REDIRECT_REFUSED`;function f(e){return e.type===`opaqueredirect`||e.status>=300&&e.status<400&&e.status!==304}var p=15e3,m=3e5;function h(e){return typeof FormData<`u`&&e instanceof FormData}function g(e){return Math.round(performance.now()-e)}function _(e){return e.replace(/[?#][^]*/,``).replace(/^([^/]*\/\/)[^/]*@/,`$1`)}async function v(t,n){let r;try{r=await t.clone().json()}catch{try{r=await t.text()}catch{r=null}}return new e.TempestApiError(e.buildApiError(t.status,r,t.headers,n))}function y(c){let y=c.fetcher??globalThis.fetch.bind(globalThis);function b(e,t,n){let i=n?null:c.getToken?.();if(!t){i&&r.reportSuppressedCredential(e,c.baseURL);let t=Object.keys(c.headers??{});return t.length>0&&r.reportSuppressedCredential(e,c.baseURL,t.join(`, `)),{}}return{...c.headers,...i?{Authorization:`Bearer ${i}`}:{}}}async function x(t,n,r,a,o){let{body:l,headers:u,signal:g,timeout:_,...v}=r,x=h(l),S=x?c.uploadTimeout:c.timeout,C=_===void 0?S===void 0?x?m:p:S:_,w=u,T={...x?{}:{"Content-Type":`application/json`},...a?{"X-Request-ID":a}:{},...b(t,n,o),...i.csrfHeaders({method:v.method??`GET`,url:t,reference:c.baseURL,csrf:c.csrf,trustedOrigins:c.trustedOrigins,headers:{...c.headers,...w}}),...w},E=n&&v.redirect===void 0&&Object.keys(c.headers??{}).length>0,D=s.withTimeout(g,C),O={...v,...E?{redirect:`manual`}:{},signal:D.signal,headers:T,credentials:c.withCredentials?`include`:v.credentials,body:l==null?void 0:x?l:JSON.stringify(l)},k;try{k=await y(t,O)}catch(t){throw D.timedOut()?new e.TempestApiError({status:0,detail:`A requisição excedeu ${C}ms e foi abandonada.`}):t}finally{D.dispose()}if(E&&f(k))throw new e.TempestApiError({status:k.status,code:d,requestId:a,detail:`Redirect não seguido: a requisição levava os headers fixos do cliente.`});return k}async function S(e,t,n,r,i,a,o){let s=c.logger;if(!s)return x(t,n,r,i,o);let l=`${a} ${_(e)}`,u=performance.now();try{let e=await x(t,n,r,i,o),a={requestId:i,status:e.status,ms:g(u)},c=`${l} → ${e.status}`;return e.status>=400?s.warn(c,a):s.debug(c,a),e}catch(e){throw s.warn(`${l} → no response`,{requestId:i,ms:g(u),error:e}),e}}async function C(t,n,r){if(c.onUnauthorized){c.logger?.warn(`unauthorized — calling onUnauthorized`,{requestId:n,status:t.status});try{await c.onUnauthorized(t,r)}catch(r){if(r instanceof e.SessionSupersededError)throw r;c.logger?.warn(`onUnauthorized threw — keeping the original response error`,{requestId:n,status:t.status,error:r})}}}function w(e){return{token:e?null:c.getToken?.()??null,epoch:c.getSessionEpoch?.()??null}}async function T(i,a,o){let{skipAuth:s=!1,skipAuthRetry:l=!1,params:u,...d}=a,f=c.requestId?c.requestId():t.randomId(),p=(d.method??`GET`).toUpperCase(),m=n.buildApiUrl(c.baseURL,i,{prefix:c.prefix,params:u}),h=r.isTrustedCredentialTarget(m,c.baseURL,c.trustedOrigins),g=w(s),_=await S(i,m,h,d,f,p,s);if(_.status===401&&h&&!s&&!l){if(c.refresh){let t;try{await c.refresh(g),t=w(s),_=await S(i,m,h,d,f,p,s)}catch(t){throw t instanceof e.SessionSupersededError?t:(await C(_,f,g),await v(_,f))}_.status===401&&await C(_,f,t)}else await C(_,f,g)}if(!_.ok)throw await v(_,f);return o(_)}async function E(e,t,n){let{retry:r,...i}=t,a=u(r===void 0?c.retry:r);if(!a)return T(e,i,n);let s=(i.method??`GET`).toUpperCase();return o.retry(()=>T(e,i,n),{...a,shouldRetry:a.shouldRetry??(e=>l(e,s))})}async function D(e,t={}){return E(e,t,a.decodeByContentType)}async function O(e,t,n=`POST`,r){return D(e,{...r,method:n,body:t})}return{request:D,get:(e,t)=>D(e,{...t,method:`GET`}),post:(e,t)=>D(e,{...t,method:`POST`}),put:(e,t)=>D(e,{...t,method:`PUT`}),patch:(e,t)=>D(e,{...t,method:`PATCH`}),delete:(e,t)=>D(e,{...t,method:`DELETE`}),blob:(e,t)=>E(e,{...t},e=>e.blob()),arrayBuffer:(e,t)=>E(e,{...t},e=>e.arrayBuffer()),upload:O,url:(e,t)=>n.buildApiUrl(c.baseURL,e,{prefix:c.prefix,params:t})}}exports.createApiClient=y;
2
2
  //# sourceMappingURL=api-client.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"api-client.cjs","names":["TempestApiError","isRetriableStatus","buildApiError","isTrustedCredentialTarget","buildApiUrl","csrfHeaders","withTimeout","SessionSupersededError","randomId","retryWithBackoff","decodeByContentType","require_errors","require_ids","require_build_url","require_credential_scope","require_csrf","require_decode_response","require_retry","require_timeout","IDEMPOTENT_METHODS","isRetriableFailure","method","error","resolveRetry","config","DEFAULT_TIMEOUT","DEFAULT_UPLOAD_TIMEOUT","isFormData","body","elapsedMs","startedAt","pathForLog","path","parseError","response","sentRequestId","createApiClient","fetcher","authHeaders","skipAuth","token","url","rawRequest","rest","options","isForm","configured","limit","timeout","callerHeaders","headers","finalHeaders","requestId","timed","signal","init","cause","send","log","target","entry","line","notifyUnauthorized","context","authContext","attempt","sent","skipAuthRetry","replayed","decode","run","perRequest","retryOptions","request","upload","formData"],"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 { isTrustedCredentialTarget, reportSuppressedCredential } from \"./credential-scope\";\nimport { csrfHeaders } from \"./csrf\";\nimport { decodeByContentType } from \"./decode-response\";\nimport type { ResponseDecoder } from \"./decode-response\";\nimport {\n buildApiError,\n SessionSupersededError,\n TempestApiError,\n isRetriableStatus,\n} from \"./errors\";\nimport { retry as retryWithBackoff } from \"./retry\";\nimport type { RetryOptions } from \"./retry\";\nimport { withTimeout } from \"./timeout\";\nimport type { ApiClient, ApiClientConfig, RequestAuthContext, 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 */\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 isRetriableStatus(error.status);\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\n/** Default milliseconds before a request is abandoned. */\nconst DEFAULT_TIMEOUT = 15_000;\n\n/** Default milliseconds before a `FormData` request is abandoned. */\nconst DEFAULT_UPLOAD_TIMEOUT = 300_000;\n\nfunction isFormData(body: unknown): body is FormData {\n return typeof FormData !== \"undefined\" && body instanceof FormData;\n}\n\n/**\n * Milliseconds since a `performance.now()` reading, rounded.\n *\n * `performance.now()` rather than `Date.now()` because this measures a\n * duration: the wall clock can step sideways mid-request (an NTP correction, a\n * VM resuming, the user changing the clock) and turn a 40 ms call into a\n * negative number. The monotonic clock cannot.\n *\n * @param startedAt - The reading taken before the work started.\n * @returns Whole milliseconds elapsed.\n */\nfunction elapsedMs(startedAt: number): number {\n return Math.round(performance.now() - startedAt);\n}\n\n/**\n * The request path as the log line shows it: no query, no fragment, no userinfo.\n *\n * The logger promises never to write the query string, and `params` was never\n * the only way one gets in: a call site can write `\"/files?access_token=…\"`\n * inline, and an absolute path — the signed upload endpoint or CDN URL that\n * `buildApiUrl` passes through untouched — carries its signature in the query\n * and may carry `user:password@` in the authority. Everything from the first\n * `?` or `#` is dropped and the userinfo of an absolute (or scheme-relative)\n * URL is removed; the host and the path stay, because they are what makes the\n * line useful. Pure string work, no `URL` round trip, so a path that would not\n * parse is still logged instead of throwing inside the logging branch.\n *\n * @param path - The path exactly as the call site passed it.\n * @returns The path without query, fragment or credentials.\n */\nfunction pathForLog(path: string): string {\n return path.replace(/[?#][^]*/, \"\").replace(/^([^/]*\\/\\/)[^/]*@/, \"$1\");\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 * **Binary bodies** have their own methods — `blob()` and `arrayBuffer()` — which\n * run the same pipeline and skip the parse. `request()` no longer decodes an\n * unknown `Content-Type` as text either: it returns a `Blob` and says so once in\n * a development build, because reading `image/jpeg` as UTF-8 destroyed the bytes\n * on the way in.\n *\n * **The bearer token is scoped to `baseURL`'s origin** since 0.66.0. A path may\n * be an absolute URL, which overrides the base entirely, so without a scope the\n * destination of a credentialed request could come from a value read off the\n * network. A request to another origin still goes out; it goes without the\n * header, and a development build says so once. Declare the exceptions in\n * {@link ApiClientConfig.trustedOrigins}.\n *\n * **CSRF** is off unless you set `csrf`. With it, every unsafe request (anything\n * but `GET`/`HEAD`/`OPTIONS`/`TRACE`) echoes the `csrf_token` cookie in an\n * `X-CSRF-Token` header — the double-submit contract of `tempest-fastapi-sdk`'s\n * `CSRFMiddleware` — scoped to the same origins as the bearer token and sent on\n * `skipAuth` requests too. See {@link ApiClientConfig.csrf}.\n *\n * **Retries** are off unless you set `retry`. See {@link ApiClientConfig.retry}\n * for the built-in policy; it never replays a write. A single call overrides it\n * with {@link RequestOptions.retry}, and opts out of auth entirely with\n * {@link RequestOptions.skipAuth}.\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`/`blob`/\n * `arrayBuffer`/`upload`.\n */\nexport function createApiClient(config: ApiClientConfig): ApiClient {\n const fetcher = config.fetcher ?? globalThis.fetch.bind(globalThis);\n\n /**\n * The `Authorization` header for a request, or nothing.\n *\n * Takes the resolved URL rather than reading `config.baseURL`, because an\n * absolute path overrides the base entirely (see `buildApiUrl`) and the\n * credential is scoped to an origin, not to a client.\n *\n * @param url - The absolute URL the request is going to.\n * @param skipAuth - Whether the caller opted this request out of auth.\n * @returns The header, or an empty object.\n */\n function authHeaders(url: string, skipAuth: boolean): Record<string, string> {\n if (skipAuth) return {};\n const token = config.getToken?.();\n if (!token) return {};\n if (!isTrustedCredentialTarget(url, config.baseURL, config.trustedOrigins)) {\n reportSuppressedCredential(url, config.baseURL);\n return {};\n }\n return { Authorization: `Bearer ${token}` };\n }\n\n async function rawRequest(\n path: string,\n options: RequestOptions,\n requestId: string | undefined,\n skipAuth: boolean,\n ): Promise<Response> {\n const { body, params, headers, signal, timeout, ...rest } = options;\n const isForm = isFormData(body);\n const configured = isForm ? config.uploadTimeout : config.timeout;\n const fallback = isForm ? DEFAULT_UPLOAD_TIMEOUT : DEFAULT_TIMEOUT;\n const limit =\n timeout !== undefined ? timeout : configured !== undefined ? configured : fallback;\n\n const url = buildApiUrl(config.baseURL, path, { prefix: config.prefix, params });\n\n const callerHeaders = headers as Record<string, string> | undefined;\n const finalHeaders: Record<string, string> = {\n ...(isForm ? {} : { \"Content-Type\": \"application/json\" }),\n ...(requestId ? { \"X-Request-ID\": requestId } : {}),\n ...config.headers,\n ...authHeaders(url, skipAuth),\n ...csrfHeaders({\n method: rest.method ?? \"GET\",\n url,\n reference: config.baseURL,\n csrf: config.csrf,\n trustedOrigins: config.trustedOrigins,\n headers: { ...config.headers, ...callerHeaders },\n }),\n ...callerHeaders,\n };\n\n const timed = withTimeout(signal, limit);\n const init: RequestInit = {\n ...rest,\n signal: timed.signal,\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 try {\n return await fetcher(url, init);\n } catch (cause) {\n if (timed.timedOut()) {\n throw new TempestApiError({\n status: 0,\n detail: `A requisição excedeu ${limit}ms e foi abandonada.`,\n });\n }\n throw cause;\n } finally {\n timed.dispose();\n }\n }\n\n async function send(\n path: string,\n options: RequestOptions,\n requestId: string,\n method: string,\n skipAuth: boolean,\n ): Promise<Response> {\n const log = config.logger;\n if (!log) return rawRequest(path, options, requestId, skipAuth);\n\n const target = `${method} ${pathForLog(path)}`;\n const startedAt = performance.now();\n try {\n const response = await rawRequest(path, options, requestId, skipAuth);\n const entry = { requestId, status: response.status, ms: elapsedMs(startedAt) };\n const line = `${target} → ${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(`${target} → no response`, {\n requestId,\n ms: elapsedMs(startedAt),\n error,\n });\n throw error;\n }\n }\n\n /**\n * Call `onUnauthorized`, keeping the request's own error unless the hook\n * says the attempt belongs to an ended session.\n *\n * @param response - The `401` that ended the attempt.\n * @param requestId - Correlation id for the log.\n * @param context - The credential and session the attempt left with.\n * @throws {SessionSupersededError} When `onUnauthorized` threw it.\n */\n async function notifyUnauthorized(\n response: Response,\n requestId: string,\n context: RequestAuthContext,\n ): Promise<void> {\n if (!config.onUnauthorized) return;\n config.logger?.warn(`unauthorized — calling onUnauthorized`, {\n requestId,\n status: response.status,\n });\n try {\n await config.onUnauthorized(response, context);\n } catch (error) {\n if (error instanceof SessionSupersededError) throw error;\n config.logger?.warn(`onUnauthorized threw — keeping the original response error`, {\n requestId,\n status: response.status,\n error,\n });\n }\n }\n\n /**\n * The credential and session an attempt leaves with, read just before it is\n * sent.\n *\n * @param skipAuth - Whether the attempt goes out without credentials.\n * @returns The context handed to `refresh` and `onUnauthorized`.\n */\n function authContext(skipAuth: boolean): RequestAuthContext {\n return {\n token: skipAuth ? null : (config.getToken?.() ?? null),\n epoch: config.getSessionEpoch?.() ?? null,\n };\n }\n\n async function attempt<T>(\n path: string,\n options: RequestOptions,\n decode: ResponseDecoder<T>,\n ): Promise<T> {\n const { skipAuth = false, skipAuthRetry = false, ...init } = options;\n const requestId = config.requestId ? config.requestId() : randomId();\n const method = (init.method ?? \"GET\").toUpperCase();\n const sent = authContext(skipAuth);\n let response = await send(path, init, requestId, method, skipAuth);\n\n if (response.status === 401 && !skipAuth && !skipAuthRetry) {\n if (config.refresh) {\n let replayed: RequestAuthContext;\n try {\n await config.refresh(sent);\n replayed = authContext(skipAuth);\n response = await send(path, init, requestId, method, skipAuth);\n } catch (error) {\n if (error instanceof SessionSupersededError) throw error;\n await notifyUnauthorized(response, requestId, sent);\n throw await parseError(response, requestId);\n }\n if (response.status === 401) {\n await notifyUnauthorized(response, requestId, replayed);\n }\n } else {\n await notifyUnauthorized(response, requestId, sent);\n }\n }\n\n if (!response.ok) {\n throw await parseError(response, requestId);\n }\n\n return decode(response);\n }\n\n async function run<T>(\n path: string,\n options: RequestOptions,\n decode: ResponseDecoder<T>,\n ): Promise<T> {\n const { retry: perRequest, ...rest } = options;\n const retryOptions = resolveRetry(perRequest !== undefined ? perRequest : config.retry);\n if (!retryOptions) return attempt<T>(path, rest, decode);\n\n const method = (rest.method ?? \"GET\").toUpperCase();\n return retryWithBackoff(() => attempt<T>(path, rest, decode), {\n ...retryOptions,\n shouldRetry:\n retryOptions.shouldRetry ?? ((error: unknown) => isRetriableFailure(error, method)),\n });\n }\n\n async function request<T>(path: string, options: RequestOptions = {}): Promise<T> {\n return run<T>(path, options, decodeByContentType);\n }\n\n async function upload<T>(\n path: string,\n formData: FormData,\n method: \"POST\" | \"PUT\" | \"PATCH\" = \"POST\",\n options?: Omit<RequestOptions, \"body\" | \"method\">,\n ): Promise<T> {\n return request<T>(path, { ...options, 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 blob: (path: string, options?: RequestOptions) =>\n run<Blob>(path, { ...options }, (response) => response.blob()),\n arrayBuffer: (path: string, options?: RequestOptions) =>\n run<ArrayBuffer>(path, { ...options }, (response) => response.arrayBuffer()),\n upload,\n url: (path: string, params?: RequestOptions[\"params\"]) =>\n buildApiUrl(config.baseURL, path, { prefix: config.prefix, params }),\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"api-client.cjs","names":["TempestApiError","isRetriableStatus","buildApiError","csrfHeaders","withTimeout","SessionSupersededError","randomId","buildApiUrl","isTrustedCredentialTarget","retryWithBackoff","decodeByContentType","require_errors","require_ids","require_build_url","require_credential_scope","require_csrf","require_decode_response","require_retry","require_timeout","IDEMPOTENT_METHODS","isRetriableFailure","method","error","REDIRECT_REFUSED","resolveRetry","config","isStoppedRedirect","response","DEFAULT_TIMEOUT","DEFAULT_UPLOAD_TIMEOUT","isFormData","body","elapsedMs","startedAt","pathForLog","path","parseError","sentRequestId","createApiClient","fetcher","scopedHeaders","token","skipAuth","inScope","url","names","rawRequest","rest","options","isForm","configured","limit","timeout","callerHeaders","headers","finalHeaders","requestId","holdRedirects","timed","signal","init","cause","send","log","target","entry","line","notifyUnauthorized","context","authContext","attempt","sent","skipAuthRetry","replayed","decode","run","perRequest","retryOptions","request","upload","formData"],"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 { isTrustedCredentialTarget, reportSuppressedCredential } from \"./credential-scope\";\nimport { csrfHeaders } from \"./csrf\";\nimport { decodeByContentType } from \"./decode-response\";\nimport type { ResponseDecoder } from \"./decode-response\";\nimport {\n buildApiError,\n SessionSupersededError,\n TempestApiError,\n isRetriableStatus,\n} from \"./errors\";\nimport { retry as retryWithBackoff } from \"./retry\";\nimport type { RetryOptions } from \"./retry\";\nimport { withTimeout } from \"./timeout\";\nimport type { ApiClient, ApiClientConfig, RequestAuthContext, 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 */\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 if (error.code === REDIRECT_REFUSED) return false;\n return isRetriableStatus(error.status);\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\n/**\n * The `code` of the {@link TempestApiError} thrown when a request carrying the\n * client's static `headers` is answered with a redirect.\n *\n * Kept out of the built-in retry policy: the status is `0` in a browser, which\n * would otherwise read as a network failure, and replaying gets the same\n * redirect back.\n */\nconst REDIRECT_REFUSED = \"REDIRECT_REFUSED\";\n\n/**\n * Whether `response` is a redirect that `redirect: \"manual\"` stopped.\n *\n * Two shapes, measured: a browser hands back an `opaqueredirect` with status\n * `0` and no readable headers (Chrome 150, #554); a server runtime such as\n * Node's `fetch` hands back the real `3xx` with its `Location`. `304` is a\n * cache answer, not a redirect.\n *\n * @param response - What the fetcher resolved with.\n * @returns Whether the request was redirected and not followed.\n */\nfunction isStoppedRedirect(response: Response): boolean {\n if (response.type === \"opaqueredirect\") return true;\n return response.status >= 300 && response.status < 400 && response.status !== 304;\n}\n\n/** Default milliseconds before a request is abandoned. */\nconst DEFAULT_TIMEOUT = 15_000;\n\n/** Default milliseconds before a `FormData` request is abandoned. */\nconst DEFAULT_UPLOAD_TIMEOUT = 300_000;\n\nfunction isFormData(body: unknown): body is FormData {\n return typeof FormData !== \"undefined\" && body instanceof FormData;\n}\n\n/**\n * Milliseconds since a `performance.now()` reading, rounded.\n *\n * `performance.now()` rather than `Date.now()` because this measures a\n * duration: the wall clock can step sideways mid-request (an NTP correction, a\n * VM resuming, the user changing the clock) and turn a 40 ms call into a\n * negative number. The monotonic clock cannot.\n *\n * @param startedAt - The reading taken before the work started.\n * @returns Whole milliseconds elapsed.\n */\nfunction elapsedMs(startedAt: number): number {\n return Math.round(performance.now() - startedAt);\n}\n\n/**\n * The request path as the log line shows it: no query, no fragment, no userinfo.\n *\n * The logger promises never to write the query string, and `params` was never\n * the only way one gets in: a call site can write `\"/files?access_token=…\"`\n * inline, and an absolute path — the signed upload endpoint or CDN URL that\n * `buildApiUrl` passes through untouched — carries its signature in the query\n * and may carry `user:password@` in the authority. Everything from the first\n * `?` or `#` is dropped and the userinfo of an absolute (or scheme-relative)\n * URL is removed; the host and the path stay, because they are what makes the\n * line useful. Pure string work, no `URL` round trip, so a path that would not\n * parse is still logged instead of throwing inside the logging branch.\n *\n * @param path - The path exactly as the call site passed it.\n * @returns The path without query, fragment or credentials.\n */\nfunction pathForLog(path: string): string {\n return path.replace(/[?#][^]*/, \"\").replace(/^([^/]*\\/\\/)[^/]*@/, \"$1\");\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 * **Binary bodies** have their own methods — `blob()` and `arrayBuffer()` — which\n * run the same pipeline and skip the parse. `request()` no longer decodes an\n * unknown `Content-Type` as text either: it returns a `Blob` and says so once in\n * a development build, because reading `image/jpeg` as UTF-8 destroyed the bytes\n * on the way in.\n *\n * **The bearer token is scoped to `baseURL`'s origin** since 0.66.0. A path may\n * be an absolute URL, which overrides the base entirely, so without a scope the\n * destination of a credentialed request could come from a value read off the\n * network. A request to another origin still goes out; it goes without the\n * header, and a development build says so once. Declare the exceptions in\n * {@link ApiClientConfig.trustedOrigins}. Since 0.82.0 the static\n * {@link ApiClientConfig.headers} follow the same scope, and a `401` from\n * outside it is thrown without `refresh` or `onUnauthorized` — it answered a\n * request that never carried this session's credential.\n *\n * **Redirects.** The scope is checked against the URL the request starts at;\n * where a redirect then goes is the transport's call. For `Authorization` that\n * is safe: browsers strip it on a cross-origin redirect, measured in Chrome 150\n * for `fetch` and XHR on `301`/`302`/`303`/`307`/`308` (#554). They strip\n * nothing else, so a request carrying the static {@link ApiClientConfig.headers}\n * goes out with `redirect: \"manual\"`, and a redirect answering it is thrown as a\n * {@link TempestApiError} with `code: \"REDIRECT_REFUSED\"` instead of being\n * followed. It cannot be followed by hand: a browser's manual redirect is an\n * opaque response whose `Location` is unreadable. A `redirect` set on the call\n * always wins.\n *\n * **CSRF** is off unless you set `csrf`. With it, every unsafe request (anything\n * but `GET`/`HEAD`/`OPTIONS`/`TRACE`) echoes the `csrf_token` cookie in an\n * `X-CSRF-Token` header — the double-submit contract of `tempest-fastapi-sdk`'s\n * `CSRFMiddleware` — scoped to the same origins as the bearer token and sent on\n * `skipAuth` requests too. See {@link ApiClientConfig.csrf}.\n *\n * **Retries** are off unless you set `retry`. See {@link ApiClientConfig.retry}\n * for the built-in policy; it never replays a write. A single call overrides it\n * with {@link RequestOptions.retry}, and opts out of auth entirely with\n * {@link RequestOptions.skipAuth}.\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`/`blob`/\n * `arrayBuffer`/`upload`.\n */\nexport function createApiClient(config: ApiClientConfig): ApiClient {\n const fetcher = config.fetcher ?? globalThis.fetch.bind(globalThis);\n\n /**\n * The headers that belong to the client's credential scope — the static\n * `config.headers` and the `Authorization` header — or nothing.\n *\n * Takes the resolved URL rather than reading `config.baseURL`, because an\n * absolute path overrides the base entirely (see `buildApiUrl`) and the\n * credential is scoped to an origin, not to a client. Static headers ride\n * with the token since 0.82.0: an `X-Api-Key` set once on the client is as\n * much a credential as the bearer (#530).\n *\n * @param url - The absolute URL the request is going to.\n * @param inScope - Whether `url` is inside the credential scope.\n * @param skipAuth - Whether the caller opted this request out of auth.\n * @returns The headers, or an empty object.\n */\n function scopedHeaders(\n url: string,\n inScope: boolean,\n skipAuth: boolean,\n ): Record<string, string> {\n const token = skipAuth ? null : config.getToken?.();\n if (!inScope) {\n if (token) reportSuppressedCredential(url, config.baseURL);\n const names = Object.keys(config.headers ?? {});\n if (names.length > 0) reportSuppressedCredential(url, config.baseURL, names.join(\", \"));\n return {};\n }\n return { ...config.headers, ...(token ? { Authorization: `Bearer ${token}` } : {}) };\n }\n\n async function rawRequest(\n url: string,\n inScope: boolean,\n options: RequestOptions,\n requestId: string | undefined,\n skipAuth: boolean,\n ): Promise<Response> {\n const { body, headers, signal, timeout, ...rest } = options;\n const isForm = isFormData(body);\n const configured = isForm ? config.uploadTimeout : config.timeout;\n const fallback = isForm ? DEFAULT_UPLOAD_TIMEOUT : DEFAULT_TIMEOUT;\n const limit =\n timeout !== undefined ? timeout : configured !== undefined ? configured : fallback;\n\n const callerHeaders = headers as Record<string, string> | undefined;\n const finalHeaders: Record<string, string> = {\n ...(isForm ? {} : { \"Content-Type\": \"application/json\" }),\n ...(requestId ? { \"X-Request-ID\": requestId } : {}),\n ...scopedHeaders(url, inScope, skipAuth),\n ...csrfHeaders({\n method: rest.method ?? \"GET\",\n url,\n reference: config.baseURL,\n csrf: config.csrf,\n trustedOrigins: config.trustedOrigins,\n headers: { ...config.headers, ...callerHeaders },\n }),\n ...callerHeaders,\n };\n\n const holdRedirects =\n inScope && rest.redirect === undefined && Object.keys(config.headers ?? {}).length > 0;\n const timed = withTimeout(signal, limit);\n const init: RequestInit = {\n ...rest,\n ...(holdRedirects ? { redirect: \"manual\" as const } : {}),\n signal: timed.signal,\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 let response: Response;\n try {\n response = await fetcher(url, init);\n } catch (cause) {\n if (timed.timedOut()) {\n throw new TempestApiError({\n status: 0,\n detail: `A requisição excedeu ${limit}ms e foi abandonada.`,\n });\n }\n throw cause;\n } finally {\n timed.dispose();\n }\n if (holdRedirects && isStoppedRedirect(response)) {\n throw new TempestApiError({\n status: response.status,\n code: REDIRECT_REFUSED,\n requestId,\n detail: \"Redirect não seguido: a requisição levava os headers fixos do cliente.\",\n });\n }\n return response;\n }\n\n async function send(\n path: string,\n url: string,\n inScope: boolean,\n options: RequestOptions,\n requestId: string,\n method: string,\n skipAuth: boolean,\n ): Promise<Response> {\n const log = config.logger;\n if (!log) return rawRequest(url, inScope, options, requestId, skipAuth);\n\n const target = `${method} ${pathForLog(path)}`;\n const startedAt = performance.now();\n try {\n const response = await rawRequest(url, inScope, options, requestId, skipAuth);\n const entry = { requestId, status: response.status, ms: elapsedMs(startedAt) };\n const line = `${target} → ${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(`${target} → no response`, {\n requestId,\n ms: elapsedMs(startedAt),\n error,\n });\n throw error;\n }\n }\n\n /**\n * Call `onUnauthorized`, keeping the request's own error unless the hook\n * says the attempt belongs to an ended session.\n *\n * @param response - The `401` that ended the attempt.\n * @param requestId - Correlation id for the log.\n * @param context - The credential and session the attempt left with.\n * @throws {SessionSupersededError} When `onUnauthorized` threw it.\n */\n async function notifyUnauthorized(\n response: Response,\n requestId: string,\n context: RequestAuthContext,\n ): Promise<void> {\n if (!config.onUnauthorized) return;\n config.logger?.warn(`unauthorized — calling onUnauthorized`, {\n requestId,\n status: response.status,\n });\n try {\n await config.onUnauthorized(response, context);\n } catch (error) {\n if (error instanceof SessionSupersededError) throw error;\n config.logger?.warn(`onUnauthorized threw — keeping the original response error`, {\n requestId,\n status: response.status,\n error,\n });\n }\n }\n\n /**\n * The credential and session an attempt leaves with, read just before it is\n * sent.\n *\n * @param skipAuth - Whether the attempt goes out without credentials.\n * @returns The context handed to `refresh` and `onUnauthorized`.\n */\n function authContext(skipAuth: boolean): RequestAuthContext {\n return {\n token: skipAuth ? null : (config.getToken?.() ?? null),\n epoch: config.getSessionEpoch?.() ?? null,\n };\n }\n\n /**\n * One request, with the `401` refresh-and-replay around it.\n *\n * The refresh path runs only for a target inside the credential scope. A\n * `401` from any other origin answers a request that never carried this\n * session's credential, so it says nothing about the session; until 0.82.0\n * it still drove `refresh()` and `onUnauthorized()`, so a foreign host could\n * spend a refresh and end the user's session (#530).\n *\n * @param path - The path as the call site wrote it.\n * @param options - Request options, policy fields included.\n * @param decode - How to turn the response into a value.\n * @returns The decoded value.\n */\n async function attempt<T>(\n path: string,\n options: RequestOptions,\n decode: ResponseDecoder<T>,\n ): Promise<T> {\n const { skipAuth = false, skipAuthRetry = false, params, ...init } = options;\n const requestId = config.requestId ? config.requestId() : randomId();\n const method = (init.method ?? \"GET\").toUpperCase();\n const url = buildApiUrl(config.baseURL, path, { prefix: config.prefix, params });\n const inScope = isTrustedCredentialTarget(url, config.baseURL, config.trustedOrigins);\n const sent = authContext(skipAuth);\n let response = await send(path, url, inScope, init, requestId, method, skipAuth);\n\n if (response.status === 401 && inScope && !skipAuth && !skipAuthRetry) {\n if (config.refresh) {\n let replayed: RequestAuthContext;\n try {\n await config.refresh(sent);\n replayed = authContext(skipAuth);\n response = await send(path, url, inScope, init, requestId, method, skipAuth);\n } catch (error) {\n if (error instanceof SessionSupersededError) throw error;\n await notifyUnauthorized(response, requestId, sent);\n throw await parseError(response, requestId);\n }\n if (response.status === 401) {\n await notifyUnauthorized(response, requestId, replayed);\n }\n } else {\n await notifyUnauthorized(response, requestId, sent);\n }\n }\n\n if (!response.ok) {\n throw await parseError(response, requestId);\n }\n\n return decode(response);\n }\n\n async function run<T>(\n path: string,\n options: RequestOptions,\n decode: ResponseDecoder<T>,\n ): Promise<T> {\n const { retry: perRequest, ...rest } = options;\n const retryOptions = resolveRetry(perRequest !== undefined ? perRequest : config.retry);\n if (!retryOptions) return attempt<T>(path, rest, decode);\n\n const method = (rest.method ?? \"GET\").toUpperCase();\n return retryWithBackoff(() => attempt<T>(path, rest, decode), {\n ...retryOptions,\n shouldRetry:\n retryOptions.shouldRetry ?? ((error: unknown) => isRetriableFailure(error, method)),\n });\n }\n\n async function request<T>(path: string, options: RequestOptions = {}): Promise<T> {\n return run<T>(path, options, decodeByContentType);\n }\n\n async function upload<T>(\n path: string,\n formData: FormData,\n method: \"POST\" | \"PUT\" | \"PATCH\" = \"POST\",\n options?: Omit<RequestOptions, \"body\" | \"method\">,\n ): Promise<T> {\n return request<T>(path, { ...options, 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 blob: (path: string, options?: RequestOptions) =>\n run<Blob>(path, { ...options }, (response) => response.blob()),\n arrayBuffer: (path: string, options?: RequestOptions) =>\n run<ArrayBuffer>(path, { ...options }, (response) => response.arrayBuffer()),\n upload,\n url: (path: string, params?: RequestOptions[\"params\"]) =>\n buildApiUrl(config.baseURL, path, { prefix: config.prefix, params }),\n };\n}\n"],"mappings":"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"}
@@ -13,22 +13,26 @@ var f = /* @__PURE__ */ new Set([
13
13
  "OPTIONS"
14
14
  ]);
15
15
  function p(e, n) {
16
- return !f.has(n) || !(e instanceof t) ? !1 : r(e.status);
16
+ return !f.has(n) || !(e instanceof t) || e.code === h ? !1 : r(e.status);
17
17
  }
18
18
  function m(e) {
19
19
  return e ? e === !0 ? {} : e : null;
20
20
  }
21
- var h = 15e3, g = 3e5;
22
- function _(e) {
21
+ var h = "REDIRECT_REFUSED";
22
+ function g(e) {
23
+ return e.type === "opaqueredirect" || e.status >= 300 && e.status < 400 && e.status !== 304;
24
+ }
25
+ var _ = 15e3, v = 3e5;
26
+ function y(e) {
23
27
  return typeof FormData < "u" && e instanceof FormData;
24
28
  }
25
- function v(e) {
29
+ function b(e) {
26
30
  return Math.round(performance.now() - e);
27
31
  }
28
- function y(e) {
32
+ function x(e) {
29
33
  return e.replace(/[?#][^]*/, "").replace(/^([^/]*\/\/)[^/]*@/, "$1");
30
34
  }
31
- async function b(e, r) {
35
+ async function S(e, r) {
32
36
  let i;
33
37
  try {
34
38
  i = await e.clone().json();
@@ -41,25 +45,28 @@ async function b(e, r) {
41
45
  }
42
46
  return new t(n(e.status, i, e.headers, r));
43
47
  }
44
- function x(n) {
48
+ function C(n) {
45
49
  let r = n.fetcher ?? globalThis.fetch.bind(globalThis);
46
- function f(e, t) {
47
- if (t) return {};
48
- let r = n.getToken?.();
49
- return r ? o(e, n.baseURL, n.trustedOrigins) ? { Authorization: `Bearer ${r}` } : (s(e, n.baseURL), {}) : {};
50
+ function f(e, t, r) {
51
+ let i = r ? null : n.getToken?.();
52
+ if (!t) {
53
+ i && s(e, n.baseURL);
54
+ let t = Object.keys(n.headers ?? {});
55
+ return t.length > 0 && s(e, n.baseURL, t.join(", ")), {};
56
+ }
57
+ return {
58
+ ...n.headers,
59
+ ...i ? { Authorization: `Bearer ${i}` } : {}
60
+ };
50
61
  }
51
- async function x(e, i, o, s) {
52
- let { body: l, params: u, headers: p, signal: m, timeout: v, ...y } = i, b = _(l), x = b ? n.uploadTimeout : n.timeout, S = v === void 0 ? x === void 0 ? b ? g : h : x : v, C = a(n.baseURL, e, {
53
- prefix: n.prefix,
54
- params: u
55
- }), w = p, T = {
56
- ...b ? {} : { "Content-Type": "application/json" },
62
+ async function C(e, i, a, o, s) {
63
+ let { body: l, headers: u, signal: p, timeout: m, ...b } = a, x = y(l), S = x ? n.uploadTimeout : n.timeout, C = m === void 0 ? S === void 0 ? x ? v : _ : S : m, w = u, T = {
64
+ ...x ? {} : { "Content-Type": "application/json" },
57
65
  ...o ? { "X-Request-ID": o } : {},
58
- ...n.headers,
59
- ...f(C, s),
66
+ ...f(e, i, s),
60
67
  ...c({
61
- method: y.method ?? "GET",
62
- url: C,
68
+ method: b.method ?? "GET",
69
+ url: e,
63
70
  reference: n.baseURL,
64
71
  csrf: n.csrf,
65
72
  trustedOrigins: n.trustedOrigins,
@@ -69,44 +76,52 @@ function x(n) {
69
76
  }
70
77
  }),
71
78
  ...w
72
- }, E = d(m, S), D = {
73
- ...y,
74
- signal: E.signal,
79
+ }, E = i && b.redirect === void 0 && Object.keys(n.headers ?? {}).length > 0, D = d(p, C), O = {
80
+ ...b,
81
+ ...E ? { redirect: "manual" } : {},
82
+ signal: D.signal,
75
83
  headers: T,
76
- credentials: n.withCredentials ? "include" : y.credentials,
77
- body: l == null ? void 0 : b ? l : JSON.stringify(l)
78
- };
84
+ credentials: n.withCredentials ? "include" : b.credentials,
85
+ body: l == null ? void 0 : x ? l : JSON.stringify(l)
86
+ }, k;
79
87
  try {
80
- return await r(C, D);
88
+ k = await r(e, O);
81
89
  } catch (e) {
82
- throw E.timedOut() ? new t({
90
+ throw D.timedOut() ? new t({
83
91
  status: 0,
84
- detail: `A requisição excedeu ${S}ms e foi abandonada.`
92
+ detail: `A requisição excedeu ${C}ms e foi abandonada.`
85
93
  }) : e;
86
94
  } finally {
87
- E.dispose();
95
+ D.dispose();
88
96
  }
97
+ if (E && g(k)) throw new t({
98
+ status: k.status,
99
+ code: h,
100
+ requestId: o,
101
+ detail: "Redirect não seguido: a requisição levava os headers fixos do cliente."
102
+ });
103
+ return k;
89
104
  }
90
- async function S(e, t, r, i, a) {
91
- let o = n.logger;
92
- if (!o) return x(e, t, r, a);
93
- let s = `${i} ${y(e)}`, c = performance.now();
105
+ async function w(e, t, r, i, a, o, s) {
106
+ let c = n.logger;
107
+ if (!c) return C(t, r, i, a, s);
108
+ let l = `${o} ${x(e)}`, u = performance.now();
94
109
  try {
95
- let n = await x(e, t, r, a), i = {
96
- requestId: r,
97
- status: n.status,
98
- ms: v(c)
99
- }, l = `${s} → ${n.status}`;
100
- return n.status >= 400 ? o.warn(l, i) : o.debug(l, i), n;
110
+ let e = await C(t, r, i, a, s), n = {
111
+ requestId: a,
112
+ status: e.status,
113
+ ms: b(u)
114
+ }, o = `${l} → ${e.status}`;
115
+ return e.status >= 400 ? c.warn(o, n) : c.debug(o, n), e;
101
116
  } catch (e) {
102
- throw o.warn(`${s} → no response`, {
103
- requestId: r,
104
- ms: v(c),
117
+ throw c.warn(`${l} → no response`, {
118
+ requestId: a,
119
+ ms: b(u),
105
120
  error: e
106
121
  }), e;
107
122
  }
108
123
  }
109
- async function C(t, r, i) {
124
+ async function T(t, r, i) {
110
125
  if (n.onUnauthorized) {
111
126
  n.logger?.warn("unauthorized — calling onUnauthorized", {
112
127
  requestId: r,
@@ -124,72 +139,75 @@ function x(n) {
124
139
  }
125
140
  }
126
141
  }
127
- function w(e) {
142
+ function E(e) {
128
143
  return {
129
144
  token: e ? null : n.getToken?.() ?? null,
130
145
  epoch: n.getSessionEpoch?.() ?? null
131
146
  };
132
147
  }
133
- async function T(t, r, a) {
134
- let { skipAuth: o = !1, skipAuthRetry: s = !1, ...c } = r, l = n.requestId ? n.requestId() : i(), u = (c.method ?? "GET").toUpperCase(), d = w(o), f = await S(t, c, l, u, o);
135
- if (f.status === 401 && !o && !s) {
148
+ async function D(t, r, s) {
149
+ let { skipAuth: c = !1, skipAuthRetry: l = !1, params: u, ...d } = r, f = n.requestId ? n.requestId() : i(), p = (d.method ?? "GET").toUpperCase(), m = a(n.baseURL, t, {
150
+ prefix: n.prefix,
151
+ params: u
152
+ }), h = o(m, n.baseURL, n.trustedOrigins), g = E(c), _ = await w(t, m, h, d, f, p, c);
153
+ if (_.status === 401 && h && !c && !l) {
136
154
  if (n.refresh) {
137
155
  let r;
138
156
  try {
139
- await n.refresh(d), r = w(o), f = await S(t, c, l, u, o);
157
+ await n.refresh(g), r = E(c), _ = await w(t, m, h, d, f, p, c);
140
158
  } catch (t) {
141
- throw t instanceof e ? t : (await C(f, l, d), await b(f, l));
159
+ throw t instanceof e ? t : (await T(_, f, g), await S(_, f));
142
160
  }
143
- f.status === 401 && await C(f, l, r);
144
- } else await C(f, l, d);
161
+ _.status === 401 && await T(_, f, r);
162
+ } else await T(_, f, g);
145
163
  }
146
- if (!f.ok) throw await b(f, l);
147
- return a(f);
164
+ if (!_.ok) throw await S(_, f);
165
+ return s(_);
148
166
  }
149
- async function E(e, t, r) {
167
+ async function O(e, t, r) {
150
168
  let { retry: i, ...a } = t, o = m(i === void 0 ? n.retry : i);
151
- if (!o) return T(e, a, r);
169
+ if (!o) return D(e, a, r);
152
170
  let s = (a.method ?? "GET").toUpperCase();
153
- return u(() => T(e, a, r), {
171
+ return u(() => D(e, a, r), {
154
172
  ...o,
155
173
  shouldRetry: o.shouldRetry ?? ((e) => p(e, s))
156
174
  });
157
175
  }
158
- async function D(e, t = {}) {
159
- return E(e, t, l);
176
+ async function k(e, t = {}) {
177
+ return O(e, t, l);
160
178
  }
161
- async function O(e, t, n = "POST", r) {
162
- return D(e, {
179
+ async function A(e, t, n = "POST", r) {
180
+ return k(e, {
163
181
  ...r,
164
182
  method: n,
165
183
  body: t
166
184
  });
167
185
  }
168
186
  return {
169
- request: D,
170
- get: (e, t) => D(e, {
187
+ request: k,
188
+ get: (e, t) => k(e, {
171
189
  ...t,
172
190
  method: "GET"
173
191
  }),
174
- post: (e, t) => D(e, {
192
+ post: (e, t) => k(e, {
175
193
  ...t,
176
194
  method: "POST"
177
195
  }),
178
- put: (e, t) => D(e, {
196
+ put: (e, t) => k(e, {
179
197
  ...t,
180
198
  method: "PUT"
181
199
  }),
182
- patch: (e, t) => D(e, {
200
+ patch: (e, t) => k(e, {
183
201
  ...t,
184
202
  method: "PATCH"
185
203
  }),
186
- delete: (e, t) => D(e, {
204
+ delete: (e, t) => k(e, {
187
205
  ...t,
188
206
  method: "DELETE"
189
207
  }),
190
- blob: (e, t) => E(e, { ...t }, (e) => e.blob()),
191
- arrayBuffer: (e, t) => E(e, { ...t }, (e) => e.arrayBuffer()),
192
- upload: O,
208
+ blob: (e, t) => O(e, { ...t }, (e) => e.blob()),
209
+ arrayBuffer: (e, t) => O(e, { ...t }, (e) => e.arrayBuffer()),
210
+ upload: A,
193
211
  url: (e, t) => a(n.baseURL, e, {
194
212
  prefix: n.prefix,
195
213
  params: t
@@ -197,6 +215,6 @@ function x(n) {
197
215
  };
198
216
  }
199
217
  //#endregion
200
- export { x as createApiClient };
218
+ export { C as createApiClient };
201
219
 
202
220
  //# sourceMappingURL=api-client.js.map