tempest-react-sdk 0.33.2 → 0.34.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 (36) hide show
  1. package/README.md +3 -3
  2. package/dist/components/AIChat/AIChat.cjs +2 -0
  3. package/dist/components/AIChat/AIChat.cjs.map +1 -0
  4. package/dist/components/AIChat/AIChat.js +141 -0
  5. package/dist/components/AIChat/AIChat.js.map +1 -0
  6. package/dist/components/AIChat/AIChat.module.cjs +2 -0
  7. package/dist/components/AIChat/AIChat.module.cjs.map +1 -0
  8. package/dist/components/AIChat/AIChat.module.js +58 -0
  9. package/dist/components/AIChat/AIChat.module.js.map +1 -0
  10. package/dist/components/AIChat/AIChatComposer.cjs +2 -0
  11. package/dist/components/AIChat/AIChatComposer.cjs.map +1 -0
  12. package/dist/components/AIChat/AIChatComposer.js +87 -0
  13. package/dist/components/AIChat/AIChatComposer.js.map +1 -0
  14. package/dist/components/AIChat/AIChatTurn.cjs +3 -0
  15. package/dist/components/AIChat/AIChatTurn.cjs.map +1 -0
  16. package/dist/components/AIChat/AIChatTurn.js +217 -0
  17. package/dist/components/AIChat/AIChatTurn.js.map +1 -0
  18. package/dist/components/AIChat/ai-chat-turns.cjs +2 -0
  19. package/dist/components/AIChat/ai-chat-turns.cjs.map +1 -0
  20. package/dist/components/AIChat/ai-chat-turns.js +90 -0
  21. package/dist/components/AIChat/ai-chat-turns.js.map +1 -0
  22. package/dist/styles.css +1 -1
  23. package/dist/tempest-react-sdk.cjs +1 -1
  24. package/dist/tempest-react-sdk.d.ts +415 -9
  25. package/dist/tempest-react-sdk.js +118 -114
  26. package/dist/ws/create-web-socket.cjs +1 -1
  27. package/dist/ws/create-web-socket.cjs.map +1 -1
  28. package/dist/ws/create-web-socket.js +54 -37
  29. package/dist/ws/create-web-socket.js.map +1 -1
  30. package/dist/ws/use-web-socket.cjs +1 -1
  31. package/dist/ws/use-web-socket.cjs.map +1 -1
  32. package/dist/ws/use-web-socket.js +49 -18
  33. package/dist/ws/use-web-socket.js.map +1 -1
  34. package/package.json +1 -1
  35. package/template/src/lib/api.ts +27 -5
  36. package/template/src/stores/auth.ts +14 -0
@@ -175,117 +175,121 @@ import { SignaturePad as Hn } from "./components/SignaturePad/SignaturePad.js";
175
175
  import { chatStrings as Un, dayLabel as Wn, groupMessages as Gn, timeLabel as Kn, typingLabel as qn } from "./components/Chat/chat-groups.js";
176
176
  import { ChatComposer as Jn } from "./components/Chat/ChatComposer.js";
177
177
  import { Chat as Yn } from "./components/Chat/Chat.js";
178
- import { applyMove as Xn, splitSides as Zn } from "./components/Transfer/transfer-state.js";
179
- import { Transfer as Qn } from "./components/Transfer/Transfer.js";
180
- import { Masonry as $n } from "./components/Masonry/Masonry.js";
181
- import { safeLinkUrl as er } from "./components/Markdown/markdown-url.js";
182
- import { parseMarkdown as tr } from "./components/Markdown/markdown-parse.js";
183
- import { Markdown as nr } from "./components/Markdown/Markdown.js";
184
- import { Tour as rr } from "./components/Tour/Tour.js";
185
- import { describeFilter as ir, filtersFromSearchParams as ar, filtersToSearchParams as or, operatorsFor as sr } from "./components/FilterBar/filter-model.js";
186
- import { FilterBar as cr } from "./components/FilterBar/FilterBar.js";
187
- import { formatCPF as lr, formatCurrency as ur, formatDate as dr, formatDateTime as fr, formatPercent as pr, formatPhone as mr } from "./utils/format.js";
188
- import { storage as hr } from "./utils/storage.js";
189
- import { camelCase as gr, capitalize as _r, kebabCase as vr, pluralize as yr, slugify as br, truncate as xr } from "./utils/strings.js";
190
- import { chunk as Sr, groupBy as Cr, range as wr, uniqueBy as Tr } from "./utils/arrays.js";
191
- import { deepMerge as Er, isEmpty as Dr, omit as Or, pick as kr } from "./utils/objects.js";
192
- import { assertNever as Ar, isDefined as jr, isNumber as Mr, isPlainObject as Nr, isString as Pr } from "./utils/guards.js";
193
- import { debounce as Fr, memoizeOne as Ir, once as Lr, throttle as Rr } from "./utils/functions.js";
194
- import { sleep as zr, withTimeout as Br } from "./utils/promises.js";
195
- import { randomId as Vr } from "./utils/ids.js";
196
- import { writeXlsx as Hr } from "./utils/xlsx.js";
197
- import { TempestApiError as Ur, buildApiError as Wr, isApiError as Gr } from "./http/errors.js";
198
- import { createApiClient as Kr } from "./http/api-client.js";
199
- import { parseResponse as qr } from "./http/parse-response.js";
200
- import { uploadWithProgress as Jr } from "./http/upload-with-progress.js";
201
- import { retry as Yr } from "./http/retry.js";
202
- import { generateIdempotencyKey as Xr } from "./http/idempotency.js";
203
- import { usePoll as Zr } from "./http/use-poll.js";
204
- import { createAuthStore as Qr } from "./auth/create-auth-store.js";
205
- import { AuthGuard as $r } from "./auth/AuthGuard.js";
206
- import { decodeJWT as ei, isJWTExpired as ti } from "./auth/jwt.js";
207
- import { lazyWithRetry as ni } from "./auth/lazy-with-retry.js";
208
- import { createRefreshQueue as ri } from "./auth/refresh-queue.js";
209
- import { createTempestAuth as ii } from "./auth/create-tempest-auth.js";
210
- import { AccessControlProvider as ai, useAccessControl as oi } from "./access/access-control-context.js";
211
- import { useCan as si } from "./access/use-can.js";
212
- import { Can as ci } from "./access/Can.js";
213
- import { createRoleAccessControl as li } from "./access/create-role-access-control.js";
214
- import { permissionsFromToken as ui } from "./access/permissions-from-token.js";
215
- import { GoogleSignIn as di } from "./oauth/GoogleSignIn.js";
216
- import { useOAuthCallback as fi } from "./oauth/use-oauth-callback.js";
217
- import { CACHE_TIME as pi, REFETCH_TIME as mi, STALE_TIME as hi } from "./query/constants.js";
218
- import { createQueryKeys as gi } from "./query/create-query-keys.js";
219
- import { QueryProvider as _i } from "./query/QueryProvider.js";
220
- import { emptyOffsetPage as vi, isCursorPage as yi, isOffsetPage as bi } from "./query/pagination.js";
221
- import { usePaginatedQuery as xi } from "./query/use-paginated-query.js";
222
- import { useCursorQuery as Si } from "./query/use-cursor-query.js";
223
- import { useOfflineMutation as Ci } from "./query/use-offline-mutation.js";
224
- import { removeById as wi, upsertById as Ti } from "./query/optimistic.js";
225
- import { createOfflineStore as Ei } from "./offline/create-offline-store.js";
226
- import { persistQueryClientOffline as Di } from "./query/offline-persistence.js";
227
- import { createDataProvider as Oi } from "./data/create-data-provider.js";
228
- import { TempestDataProvider as ki, useDataProvider as Ai } from "./data/data-provider-context.js";
229
- import { listQueryKey as ji, oneQueryKey as Mi, useCreate as Ni, useDelete as Pi, useList as Fi, useOne as Ii, useUpdate as Li } from "./data/use-resource.js";
230
- import { RouteGuard as Ri } from "./router/RouteGuard.js";
231
- import { AppRouter as zi, defineRoutes as Bi } from "./router/AppRouter.js";
232
- import { BrowserRouter as Vi, HashRouter as Hi, Link as Ui, MemoryRouter as Wi, NavLink as Gi, Navigate as Ki, Outlet as qi, Route as Ji, Routes as Yi, redirect as Xi, useLocation as Zi, useMatch as Qi, useNavigate as $i, useParams as ea, useRouteError as ta, useSearchParams as na } from "./router/index.js";
233
- import { createStore as ra } from "./store/create-store.js";
234
- import { createSelectors as ia } from "./store/create-selectors.js";
235
- import { ThemeProvider as aa, useTheme as oa } from "./theme/ThemeProvider.js";
236
- import { createI18n as sa } from "./i18n/create-i18n.js";
237
- import { I18nProvider as ca, useI18n as la, useTranslate as ua } from "./i18n/I18nProvider.js";
238
- import { ErrorBoundary as da } from "./error-boundary/ErrorBoundary.js";
239
- import { AppProviders as fa } from "./app/AppProviders.js";
240
- import { createEventStream as pa } from "./sse/create-event-stream.js";
241
- import { useEventStream as ma } from "./sse/use-event-stream.js";
242
- import { isPushSupported as ha, urlBase64ToUint8Array as ga } from "./push/utils.js";
243
- import { WebPushClient as _a, WebPushPermissionDeniedError as va, WebPushUnsupportedError as ya } from "./push/web-push-client.js";
244
- import { usePushSubscription as ba } from "./push/use-push-subscription.js";
245
- import { installNotificationClickHandler as xa, installPushHandler as Sa, installSkipWaitingListener as Ca } from "./sw/create-push-handler.js";
246
- import { createPartialResponse as wa, installPrecache as Ta, installRuntimeCache as Ea } from "./sw/cache.js";
247
- import { installBackgroundSync as Da } from "./sw/background-sync.js";
248
- import { registerPeriodicSync as Oa } from "./sw/periodic-sync.js";
249
- import { clearCaches as ka, inspectCaches as Aa } from "./sw/cache-inspect.js";
250
- import { createAudioPlayer as ja, playAudio as Ma, stopAudio as Na } from "./audio/audio-player.js";
251
- import { useAudio as Pa } from "./audio/use-audio.js";
252
- import { createOfflineSync as Fa } from "./offline/create-offline-sync.js";
253
- import { higherVersionWins as Ia, lastWriteWins as La } from "./offline/conflict.js";
254
- import { useErrorHandler as Ra } from "./error-boundary/use-error-handler.js";
255
- import { FormField as za } from "./forms/FormField.js";
256
- import { validateForm as Ba } from "./forms/validate-form.js";
257
- import { zodResolver as Va } from "./forms/zod-resolver.js";
258
- import { useZodForm as Ha } from "./forms/use-zod-form.js";
259
- import { formatCEP as Ua, formatCNPJ as Wa, unmask as Ga, validateCNPJ as Ka, validateCPF as qa } from "./forms/br-validators.js";
260
- import { CEPInput as Ja, CNPJInput as Ya, CPFInput as Xa, MoneyInput as Za, PhoneInput as Qa } from "./forms/masked-inputs.js";
261
- import { useViaCEP as $a } from "./forms/use-viacep.js";
262
- import { Controller as eo, FormProvider as to, useFieldArray as no, useForm as ro, useFormContext as io, useFormState as ao, useWatch as oo } from "./forms/index.js";
263
- import { createWebSocket as so } from "./ws/create-web-socket.js";
264
- import { useWebSocket as co } from "./ws/use-web-socket.js";
265
- import { clampLatitude as lo, isCoordinate as uo, isValidLatitude as fo, isValidLongitude as po, normalizeLongitude as mo } from "./geo/types.js";
266
- import { EARTH_RADIUS_KM as ho, bearingDeg as go, haversineKm as _o, pathLengthKm as vo, toRadians as yo } from "./geo/distance.js";
267
- import { DEFAULT_CAR_SPEED_KMH as bo, DEFAULT_CIRCUITY_FACTOR as xo, DEFAULT_MODE_DURATION_FACTORS as So, durationFactor as Co, estimateTravel as wo } from "./geo/estimate.js";
268
- import { boundingBox as To, boundsCenter as Eo, expandBounds as Do } from "./geo/bounds.js";
269
- import { MERCATOR_MAX_LATITUDE as Oo, fitProjection as ko, projectMercator as Ao, unprojectMercator as jo } from "./geo/projection.js";
270
- import { createOSRMBackend as Mo } from "./geo/routing.js";
271
- import { createPositionTracker as No } from "./geo/create-position-tracker.js";
272
- import { usePositionTracker as Po } from "./geo/use-position-tracker.js";
273
- import { TrajectoryMap as Fo } from "./geo/TrajectoryMap.js";
274
- import { THEME_STYLE_ID as Io, applyTheme as Lo, readThemeToken as Ro } from "./theme/apply-theme.js";
275
- import { contrastRatio as zo, createColorScale as Bo, hexToOklch as Vo, hexToRgb as Ho, hexToRgbaString as Uo, oklchToHex as Wo, readableForeground as Go, relativeLuminance as Ko, rgbToHex as qo } from "./theme/color.js";
276
- import { createTheme as Jo, themeContrast as Yo } from "./theme/create-theme.js";
277
- import { getThemePreset as Xo, themePresets as Zo } from "./theme/theme-presets.js";
278
- import { getInitialTheme as Qo, themeInitScript as $o } from "./theme/initial-theme.js";
279
- import { consoleSink as es, createLogger as ts } from "./logger/logger.js";
280
- import { TelemetryProvider as ns, useTelemetry as rs } from "./telemetry/TelemetryProvider.js";
281
- import { consoleTelemetryAdapter as is } from "./telemetry/console-adapter.js";
282
- import { createSentryTelemetryAdapter as as } from "./telemetry/sentry-adapter.js";
283
- import { createPostHogTelemetryAdapter as os } from "./telemetry/posthog-adapter.js";
284
- import { FeatureFlagsProvider as ss, useFeatureFlag as cs, useFlagValue as ls } from "./feature-flags/FeatureFlagsProvider.js";
285
- import { createInMemoryFlags as us } from "./feature-flags/in-memory-adapter.js";
286
- import { createGrowthBookFeatureFlagsAdapter as ds } from "./feature-flags/growthbook-adapter.js";
287
- import { createLaunchDarklyFeatureFlagsAdapter as fs } from "./feature-flags/launchdarkly-adapter.js";
288
- import { isShareSupported as ps, share as ms } from "./share/share.js";
289
- import { shareOrDownloadBlob as hs } from "./share/share-or-download.js";
290
- import { DIVERGING_STEP_COUNT as gs, ORDINAL_START_STEP as _s, SEQUENTIAL_STEP_COUNT as vs, divergingScale as ys, scaleSteps as bs, sequentialScale as xs } from "./charts/scales.js";
291
- export { ai as AccessControlProvider, t as Accordion, n as Alert, r as AppBar, fa as AppProviders, zi as AppRouter, o as AppShell, s as AspectRatio, $r as AuthGuard, c as Avatar, l as AvatarGroup, i as BREAKPOINTS, u as Badge, d as Banner, f as BottomNavigation, p as BottomSheet, m as Breadcrumbs, Vi as BrowserRouter, h as Button, pi as CACHE_TIME, Ja as CEPInput, Ya as CNPJInput, Xa as CPFInput, Dn as Calendar, ci as Can, g as Card, Nn as Carousel, _ as Center, Yn as Chat, Jn as ChatComposer, v as Checkbox, y as ChipInput, un as ClickOutside, nn as CodeBlock, xn as Collapsible, b as Combobox, wn as Command, dn as ConditionalWrapper, S as ConfirmDialog, I as Container, Sn as ContextMenu, eo as Controller, Qt as CopyButton, bo as DEFAULT_CAR_SPEED_KMH, xo as DEFAULT_CIRCUITY_FACTOR, So as DEFAULT_MODE_DURATION_FACTORS, gs as DIVERGING_STEP_COUNT, hn as DataList, Pn as DataTable, w as DatePicker, An as DateRangePicker, gn as DescriptionList, T as Divider, E as Drawer, D as DropdownMenu, O as Dropzone, ho as EARTH_RADIUS_KM, k as EmptyState, da as ErrorBoundary, A as ErrorState, pn as ErrorText, ss as FeatureFlagsProvider, j as FileUpload, cr as FilterBar, In as FloatingActionButton, fn as For, M as Form, N as FormActions, za as FormField, to as FormProvider, P as FormRow, F as FormSection, di as GoogleSignIn, L as Grid, Hi as HashRouter, pt as Hide, Cn as HoverCard, ca as I18nProvider, mn as Image, Rt as ImageCropper, C as Input, B as InstallBanner, V as InstallButton, Je as Kanban, Xe as Kbd, bn as Label, Vn as Lightbox, Ui as Link, Fn as ListTile, Oo as MERCATOR_MAX_LATITUDE, nr as Markdown, $n as Masonry, Wi as MemoryRouter, Mn as Menubar, x as Modal, Ze as ModalsProvider, an as Money, Za as MoneyInput, kn as MultiSelect, et as NProgressBar, Gi as NavLink, $e as Navbar, Ki as Navigate, jn as NavigationMenu, Ln as NavigationRail, Ht as NotificationCenter, _s as ORDINAL_START_STEP, Ue as OfflineIndicator, qi as Outlet, nt as Page, rt as Pagination, it as PasswordInput, Qa as PhoneInput, ot as PinInput, st as Popover, ln as Portal, ct as Progress, qt as QRCapacityError, Zt as QRCode, _i as QueryProvider, mi as REFETCH_TIME, lt as Radio, ut as RadioGroup, dt as RangeSlider, ft as RatingStars, zn as RefreshIndicator, rn as RelativeTime, En as Resizable, Ji as Route, Ri as RouteGuard, Yi as Routes, vs as SEQUENTIAL_STEP_COUNT, hi as STALE_TIME, ht as SafeArea, Bt as Scheduler, Tn as ScrollArea, gt as SearchBar, _t as SegmentedControl, vt as Select, mt as Show, yt as Sidebar, Hn as SignaturePad, bt as Skeleton, On as Slider, xt as Spacer, zt as Sparkline, St as Spinner, R as Stack, Ct as Stat, wt as Stepper, Ot as StepperInput, kt as Switch, Ke as SyncStatusBadge, Io as THEME_STYLE_ID, jt as Table, Mt as Tabs, At as Tag, ns as TelemetryProvider, Ur as TempestApiError, ki as TempestDataProvider, Nt as Textarea, aa as ThemeProvider, Rn as TimePicker, Pt as Timeline, It as ToastProvider, _n as Toggle, vn as ToggleGroup, yn as ToggleGroupItem, Ft as Tooltip, rr as Tour, Fo as TrajectoryMap, Qn as Transfer, on as TreeView, sn as TruncateText, qe as UpdatePrompt, Wt as VirtualList, Kt as VirtualTable, cn as VisuallyHidden, _a as WebPushClient, va as WebPushPermissionDeniedError, ya as WebPushUnsupportedError, Bn as Wizard, Ye as applyKanbanMove, Lo as applyTheme, Xn as applyTransferMove, Ar as assertNever, go as bearingDeg, To as boundingBox, Eo as boundsCenter, Wr as buildApiError, De as buildOpenInChromeIntent, gr as camelCase, _r as capitalize, Un as chatStrings, Sr as chunk, Tt as clamp, lo as clampLatitude, ka as clearCaches, e as cn, Gt as compareValues, es as consoleSink, is as consoleTelemetryAdapter, zo as contrastRatio, Kr as createApiClient, ja as createAudioPlayer, Qr as createAuthStore, Bo as createColorScale, Oi as createDataProvider, pa as createEventStream, ds as createGrowthBookFeatureFlagsAdapter, sa as createI18n, us as createInMemoryFlags, fs as createLaunchDarklyFeatureFlagsAdapter, ts as createLogger, Mo as createOSRMBackend, Ei as createOfflineStore, Fa as createOfflineSync, wa as createPartialResponse, No as createPositionTracker, os as createPostHogTelemetryAdapter, gi as createQueryKeys, ri as createRefreshQueue, li as createRoleAccessControl, ia as createSelectors, as as createSentryTelemetryAdapter, ra as createStore, ii as createTempestAuth, Jo as createTheme, so as createWebSocket, Wn as dayLabel, Fr as debounce, ei as decodeJWT, Er as deepMerge, Bi as defineRoutes, ir as describeFilter, ys as divergingScale, Co as durationFactor, vi as emptyOffsetPage, Jt as encodeQR, at as estimatePasswordStrength, Le as estimateStorage, wo as estimateTravel, Do as expandBounds, ar as filtersFromSearchParams, or as filtersToSearchParams, ko as fitProjection, Et as formatBytes, Ua as formatCEP, Wa as formatCNPJ, lr as formatCPF, Dt as formatCompactNumber, ur as formatCurrency, dr as formatDate, fr as formatDateTime, pr as formatPercent, mr as formatPhone, Xr as generateIdempotencyKey, Qo as getInitialTheme, Xo as getThemePreset, Cr as groupBy, Gn as groupMessages, _o as haversineKm, Vo as hexToOklch, Ho as hexToRgb, Uo as hexToRgbaString, Ia as higherVersionWins, Aa as inspectCaches, Da as installBackgroundSync, xa as installNotificationClickHandler, Ta as installPrecache, Sa as installPushHandler, Ea as installRuntimeCache, Ca as installSkipWaitingListener, Oe as isAndroid, ke as isAndroidWithoutPromptApi, Gr as isApiError, uo as isCoordinate, yi as isCursorPage, jr as isDefined, Dr as isEmpty, Ae as isIOS, ti as isJWTExpired, Mr as isNumber, bi as isOffsetPage, Nr as isPlainObject, ha as isPushSupported, ps as isShareSupported, je as isStandalone, Pr as isString, fo as isValidLatitude, po as isValidLongitude, vr as kebabCase, La as lastWriteWins, ni as lazyWithRetry, ji as listQueryKey, Yt as matrixToPath, Ir as memoizeOne, Be as moveItem, mo as normalizeLongitude, tt as nprogress, Wo as oklchToHex, Or as omit, Lr as once, Mi as oneQueryKey, sr as operatorsFor, tr as parseMarkdown, qr as parseResponse, vo as pathLengthKm, ui as permissionsFromToken, Di as persistQueryClientOffline, kr as pick, Ma as playAudio, yr as pluralize, Ao as projectMercator, Vr as randomId, wr as range, Ro as readThemeToken, Go as readableForeground, Xi as redirect, Oa as registerPeriodicSync, Ne as registerServiceWorker, Ko as relativeLuminance, Vt as relativeTime, wi as removeById, Re as requestPersistentStorage, $t as resolveLanguage, Yr as retry, qo as rgbToHex, er as safeLinkUrl, bs as scaleSteps, Xt as selectMode, xs as sequentialScale, ms as share, hs as shareOrDownloadBlob, Pe as skipWaiting, zr as sleep, br as slugify, Zn as splitTransferSides, Na as stopAudio, hr as storage, Yo as themeContrast, $o as themeInitScript, Zo as themePresets, Rr as throttle, Kn as timeLabel, yo as toRadians, en as tokenize, tn as tokenizeLines, xr as truncate, qn as typingLabel, Tr as uniqueBy, Ga as unmask, jo as unprojectMercator, Fe as unregisterAllServiceWorkers, Jr as uploadWithProgress, Ti as upsertById, ga as urlBase64ToUint8Array, oi as useAccessControl, Y as useAsync, Pa as useAudio, z as useBeforeInstallPrompt, a as useBreakpoint, si as useCan, Ce as useClickOutside, W as useClientFilter, ee as useClipboard, _e as useCounter, Ni as useCreate, Si as useCursorQuery, Ai as useDataProvider, H as useDebounce, se as useDeepMemo, Pi as useDelete, he as useDisclosure, ve as useDocumentTitle, Z as useDocumentVisibility, Ra as useErrorHandler, K as useEventListener, ma as useEventStream, ye as useFavicon, cs as useFeatureFlag, no as useFieldArray, ls as useFlagValue, ae as useFocusTrap, ro as useForm, io as useFormContext, ao as useFormState, re as useGeolocation, pe as useHover, la as useI18n, ne as useIdle, Me as useInstallPrompt, Q as useIntersectionObserver, le as useInterval, we as useIsFirstRender, te as useKeyboardShortcut, Fi as useList, ge as useListState, q as useLocalStorage, Zi as useLocation, me as useLongPress, Ee as useLongPressHandlers, be as useMap, Qi as useMatch, G as useMediaQuery, Qe as useModals, $i as useNavigate, Ut as useNotificationInbox, fi as useOAuthCallback, Te as useObjectUrl, Ci as useOfflineMutation, We as useOfflineSync, Ii as useOne, X as useOnline, xi as usePaginatedQuery, U as usePagination, ea as useParams, Zr as usePoll, Po as usePositionTracker, ce as usePrevious, ba as usePushSubscription, Se as useQueue, $ as useResizeObserver, ta as useRouteError, ie as useScrollLock, He as useScrollOverflow, na as useSearchParams, Ie as useServiceWorkerUpdate, xe as useSet, Ve as useSortable, oe as useStableCallback, ze as useStorageEstimate, Ge as useSyncStatus, rs as useTelemetry, oa as useTheme, de as useThrottle, ue as useTimeout, Lt as useToast, J as useToggle, ua as useTranslate, Li as useUpdate, $a as useViaCEP, oo as useWatch, co as useWebSocket, fe as useWindowSize, Ha as useZodForm, Ka as validateCNPJ, qa as validateCPF, Ba as validateForm, Br as withTimeout, Hr as writeXlsx, Va as zodResolver };
178
+ import { aiChatStrings as Xn, isGenerating as Zn, lastAssistantId as Qn, roleLabel as $n, tailSignature as er, turnTime as tr, visibleTurns as nr } from "./components/AIChat/ai-chat-turns.js";
179
+ import { AIChatComposer as rr } from "./components/AIChat/AIChatComposer.js";
180
+ import { safeLinkUrl as ir } from "./components/Markdown/markdown-url.js";
181
+ import { parseMarkdown as ar } from "./components/Markdown/markdown-parse.js";
182
+ import { Markdown as or } from "./components/Markdown/Markdown.js";
183
+ import { AIChatTurn as sr } from "./components/AIChat/AIChatTurn.js";
184
+ import { AIChat as cr } from "./components/AIChat/AIChat.js";
185
+ import { applyMove as lr, splitSides as ur } from "./components/Transfer/transfer-state.js";
186
+ import { Transfer as dr } from "./components/Transfer/Transfer.js";
187
+ import { Masonry as fr } from "./components/Masonry/Masonry.js";
188
+ import { Tour as pr } from "./components/Tour/Tour.js";
189
+ import { describeFilter as mr, filtersFromSearchParams as hr, filtersToSearchParams as gr, operatorsFor as _r } from "./components/FilterBar/filter-model.js";
190
+ import { FilterBar as vr } from "./components/FilterBar/FilterBar.js";
191
+ import { formatCPF as yr, formatCurrency as br, formatDate as xr, formatDateTime as Sr, formatPercent as Cr, formatPhone as wr } from "./utils/format.js";
192
+ import { storage as Tr } from "./utils/storage.js";
193
+ import { camelCase as Er, capitalize as Dr, kebabCase as Or, pluralize as kr, slugify as Ar, truncate as jr } from "./utils/strings.js";
194
+ import { chunk as Mr, groupBy as Nr, range as Pr, uniqueBy as Fr } from "./utils/arrays.js";
195
+ import { deepMerge as Ir, isEmpty as Lr, omit as Rr, pick as zr } from "./utils/objects.js";
196
+ import { assertNever as Br, isDefined as Vr, isNumber as Hr, isPlainObject as Ur, isString as Wr } from "./utils/guards.js";
197
+ import { debounce as Gr, memoizeOne as Kr, once as qr, throttle as Jr } from "./utils/functions.js";
198
+ import { sleep as Yr, withTimeout as Xr } from "./utils/promises.js";
199
+ import { randomId as Zr } from "./utils/ids.js";
200
+ import { writeXlsx as Qr } from "./utils/xlsx.js";
201
+ import { TempestApiError as $r, buildApiError as ei, isApiError as ti } from "./http/errors.js";
202
+ import { createApiClient as ni } from "./http/api-client.js";
203
+ import { parseResponse as ri } from "./http/parse-response.js";
204
+ import { uploadWithProgress as ii } from "./http/upload-with-progress.js";
205
+ import { retry as ai } from "./http/retry.js";
206
+ import { generateIdempotencyKey as oi } from "./http/idempotency.js";
207
+ import { usePoll as si } from "./http/use-poll.js";
208
+ import { createAuthStore as ci } from "./auth/create-auth-store.js";
209
+ import { AuthGuard as li } from "./auth/AuthGuard.js";
210
+ import { decodeJWT as ui, isJWTExpired as di } from "./auth/jwt.js";
211
+ import { lazyWithRetry as fi } from "./auth/lazy-with-retry.js";
212
+ import { createRefreshQueue as pi } from "./auth/refresh-queue.js";
213
+ import { createTempestAuth as mi } from "./auth/create-tempest-auth.js";
214
+ import { AccessControlProvider as hi, useAccessControl as gi } from "./access/access-control-context.js";
215
+ import { useCan as _i } from "./access/use-can.js";
216
+ import { Can as vi } from "./access/Can.js";
217
+ import { createRoleAccessControl as yi } from "./access/create-role-access-control.js";
218
+ import { permissionsFromToken as bi } from "./access/permissions-from-token.js";
219
+ import { GoogleSignIn as xi } from "./oauth/GoogleSignIn.js";
220
+ import { useOAuthCallback as Si } from "./oauth/use-oauth-callback.js";
221
+ import { CACHE_TIME as Ci, REFETCH_TIME as wi, STALE_TIME as Ti } from "./query/constants.js";
222
+ import { createQueryKeys as Ei } from "./query/create-query-keys.js";
223
+ import { QueryProvider as Di } from "./query/QueryProvider.js";
224
+ import { emptyOffsetPage as Oi, isCursorPage as ki, isOffsetPage as Ai } from "./query/pagination.js";
225
+ import { usePaginatedQuery as ji } from "./query/use-paginated-query.js";
226
+ import { useCursorQuery as Mi } from "./query/use-cursor-query.js";
227
+ import { useOfflineMutation as Ni } from "./query/use-offline-mutation.js";
228
+ import { removeById as Pi, upsertById as Fi } from "./query/optimistic.js";
229
+ import { createOfflineStore as Ii } from "./offline/create-offline-store.js";
230
+ import { persistQueryClientOffline as Li } from "./query/offline-persistence.js";
231
+ import { createDataProvider as Ri } from "./data/create-data-provider.js";
232
+ import { TempestDataProvider as zi, useDataProvider as Bi } from "./data/data-provider-context.js";
233
+ import { listQueryKey as Vi, oneQueryKey as Hi, useCreate as Ui, useDelete as Wi, useList as Gi, useOne as Ki, useUpdate as qi } from "./data/use-resource.js";
234
+ import { RouteGuard as Ji } from "./router/RouteGuard.js";
235
+ import { AppRouter as Yi, defineRoutes as Xi } from "./router/AppRouter.js";
236
+ import { BrowserRouter as Zi, HashRouter as Qi, Link as $i, MemoryRouter as ea, NavLink as ta, Navigate as na, Outlet as ra, Route as ia, Routes as aa, redirect as oa, useLocation as sa, useMatch as ca, useNavigate as la, useParams as ua, useRouteError as da, useSearchParams as fa } from "./router/index.js";
237
+ import { createStore as pa } from "./store/create-store.js";
238
+ import { createSelectors as ma } from "./store/create-selectors.js";
239
+ import { ThemeProvider as ha, useTheme as ga } from "./theme/ThemeProvider.js";
240
+ import { createI18n as _a } from "./i18n/create-i18n.js";
241
+ import { I18nProvider as va, useI18n as ya, useTranslate as ba } from "./i18n/I18nProvider.js";
242
+ import { ErrorBoundary as xa } from "./error-boundary/ErrorBoundary.js";
243
+ import { AppProviders as Sa } from "./app/AppProviders.js";
244
+ import { createEventStream as Ca } from "./sse/create-event-stream.js";
245
+ import { useEventStream as wa } from "./sse/use-event-stream.js";
246
+ import { isPushSupported as Ta, urlBase64ToUint8Array as Ea } from "./push/utils.js";
247
+ import { WebPushClient as Da, WebPushPermissionDeniedError as Oa, WebPushUnsupportedError as ka } from "./push/web-push-client.js";
248
+ import { usePushSubscription as Aa } from "./push/use-push-subscription.js";
249
+ import { installNotificationClickHandler as ja, installPushHandler as Ma, installSkipWaitingListener as Na } from "./sw/create-push-handler.js";
250
+ import { createPartialResponse as Pa, installPrecache as Fa, installRuntimeCache as Ia } from "./sw/cache.js";
251
+ import { installBackgroundSync as La } from "./sw/background-sync.js";
252
+ import { registerPeriodicSync as Ra } from "./sw/periodic-sync.js";
253
+ import { clearCaches as za, inspectCaches as Ba } from "./sw/cache-inspect.js";
254
+ import { createAudioPlayer as Va, playAudio as Ha, stopAudio as Ua } from "./audio/audio-player.js";
255
+ import { useAudio as Wa } from "./audio/use-audio.js";
256
+ import { createOfflineSync as Ga } from "./offline/create-offline-sync.js";
257
+ import { higherVersionWins as Ka, lastWriteWins as qa } from "./offline/conflict.js";
258
+ import { useErrorHandler as Ja } from "./error-boundary/use-error-handler.js";
259
+ import { FormField as Ya } from "./forms/FormField.js";
260
+ import { validateForm as Xa } from "./forms/validate-form.js";
261
+ import { zodResolver as Za } from "./forms/zod-resolver.js";
262
+ import { useZodForm as Qa } from "./forms/use-zod-form.js";
263
+ import { formatCEP as $a, formatCNPJ as eo, unmask as to, validateCNPJ as no, validateCPF as ro } from "./forms/br-validators.js";
264
+ import { CEPInput as io, CNPJInput as ao, CPFInput as oo, MoneyInput as so, PhoneInput as co } from "./forms/masked-inputs.js";
265
+ import { useViaCEP as lo } from "./forms/use-viacep.js";
266
+ import { Controller as uo, FormProvider as fo, useFieldArray as po, useForm as mo, useFormContext as ho, useFormState as go, useWatch as _o } from "./forms/index.js";
267
+ import { createWebSocket as vo } from "./ws/create-web-socket.js";
268
+ import { useWebSocket as yo } from "./ws/use-web-socket.js";
269
+ import { clampLatitude as bo, isCoordinate as xo, isValidLatitude as So, isValidLongitude as Co, normalizeLongitude as wo } from "./geo/types.js";
270
+ import { EARTH_RADIUS_KM as To, bearingDeg as Eo, haversineKm as Do, pathLengthKm as Oo, toRadians as ko } from "./geo/distance.js";
271
+ import { DEFAULT_CAR_SPEED_KMH as Ao, DEFAULT_CIRCUITY_FACTOR as jo, DEFAULT_MODE_DURATION_FACTORS as Mo, durationFactor as No, estimateTravel as Po } from "./geo/estimate.js";
272
+ import { boundingBox as Fo, boundsCenter as Io, expandBounds as Lo } from "./geo/bounds.js";
273
+ import { MERCATOR_MAX_LATITUDE as Ro, fitProjection as zo, projectMercator as Bo, unprojectMercator as Vo } from "./geo/projection.js";
274
+ import { createOSRMBackend as Ho } from "./geo/routing.js";
275
+ import { createPositionTracker as Uo } from "./geo/create-position-tracker.js";
276
+ import { usePositionTracker as Wo } from "./geo/use-position-tracker.js";
277
+ import { TrajectoryMap as Go } from "./geo/TrajectoryMap.js";
278
+ import { THEME_STYLE_ID as Ko, applyTheme as qo, readThemeToken as Jo } from "./theme/apply-theme.js";
279
+ import { contrastRatio as Yo, createColorScale as Xo, hexToOklch as Zo, hexToRgb as Qo, hexToRgbaString as $o, oklchToHex as es, readableForeground as ts, relativeLuminance as ns, rgbToHex as rs } from "./theme/color.js";
280
+ import { createTheme as is, themeContrast as as } from "./theme/create-theme.js";
281
+ import { getThemePreset as os, themePresets as ss } from "./theme/theme-presets.js";
282
+ import { getInitialTheme as cs, themeInitScript as ls } from "./theme/initial-theme.js";
283
+ import { consoleSink as us, createLogger as ds } from "./logger/logger.js";
284
+ import { TelemetryProvider as fs, useTelemetry as ps } from "./telemetry/TelemetryProvider.js";
285
+ import { consoleTelemetryAdapter as ms } from "./telemetry/console-adapter.js";
286
+ import { createSentryTelemetryAdapter as hs } from "./telemetry/sentry-adapter.js";
287
+ import { createPostHogTelemetryAdapter as gs } from "./telemetry/posthog-adapter.js";
288
+ import { FeatureFlagsProvider as _s, useFeatureFlag as vs, useFlagValue as ys } from "./feature-flags/FeatureFlagsProvider.js";
289
+ import { createInMemoryFlags as bs } from "./feature-flags/in-memory-adapter.js";
290
+ import { createGrowthBookFeatureFlagsAdapter as xs } from "./feature-flags/growthbook-adapter.js";
291
+ import { createLaunchDarklyFeatureFlagsAdapter as Ss } from "./feature-flags/launchdarkly-adapter.js";
292
+ import { isShareSupported as Cs, share as ws } from "./share/share.js";
293
+ import { shareOrDownloadBlob as Ts } from "./share/share-or-download.js";
294
+ import { DIVERGING_STEP_COUNT as Es, ORDINAL_START_STEP as Ds, SEQUENTIAL_STEP_COUNT as Os, divergingScale as ks, scaleSteps as As, sequentialScale as js } from "./charts/scales.js";
295
+ export { cr as AIChat, rr as AIChatComposer, sr as AIChatTurn, hi as AccessControlProvider, t as Accordion, n as Alert, r as AppBar, Sa as AppProviders, Yi as AppRouter, o as AppShell, s as AspectRatio, li as AuthGuard, c as Avatar, l as AvatarGroup, i as BREAKPOINTS, u as Badge, d as Banner, f as BottomNavigation, p as BottomSheet, m as Breadcrumbs, Zi as BrowserRouter, h as Button, Ci as CACHE_TIME, io as CEPInput, ao as CNPJInput, oo as CPFInput, Dn as Calendar, vi as Can, g as Card, Nn as Carousel, _ as Center, Yn as Chat, Jn as ChatComposer, v as Checkbox, y as ChipInput, un as ClickOutside, nn as CodeBlock, xn as Collapsible, b as Combobox, wn as Command, dn as ConditionalWrapper, S as ConfirmDialog, I as Container, Sn as ContextMenu, uo as Controller, Qt as CopyButton, Ao as DEFAULT_CAR_SPEED_KMH, jo as DEFAULT_CIRCUITY_FACTOR, Mo as DEFAULT_MODE_DURATION_FACTORS, Es as DIVERGING_STEP_COUNT, hn as DataList, Pn as DataTable, w as DatePicker, An as DateRangePicker, gn as DescriptionList, T as Divider, E as Drawer, D as DropdownMenu, O as Dropzone, To as EARTH_RADIUS_KM, k as EmptyState, xa as ErrorBoundary, A as ErrorState, pn as ErrorText, _s as FeatureFlagsProvider, j as FileUpload, vr as FilterBar, In as FloatingActionButton, fn as For, M as Form, N as FormActions, Ya as FormField, fo as FormProvider, P as FormRow, F as FormSection, xi as GoogleSignIn, L as Grid, Qi as HashRouter, pt as Hide, Cn as HoverCard, va as I18nProvider, mn as Image, Rt as ImageCropper, C as Input, B as InstallBanner, V as InstallButton, Je as Kanban, Xe as Kbd, bn as Label, Vn as Lightbox, $i as Link, Fn as ListTile, Ro as MERCATOR_MAX_LATITUDE, or as Markdown, fr as Masonry, ea as MemoryRouter, Mn as Menubar, x as Modal, Ze as ModalsProvider, an as Money, so as MoneyInput, kn as MultiSelect, et as NProgressBar, ta as NavLink, $e as Navbar, na as Navigate, jn as NavigationMenu, Ln as NavigationRail, Ht as NotificationCenter, Ds as ORDINAL_START_STEP, Ue as OfflineIndicator, ra as Outlet, nt as Page, rt as Pagination, it as PasswordInput, co as PhoneInput, ot as PinInput, st as Popover, ln as Portal, ct as Progress, qt as QRCapacityError, Zt as QRCode, Di as QueryProvider, wi as REFETCH_TIME, lt as Radio, ut as RadioGroup, dt as RangeSlider, ft as RatingStars, zn as RefreshIndicator, rn as RelativeTime, En as Resizable, ia as Route, Ji as RouteGuard, aa as Routes, Os as SEQUENTIAL_STEP_COUNT, Ti as STALE_TIME, ht as SafeArea, Bt as Scheduler, Tn as ScrollArea, gt as SearchBar, _t as SegmentedControl, vt as Select, mt as Show, yt as Sidebar, Hn as SignaturePad, bt as Skeleton, On as Slider, xt as Spacer, zt as Sparkline, St as Spinner, R as Stack, Ct as Stat, wt as Stepper, Ot as StepperInput, kt as Switch, Ke as SyncStatusBadge, Ko as THEME_STYLE_ID, jt as Table, Mt as Tabs, At as Tag, fs as TelemetryProvider, $r as TempestApiError, zi as TempestDataProvider, Nt as Textarea, ha as ThemeProvider, Rn as TimePicker, Pt as Timeline, It as ToastProvider, _n as Toggle, vn as ToggleGroup, yn as ToggleGroupItem, Ft as Tooltip, pr as Tour, Go as TrajectoryMap, dr as Transfer, on as TreeView, sn as TruncateText, qe as UpdatePrompt, Wt as VirtualList, Kt as VirtualTable, cn as VisuallyHidden, Da as WebPushClient, Oa as WebPushPermissionDeniedError, ka as WebPushUnsupportedError, Bn as Wizard, Xn as aiChatStrings, Ye as applyKanbanMove, qo as applyTheme, lr as applyTransferMove, Br as assertNever, Eo as bearingDeg, Fo as boundingBox, Io as boundsCenter, ei as buildApiError, De as buildOpenInChromeIntent, Er as camelCase, Dr as capitalize, Un as chatStrings, Mr as chunk, Tt as clamp, bo as clampLatitude, za as clearCaches, e as cn, Gt as compareValues, us as consoleSink, ms as consoleTelemetryAdapter, Yo as contrastRatio, ni as createApiClient, Va as createAudioPlayer, ci as createAuthStore, Xo as createColorScale, Ri as createDataProvider, Ca as createEventStream, xs as createGrowthBookFeatureFlagsAdapter, _a as createI18n, bs as createInMemoryFlags, Ss as createLaunchDarklyFeatureFlagsAdapter, ds as createLogger, Ho as createOSRMBackend, Ii as createOfflineStore, Ga as createOfflineSync, Pa as createPartialResponse, Uo as createPositionTracker, gs as createPostHogTelemetryAdapter, Ei as createQueryKeys, pi as createRefreshQueue, yi as createRoleAccessControl, ma as createSelectors, hs as createSentryTelemetryAdapter, pa as createStore, mi as createTempestAuth, is as createTheme, vo as createWebSocket, Wn as dayLabel, Gr as debounce, ui as decodeJWT, Ir as deepMerge, Xi as defineRoutes, mr as describeFilter, ks as divergingScale, No as durationFactor, Oi as emptyOffsetPage, Jt as encodeQR, at as estimatePasswordStrength, Le as estimateStorage, Po as estimateTravel, Lo as expandBounds, hr as filtersFromSearchParams, gr as filtersToSearchParams, zo as fitProjection, Et as formatBytes, $a as formatCEP, eo as formatCNPJ, yr as formatCPF, Dt as formatCompactNumber, br as formatCurrency, xr as formatDate, Sr as formatDateTime, Cr as formatPercent, wr as formatPhone, oi as generateIdempotencyKey, cs as getInitialTheme, os as getThemePreset, Nr as groupBy, Gn as groupMessages, Do as haversineKm, Zo as hexToOklch, Qo as hexToRgb, $o as hexToRgbaString, Ka as higherVersionWins, Ba as inspectCaches, La as installBackgroundSync, ja as installNotificationClickHandler, Fa as installPrecache, Ma as installPushHandler, Ia as installRuntimeCache, Na as installSkipWaitingListener, Oe as isAndroid, ke as isAndroidWithoutPromptApi, ti as isApiError, xo as isCoordinate, ki as isCursorPage, Vr as isDefined, Lr as isEmpty, Zn as isGenerating, Ae as isIOS, di as isJWTExpired, Hr as isNumber, Ai as isOffsetPage, Ur as isPlainObject, Ta as isPushSupported, Cs as isShareSupported, je as isStandalone, Wr as isString, So as isValidLatitude, Co as isValidLongitude, Or as kebabCase, Qn as lastAssistantId, qa as lastWriteWins, fi as lazyWithRetry, Vi as listQueryKey, Yt as matrixToPath, Kr as memoizeOne, Be as moveItem, wo as normalizeLongitude, tt as nprogress, es as oklchToHex, Rr as omit, qr as once, Hi as oneQueryKey, _r as operatorsFor, ar as parseMarkdown, ri as parseResponse, Oo as pathLengthKm, bi as permissionsFromToken, Li as persistQueryClientOffline, zr as pick, Ha as playAudio, kr as pluralize, Bo as projectMercator, Zr as randomId, Pr as range, Jo as readThemeToken, ts as readableForeground, oa as redirect, Ra as registerPeriodicSync, Ne as registerServiceWorker, ns as relativeLuminance, Vt as relativeTime, Pi as removeById, Re as requestPersistentStorage, $t as resolveLanguage, ai as retry, rs as rgbToHex, $n as roleLabel, ir as safeLinkUrl, As as scaleSteps, Xt as selectMode, js as sequentialScale, ws as share, Ts as shareOrDownloadBlob, Pe as skipWaiting, Yr as sleep, Ar as slugify, ur as splitTransferSides, Ua as stopAudio, Tr as storage, er as tailSignature, as as themeContrast, ls as themeInitScript, ss as themePresets, Jr as throttle, Kn as timeLabel, ko as toRadians, en as tokenize, tn as tokenizeLines, jr as truncate, tr as turnTime, qn as typingLabel, Fr as uniqueBy, to as unmask, Vo as unprojectMercator, Fe as unregisterAllServiceWorkers, ii as uploadWithProgress, Fi as upsertById, Ea as urlBase64ToUint8Array, gi as useAccessControl, Y as useAsync, Wa as useAudio, z as useBeforeInstallPrompt, a as useBreakpoint, _i as useCan, Ce as useClickOutside, W as useClientFilter, ee as useClipboard, _e as useCounter, Ui as useCreate, Mi as useCursorQuery, Bi as useDataProvider, H as useDebounce, se as useDeepMemo, Wi as useDelete, he as useDisclosure, ve as useDocumentTitle, Z as useDocumentVisibility, Ja as useErrorHandler, K as useEventListener, wa as useEventStream, ye as useFavicon, vs as useFeatureFlag, po as useFieldArray, ys as useFlagValue, ae as useFocusTrap, mo as useForm, ho as useFormContext, go as useFormState, re as useGeolocation, pe as useHover, ya as useI18n, ne as useIdle, Me as useInstallPrompt, Q as useIntersectionObserver, le as useInterval, we as useIsFirstRender, te as useKeyboardShortcut, Gi as useList, ge as useListState, q as useLocalStorage, sa as useLocation, me as useLongPress, Ee as useLongPressHandlers, be as useMap, ca as useMatch, G as useMediaQuery, Qe as useModals, la as useNavigate, Ut as useNotificationInbox, Si as useOAuthCallback, Te as useObjectUrl, Ni as useOfflineMutation, We as useOfflineSync, Ki as useOne, X as useOnline, ji as usePaginatedQuery, U as usePagination, ua as useParams, si as usePoll, Wo as usePositionTracker, ce as usePrevious, Aa as usePushSubscription, Se as useQueue, $ as useResizeObserver, da as useRouteError, ie as useScrollLock, He as useScrollOverflow, fa as useSearchParams, Ie as useServiceWorkerUpdate, xe as useSet, Ve as useSortable, oe as useStableCallback, ze as useStorageEstimate, Ge as useSyncStatus, ps as useTelemetry, ga as useTheme, de as useThrottle, ue as useTimeout, Lt as useToast, J as useToggle, ba as useTranslate, qi as useUpdate, lo as useViaCEP, _o as useWatch, yo as useWebSocket, fe as useWindowSize, Qa as useZodForm, no as validateCNPJ, ro as validateCPF, Xa as validateForm, nr as visibleTurns, Xr as withTimeout, Qr as writeXlsx, Za as zodResolver };
@@ -1,2 +1,2 @@
1
- function e(e){try{return JSON.parse(e)}catch{return e}}function t(t,n={}){let{protocols:r,maxRetries:i=10,initialBackoff:a=1e3,maxBackoff:o=3e4,pingInterval:s=0,pingPayload:c=JSON.stringify({type:`ping`}),parser:l=e,onOpen:u,onMessage:d,onClose:f,onError:p,onStatusChange:m}=n,h=null,g=null,_=null,v=0,y=`idle`,b=!1;function x(e){y!==e&&(y=e,m?.(e))}function S(){_&&=(clearInterval(_),null)}function C(){!s||s<=0||(S(),_=setInterval(()=>{h?.readyState===WebSocket.OPEN&&h.send(c)},s))}function w(){if(b)return;if(v>=i){x(`error`);return}let e=Math.min(a*2**v,o);v+=1,g=setTimeout(T,e)}function T(){if(b)return;h&&(h.onopen=null,h.onmessage=null,h.onclose=null,h.onerror=null,h.close()),x(`connecting`);let e=new WebSocket(t,r);h=e,e.onopen=e=>{v=0,x(`open`),C(),u?.(e)},e.onmessage=e=>{let t=typeof e.data==`string`?e.data:``;d?.({data:l(t),raw:e})},e.onerror=e=>{p?.(e)},e.onclose=e=>{S(),f?.(e),h=null,x(`closed`),!b&&!e.wasClean&&w()}}function E(e){return h?.readyState===WebSocket.OPEN?(h.send(e),!0):!1}function D(e,t){b=!0,g&&=(clearTimeout(g),null),S(),v=0,h&&=(x(`closing`),h.close(e,t),null),x(`closed`)}function O(){g&&=(clearTimeout(g),null),v=0,b=!1,T()}return T(),{send:E,close:D,reconnect:O,get status(){return y}}}exports.createWebSocket=t;
1
+ function e(e){try{return JSON.parse(e)}catch{return e}}function t(t,n={}){let{protocols:r,maxRetries:i=10,initialBackoff:a=1e3,maxBackoff:o=3e4,pingInterval:s=0,pingPayload:c=JSON.stringify({type:`ping`}),respondToPing:l=!0,pongPayload:u=JSON.stringify({type:`pong`}),queueWhileClosed:d=!1,maxQueuedMessages:f=100,parser:p=e,onOpen:m,onMessage:h,onClose:g,onError:_,onStatusChange:v}=n,y=null,b=null,x=null,S=0,C=`idle`,w=!1,T=[];function E(e){return typeof e==`object`&&!!e&&e.type===`ping`}function D(e){for(;T.length>0&&e.readyState===WebSocket.OPEN;)e.send(T.shift())}function O(e){C!==e&&(C=e,v?.(e))}function k(){x&&=(clearInterval(x),null)}function A(){!s||s<=0||(k(),x=setInterval(()=>{y?.readyState===WebSocket.OPEN&&y.send(c)},s))}function j(){if(w)return;if(S>=i){O(`error`);return}let e=Math.min(a*2**S,o);S+=1,b=setTimeout(M,e)}function M(){if(w)return;if(y){let e=y;e.onmessage=null,e.onclose=null,e.onerror=null,e.readyState!==WebSocket.CONNECTING&&(e.onopen=null),F(e)}O(`connecting`);let e=new WebSocket(t,r);y=e,e.onopen=t=>{S=0,O(`open`),A(),D(e),m?.(t)},e.onmessage=t=>{let n=typeof t.data==`string`?t.data:``,r=p(n);l&&E(r)&&e.readyState===WebSocket.OPEN&&e.send(u),h?.({data:r,raw:t})},e.onerror=e=>{_?.(e)},e.onclose=e=>{k(),g?.(e),y=null,O(`closed`),!w&&!e.wasClean&&j()}}function N(e){return y?.readyState===WebSocket.OPEN?(y.send(e),!0):!d||w?!1:(T.length>=f&&T.shift(),T.push(e),!0)}function P(e,t){w=!0,b&&=(clearTimeout(b),null),k(),S=0,T.length=0,y&&=(O(`closing`),F(y,e,t),null),O(`closed`)}function F(e,t,n){if(e.readyState===WebSocket.CONNECTING){e.onopen=()=>e.close(t,n),e.onmessage=null,e.onerror=null,e.onclose=null;return}e.close(t,n)}function I(){b&&=(clearTimeout(b),null),S=0,w=!1,M()}return M(),{send:N,close:P,reconnect:I,get status(){return C}}}exports.createWebSocket=t;
2
2
  //# sourceMappingURL=create-web-socket.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"create-web-socket.cjs","names":[],"sources":["../../src/ws/create-web-socket.ts"],"sourcesContent":["export type WebSocketStatus = \"idle\" | \"connecting\" | \"open\" | \"closing\" | \"closed\" | \"error\";\n\nexport interface WebSocketMessage<T> {\n /** Parsed payload — JSON-decoded when possible, raw string otherwise. */\n data: T;\n /** The original `MessageEvent`. */\n raw: MessageEvent;\n}\n\nexport interface CreateWebSocketOptions<T> {\n /** Subprotocol(s) forwarded to the `WebSocket` constructor. */\n protocols?: string | string[];\n /** Max reconnect attempts. Default: 10. Pass 0 to disable. */\n maxRetries?: number;\n /** Initial backoff (ms). Doubles each attempt, capped at `maxBackoff`. Default: 1000. */\n initialBackoff?: number;\n /** Maximum backoff (ms). Default: 30000. */\n maxBackoff?: number;\n /**\n * Ping interval (ms). When set, the client sends `pingPayload` periodically\n * to keep the socket alive. Default: 0 (disabled).\n */\n pingInterval?: number;\n /** Payload sent on each ping. Default: `JSON.stringify({ type: \"ping\" })`. */\n pingPayload?: string | Blob | BufferSource;\n /** Parse incoming frames. Default: JSON with raw-string fallback. */\n parser?: (raw: string) => T;\n onOpen?: (event: Event) => void;\n onMessage?: (message: WebSocketMessage<T>) => void;\n onClose?: (event: CloseEvent) => void;\n onError?: (event: Event) => void;\n onStatusChange?: (status: WebSocketStatus) => void;\n}\n\nexport interface WebSocketController {\n /** Send a payload over the current connection. No-op when not open. */\n send: (payload: string | Blob | BufferSource) => boolean;\n /** Close the connection and stop reconnecting. */\n close: (code?: number, reason?: string) => void;\n /** Force an immediate reconnect, resetting the retry counter. */\n reconnect: () => void;\n /** Current connection status. */\n readonly status: WebSocketStatus;\n}\n\nfunction defaultParser<T>(raw: string): T {\n try {\n return JSON.parse(raw) as T;\n } catch {\n return raw as unknown as T;\n }\n}\n\n/**\n * Open a WebSocket with automatic exponential-backoff reconnect, optional\n * heartbeat pings, and typed JSON parsing.\n *\n * @param url - Full ws:// or wss:// URL.\n * @param options - Connection configuration and callbacks.\n * @returns Controller exposing `send`, `close`, `reconnect`, and `status`.\n */\nexport function createWebSocket<T = unknown>(\n url: string,\n options: CreateWebSocketOptions<T> = {},\n): WebSocketController {\n const {\n protocols,\n maxRetries = 10,\n initialBackoff = 1000,\n maxBackoff = 30000,\n pingInterval = 0,\n pingPayload = JSON.stringify({ type: \"ping\" }),\n parser = defaultParser<T>,\n onOpen,\n onMessage,\n onClose,\n onError,\n onStatusChange,\n } = options;\n\n let socket: WebSocket | null = null;\n let retryTimer: ReturnType<typeof setTimeout> | null = null;\n let pingTimer: ReturnType<typeof setInterval> | null = null;\n let retries = 0;\n let status: WebSocketStatus = \"idle\";\n let closed = false;\n\n function setStatus(next: WebSocketStatus): void {\n if (status === next) return;\n status = next;\n onStatusChange?.(next);\n }\n\n function clearPing(): void {\n if (pingTimer) {\n clearInterval(pingTimer);\n pingTimer = null;\n }\n }\n\n function startPing(): void {\n if (!pingInterval || pingInterval <= 0) return;\n clearPing();\n pingTimer = setInterval(() => {\n if (socket?.readyState === WebSocket.OPEN) {\n socket.send(pingPayload);\n }\n }, pingInterval);\n }\n\n function scheduleReconnect(): void {\n if (closed) return;\n if (retries >= maxRetries) {\n setStatus(\"error\");\n return;\n }\n const delay = Math.min(initialBackoff * 2 ** retries, maxBackoff);\n retries += 1;\n retryTimer = setTimeout(connect, delay);\n }\n\n function connect(): void {\n if (closed) return;\n if (socket) {\n socket.onopen = null;\n socket.onmessage = null;\n socket.onclose = null;\n socket.onerror = null;\n socket.close();\n }\n setStatus(\"connecting\");\n\n const ws = new WebSocket(url, protocols);\n socket = ws;\n\n ws.onopen = (event) => {\n retries = 0;\n setStatus(\"open\");\n startPing();\n onOpen?.(event);\n };\n\n ws.onmessage = (event) => {\n const raw = typeof event.data === \"string\" ? event.data : \"\";\n onMessage?.({\n data: parser(raw),\n raw: event,\n });\n };\n\n ws.onerror = (event) => {\n onError?.(event);\n };\n\n ws.onclose = (event) => {\n clearPing();\n onClose?.(event);\n socket = null;\n setStatus(\"closed\");\n if (!closed && !event.wasClean) {\n scheduleReconnect();\n }\n };\n }\n\n function send(payload: string | Blob | BufferSource): boolean {\n if (socket?.readyState !== WebSocket.OPEN) return false;\n socket.send(payload);\n return true;\n }\n\n function close(code?: number, reason?: string): void {\n closed = true;\n if (retryTimer) {\n clearTimeout(retryTimer);\n retryTimer = null;\n }\n clearPing();\n retries = 0;\n if (socket) {\n setStatus(\"closing\");\n socket.close(code, reason);\n socket = null;\n }\n setStatus(\"closed\");\n }\n\n function reconnect(): void {\n if (retryTimer) {\n clearTimeout(retryTimer);\n retryTimer = null;\n }\n retries = 0;\n closed = false;\n connect();\n }\n\n connect();\n\n return {\n send,\n close,\n reconnect,\n get status() {\n return status;\n },\n };\n}\n"],"mappings":"AA6CA,SAAS,EAAiB,EAAgB,CACtC,GAAI,CACA,OAAO,KAAK,MAAM,CAAG,CACzB,MAAQ,CACJ,OAAO,CACX,CACJ,CAUA,SAAgB,EACZ,EACA,EAAqC,CAAC,EACnB,CACnB,GAAM,CACF,YACA,aAAa,GACb,iBAAiB,IACjB,aAAa,IACb,eAAe,EACf,cAAc,KAAK,UAAU,CAAE,KAAM,MAAO,CAAC,EAC7C,SAAS,EACT,SACA,YACA,UACA,UACA,kBACA,EAEA,EAA2B,KAC3B,EAAmD,KACnD,EAAmD,KACnD,EAAU,EACV,EAA0B,OAC1B,EAAS,GAEb,SAAS,EAAU,EAA6B,CACxC,IAAW,IACf,EAAS,EACT,IAAiB,CAAI,EACzB,CAEA,SAAS,GAAkB,CACvB,AAEI,KADA,cAAc,CAAS,EACX,KAEpB,CAEA,SAAS,GAAkB,CACnB,CAAC,GAAgB,GAAgB,IACrC,EAAU,EACV,EAAY,gBAAkB,CACtB,GAAQ,aAAe,UAAU,MACjC,EAAO,KAAK,CAAW,CAE/B,EAAG,CAAY,EACnB,CAEA,SAAS,GAA0B,CAC/B,GAAI,EAAQ,OACZ,GAAI,GAAW,EAAY,CACvB,EAAU,OAAO,EACjB,MACJ,CACA,IAAM,EAAQ,KAAK,IAAI,EAAiB,GAAK,EAAS,CAAU,EAChE,GAAW,EACX,EAAa,WAAW,EAAS,CAAK,CAC1C,CAEA,SAAS,GAAgB,CACrB,GAAI,EAAQ,OACR,IACA,EAAO,OAAS,KAChB,EAAO,UAAY,KACnB,EAAO,QAAU,KACjB,EAAO,QAAU,KACjB,EAAO,MAAM,GAEjB,EAAU,YAAY,EAEtB,IAAM,EAAK,IAAI,UAAU,EAAK,CAAS,EACvC,EAAS,EAET,EAAG,OAAU,GAAU,CACnB,EAAU,EACV,EAAU,MAAM,EAChB,EAAU,EACV,IAAS,CAAK,CAClB,EAEA,EAAG,UAAa,GAAU,CACtB,IAAM,EAAM,OAAO,EAAM,MAAS,SAAW,EAAM,KAAO,GAC1D,IAAY,CACR,KAAM,EAAO,CAAG,EAChB,IAAK,CACT,CAAC,CACL,EAEA,EAAG,QAAW,GAAU,CACpB,IAAU,CAAK,CACnB,EAEA,EAAG,QAAW,GAAU,CACpB,EAAU,EACV,IAAU,CAAK,EACf,EAAS,KACT,EAAU,QAAQ,EACd,CAAC,GAAU,CAAC,EAAM,UAClB,EAAkB,CAE1B,CACJ,CAEA,SAAS,EAAK,EAAgD,CAG1D,OAFI,GAAQ,aAAe,UAAU,MACrC,EAAO,KAAK,CAAO,EACZ,IAF2C,EAGtD,CAEA,SAAS,EAAM,EAAe,EAAuB,CACjD,EAAS,GACT,AAEI,KADA,aAAa,CAAU,EACV,MAEjB,EAAU,EACV,EAAU,EACV,AAGI,KAFA,EAAU,SAAS,EACnB,EAAO,MAAM,EAAM,CAAM,EAChB,MAEb,EAAU,QAAQ,CACtB,CAEA,SAAS,GAAkB,CACvB,AAEI,KADA,aAAa,CAAU,EACV,MAEjB,EAAU,EACV,EAAS,GACT,EAAQ,CACZ,CAIA,OAFA,EAAQ,EAED,CACH,OACA,QACA,YACA,IAAI,QAAS,CACT,OAAO,CACX,CACJ,CACJ"}
1
+ {"version":3,"file":"create-web-socket.cjs","names":[],"sources":["../../src/ws/create-web-socket.ts"],"sourcesContent":["export type WebSocketStatus = \"idle\" | \"connecting\" | \"open\" | \"closing\" | \"closed\" | \"error\";\n\nexport interface WebSocketMessage<T> {\n /** Parsed payload — JSON-decoded when possible, raw string otherwise. */\n data: T;\n /** The original `MessageEvent`. */\n raw: MessageEvent;\n}\n\nexport interface CreateWebSocketOptions<T> {\n /** Subprotocol(s) forwarded to the `WebSocket` constructor. */\n protocols?: string | string[];\n /** Max reconnect attempts. Default: 10. Pass 0 to disable. */\n maxRetries?: number;\n /** Initial backoff (ms). Doubles each attempt, capped at `maxBackoff`. Default: 1000. */\n initialBackoff?: number;\n /** Maximum backoff (ms). Default: 30000. */\n maxBackoff?: number;\n /**\n * Ping interval (ms). When set, the client sends `pingPayload` periodically\n * to keep the socket alive. Default: 0 (disabled).\n *\n * Leave it off against a `tempest-fastapi-sdk` server: that server pings on\n * its own and answers a client-sent `{\"type\":\"ping\"}` with nothing, while a\n * strict handler rejects the unknown frame. What it needs from the client\n * is the `pong` reply, which `respondToPing` sends for you.\n */\n pingInterval?: number;\n /** Payload sent on each ping. Default: `JSON.stringify({ type: \"ping\" })`. */\n pingPayload?: string | Blob | BufferSource;\n /**\n * Reply to a server `{\"type\":\"ping\"}` with `pongPayload`. Default: true.\n *\n * `tempest-fastapi-sdk` closes a socket with code `4408` when no `pong`\n * arrives within `WS_HEARTBEAT_TIMEOUT_SECONDS`, so a client that stays\n * silent is dropped once per timeout. The ping is still forwarded to\n * `onMessage` — the reply is sent before your handler runs.\n */\n respondToPing?: boolean;\n /** Payload sent in reply to a server ping. Default: `JSON.stringify({ type: \"pong\" })`. */\n pongPayload?: string | Blob | BufferSource;\n /**\n * Buffer payloads sent while the socket is not open and flush them on the\n * next `open`. Default: false — `send()` returns false and drops.\n *\n * Without it, an action fired during reconnect backoff vanishes and the UI\n * cannot tell \"never sent\" from \"sent and ignored\".\n */\n queueWhileClosed?: boolean;\n /** Cap on buffered payloads when `queueWhileClosed` is on. Default: 100. */\n maxQueuedMessages?: number;\n /** Parse incoming frames. Default: JSON with raw-string fallback. */\n parser?: (raw: string) => T;\n onOpen?: (event: Event) => void;\n onMessage?: (message: WebSocketMessage<T>) => void;\n onClose?: (event: CloseEvent) => void;\n onError?: (event: Event) => void;\n onStatusChange?: (status: WebSocketStatus) => void;\n}\n\nexport interface WebSocketController {\n /** Send a payload over the current connection. No-op when not open. */\n send: (payload: string | Blob | BufferSource) => boolean;\n /** Close the connection and stop reconnecting. */\n close: (code?: number, reason?: string) => void;\n /** Force an immediate reconnect, resetting the retry counter. */\n reconnect: () => void;\n /** Current connection status. */\n readonly status: WebSocketStatus;\n}\n\nfunction defaultParser<T>(raw: string): T {\n try {\n return JSON.parse(raw) as T;\n } catch {\n return raw as unknown as T;\n }\n}\n\n/**\n * Open a WebSocket with automatic exponential-backoff reconnect, optional\n * heartbeat pings, and typed JSON parsing.\n *\n * @param url - Full ws:// or wss:// URL.\n * @param options - Connection configuration and callbacks.\n * @returns Controller exposing `send`, `close`, `reconnect`, and `status`.\n */\nexport function createWebSocket<T = unknown>(\n url: string,\n options: CreateWebSocketOptions<T> = {},\n): WebSocketController {\n const {\n protocols,\n maxRetries = 10,\n initialBackoff = 1000,\n maxBackoff = 30000,\n pingInterval = 0,\n pingPayload = JSON.stringify({ type: \"ping\" }),\n respondToPing = true,\n pongPayload = JSON.stringify({ type: \"pong\" }),\n queueWhileClosed = false,\n maxQueuedMessages = 100,\n parser = defaultParser<T>,\n onOpen,\n onMessage,\n onClose,\n onError,\n onStatusChange,\n } = options;\n\n let socket: WebSocket | null = null;\n let retryTimer: ReturnType<typeof setTimeout> | null = null;\n let pingTimer: ReturnType<typeof setInterval> | null = null;\n let retries = 0;\n let status: WebSocketStatus = \"idle\";\n let closed = false;\n const outbox: Array<string | Blob | BufferSource> = [];\n\n /** True for a decoded frame that is the server's heartbeat ping. */\n function isServerPing(data: unknown): boolean {\n return (\n typeof data === \"object\" &&\n data !== null &&\n (data as { type?: unknown }).type === \"ping\"\n );\n }\n\n /** Send everything buffered while the socket was down, oldest first. */\n function flushOutbox(ws: WebSocket): void {\n while (outbox.length > 0 && ws.readyState === WebSocket.OPEN) {\n ws.send(outbox.shift()!);\n }\n }\n\n function setStatus(next: WebSocketStatus): void {\n if (status === next) return;\n status = next;\n onStatusChange?.(next);\n }\n\n function clearPing(): void {\n if (pingTimer) {\n clearInterval(pingTimer);\n pingTimer = null;\n }\n }\n\n function startPing(): void {\n if (!pingInterval || pingInterval <= 0) return;\n clearPing();\n pingTimer = setInterval(() => {\n if (socket?.readyState === WebSocket.OPEN) {\n socket.send(pingPayload);\n }\n }, pingInterval);\n }\n\n function scheduleReconnect(): void {\n if (closed) return;\n if (retries >= maxRetries) {\n setStatus(\"error\");\n return;\n }\n const delay = Math.min(initialBackoff * 2 ** retries, maxBackoff);\n retries += 1;\n retryTimer = setTimeout(connect, delay);\n }\n\n function connect(): void {\n if (closed) return;\n if (socket) {\n const previous = socket;\n previous.onmessage = null;\n previous.onclose = null;\n previous.onerror = null;\n if (previous.readyState !== WebSocket.CONNECTING) previous.onopen = null;\n closeSocket(previous);\n }\n setStatus(\"connecting\");\n\n const ws = new WebSocket(url, protocols);\n socket = ws;\n\n ws.onopen = (event) => {\n retries = 0;\n setStatus(\"open\");\n startPing();\n flushOutbox(ws);\n onOpen?.(event);\n };\n\n ws.onmessage = (event) => {\n const raw = typeof event.data === \"string\" ? event.data : \"\";\n const data = parser(raw);\n if (respondToPing && isServerPing(data) && ws.readyState === WebSocket.OPEN) {\n ws.send(pongPayload);\n }\n onMessage?.({ data, raw: event });\n };\n\n ws.onerror = (event) => {\n onError?.(event);\n };\n\n ws.onclose = (event) => {\n clearPing();\n onClose?.(event);\n socket = null;\n setStatus(\"closed\");\n if (!closed && !event.wasClean) {\n scheduleReconnect();\n }\n };\n }\n\n function send(payload: string | Blob | BufferSource): boolean {\n if (socket?.readyState === WebSocket.OPEN) {\n socket.send(payload);\n return true;\n }\n if (!queueWhileClosed || closed) return false;\n if (outbox.length >= maxQueuedMessages) outbox.shift();\n outbox.push(payload);\n return true;\n }\n\n function close(code?: number, reason?: string): void {\n closed = true;\n if (retryTimer) {\n clearTimeout(retryTimer);\n retryTimer = null;\n }\n clearPing();\n retries = 0;\n outbox.length = 0;\n if (socket) {\n setStatus(\"closing\");\n closeSocket(socket, code, reason);\n socket = null;\n }\n setStatus(\"closed\");\n }\n\n /**\n * Close a socket without the \"closed before the connection is established\"\n * console warning.\n *\n * A socket still in `CONNECTING` cannot be closed cleanly — the browser\n * logs that warning on every attempt. React's StrictMode mounts, unmounts\n * and remounts each component in development, so the first socket is\n * always torn down mid-handshake and the message shows up in every dev\n * session of every app using the hook. Deferring the close to `onopen`\n * costs one round trip and keeps the console usable.\n */\n function closeSocket(ws: WebSocket, code?: number, reason?: string): void {\n if (ws.readyState === WebSocket.CONNECTING) {\n ws.onopen = () => ws.close(code, reason);\n ws.onmessage = null;\n ws.onerror = null;\n ws.onclose = null;\n return;\n }\n ws.close(code, reason);\n }\n\n function reconnect(): void {\n if (retryTimer) {\n clearTimeout(retryTimer);\n retryTimer = null;\n }\n retries = 0;\n closed = false;\n connect();\n }\n\n connect();\n\n return {\n send,\n close,\n reconnect,\n get status() {\n return status;\n },\n };\n}\n"],"mappings":"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"}
@@ -7,60 +7,77 @@ function e(e) {
7
7
  }
8
8
  }
9
9
  function t(t, n = {}) {
10
- let { protocols: r, maxRetries: i = 10, initialBackoff: a = 1e3, maxBackoff: o = 3e4, pingInterval: s = 0, pingPayload: c = JSON.stringify({ type: "ping" }), parser: l = e, onOpen: u, onMessage: d, onClose: f, onError: p, onStatusChange: m } = n, h = null, g = null, _ = null, v = 0, y = "idle", b = !1;
11
- function x(e) {
12
- y !== e && (y = e, m?.(e));
10
+ let { protocols: r, maxRetries: i = 10, initialBackoff: a = 1e3, maxBackoff: o = 3e4, pingInterval: s = 0, pingPayload: c = JSON.stringify({ type: "ping" }), respondToPing: l = !0, pongPayload: u = JSON.stringify({ type: "pong" }), queueWhileClosed: d = !1, maxQueuedMessages: f = 100, parser: p = e, onOpen: m, onMessage: h, onClose: g, onError: _, onStatusChange: v } = n, y = null, b = null, x = null, S = 0, C = "idle", w = !1, T = [];
11
+ function E(e) {
12
+ return typeof e == "object" && !!e && e.type === "ping";
13
+ }
14
+ function D(e) {
15
+ for (; T.length > 0 && e.readyState === WebSocket.OPEN;) e.send(T.shift());
13
16
  }
14
- function S() {
15
- _ &&= (clearInterval(_), null);
17
+ function O(e) {
18
+ C !== e && (C = e, v?.(e));
16
19
  }
17
- function C() {
18
- !s || s <= 0 || (S(), _ = setInterval(() => {
19
- h?.readyState === WebSocket.OPEN && h.send(c);
20
+ function k() {
21
+ x &&= (clearInterval(x), null);
22
+ }
23
+ function A() {
24
+ !s || s <= 0 || (k(), x = setInterval(() => {
25
+ y?.readyState === WebSocket.OPEN && y.send(c);
20
26
  }, s));
21
27
  }
22
- function w() {
23
- if (b) return;
24
- if (v >= i) {
25
- x("error");
28
+ function j() {
29
+ if (w) return;
30
+ if (S >= i) {
31
+ O("error");
26
32
  return;
27
33
  }
28
- let e = Math.min(a * 2 ** v, o);
29
- v += 1, g = setTimeout(T, e);
34
+ let e = Math.min(a * 2 ** S, o);
35
+ S += 1, b = setTimeout(M, e);
30
36
  }
31
- function T() {
32
- if (b) return;
33
- h && (h.onopen = null, h.onmessage = null, h.onclose = null, h.onerror = null, h.close()), x("connecting");
37
+ function M() {
38
+ if (w) return;
39
+ if (y) {
40
+ let e = y;
41
+ e.onmessage = null, e.onclose = null, e.onerror = null, e.readyState !== WebSocket.CONNECTING && (e.onopen = null), F(e);
42
+ }
43
+ O("connecting");
34
44
  let e = new WebSocket(t, r);
35
- h = e, e.onopen = (e) => {
36
- v = 0, x("open"), C(), u?.(e);
37
- }, e.onmessage = (e) => {
38
- let t = typeof e.data == "string" ? e.data : "";
39
- d?.({
40
- data: l(t),
41
- raw: e
45
+ y = e, e.onopen = (t) => {
46
+ S = 0, O("open"), A(), D(e), m?.(t);
47
+ }, e.onmessage = (t) => {
48
+ let n = typeof t.data == "string" ? t.data : "", r = p(n);
49
+ l && E(r) && e.readyState === WebSocket.OPEN && e.send(u), h?.({
50
+ data: r,
51
+ raw: t
42
52
  });
43
53
  }, e.onerror = (e) => {
44
- p?.(e);
54
+ _?.(e);
45
55
  }, e.onclose = (e) => {
46
- S(), f?.(e), h = null, x("closed"), !b && !e.wasClean && w();
56
+ k(), g?.(e), y = null, O("closed"), !w && !e.wasClean && j();
47
57
  };
48
58
  }
49
- function E(e) {
50
- return h?.readyState === WebSocket.OPEN ? (h.send(e), !0) : !1;
59
+ function N(e) {
60
+ return y?.readyState === WebSocket.OPEN ? (y.send(e), !0) : !d || w ? !1 : (T.length >= f && T.shift(), T.push(e), !0);
61
+ }
62
+ function P(e, t) {
63
+ w = !0, b &&= (clearTimeout(b), null), k(), S = 0, T.length = 0, y &&= (O("closing"), F(y, e, t), null), O("closed");
51
64
  }
52
- function D(e, t) {
53
- b = !0, g &&= (clearTimeout(g), null), S(), v = 0, h &&= (x("closing"), h.close(e, t), null), x("closed");
65
+ function F(e, t, n) {
66
+ if (e.readyState === WebSocket.CONNECTING) {
67
+ e.onopen = () => e.close(t, n), e.onmessage = null, e.onerror = null, e.onclose = null;
68
+ return;
69
+ }
70
+ e.close(t, n);
54
71
  }
55
- function O() {
56
- g &&= (clearTimeout(g), null), v = 0, b = !1, T();
72
+ function I() {
73
+ b &&= (clearTimeout(b), null), S = 0, w = !1, M();
57
74
  }
58
- return T(), {
59
- send: E,
60
- close: D,
61
- reconnect: O,
75
+ return M(), {
76
+ send: N,
77
+ close: P,
78
+ reconnect: I,
62
79
  get status() {
63
- return y;
80
+ return C;
64
81
  }
65
82
  };
66
83
  }
@@ -1 +1 @@
1
- {"version":3,"file":"create-web-socket.js","names":[],"sources":["../../src/ws/create-web-socket.ts"],"sourcesContent":["export type WebSocketStatus = \"idle\" | \"connecting\" | \"open\" | \"closing\" | \"closed\" | \"error\";\n\nexport interface WebSocketMessage<T> {\n /** Parsed payload — JSON-decoded when possible, raw string otherwise. */\n data: T;\n /** The original `MessageEvent`. */\n raw: MessageEvent;\n}\n\nexport interface CreateWebSocketOptions<T> {\n /** Subprotocol(s) forwarded to the `WebSocket` constructor. */\n protocols?: string | string[];\n /** Max reconnect attempts. Default: 10. Pass 0 to disable. */\n maxRetries?: number;\n /** Initial backoff (ms). Doubles each attempt, capped at `maxBackoff`. Default: 1000. */\n initialBackoff?: number;\n /** Maximum backoff (ms). Default: 30000. */\n maxBackoff?: number;\n /**\n * Ping interval (ms). When set, the client sends `pingPayload` periodically\n * to keep the socket alive. Default: 0 (disabled).\n */\n pingInterval?: number;\n /** Payload sent on each ping. Default: `JSON.stringify({ type: \"ping\" })`. */\n pingPayload?: string | Blob | BufferSource;\n /** Parse incoming frames. Default: JSON with raw-string fallback. */\n parser?: (raw: string) => T;\n onOpen?: (event: Event) => void;\n onMessage?: (message: WebSocketMessage<T>) => void;\n onClose?: (event: CloseEvent) => void;\n onError?: (event: Event) => void;\n onStatusChange?: (status: WebSocketStatus) => void;\n}\n\nexport interface WebSocketController {\n /** Send a payload over the current connection. No-op when not open. */\n send: (payload: string | Blob | BufferSource) => boolean;\n /** Close the connection and stop reconnecting. */\n close: (code?: number, reason?: string) => void;\n /** Force an immediate reconnect, resetting the retry counter. */\n reconnect: () => void;\n /** Current connection status. */\n readonly status: WebSocketStatus;\n}\n\nfunction defaultParser<T>(raw: string): T {\n try {\n return JSON.parse(raw) as T;\n } catch {\n return raw as unknown as T;\n }\n}\n\n/**\n * Open a WebSocket with automatic exponential-backoff reconnect, optional\n * heartbeat pings, and typed JSON parsing.\n *\n * @param url - Full ws:// or wss:// URL.\n * @param options - Connection configuration and callbacks.\n * @returns Controller exposing `send`, `close`, `reconnect`, and `status`.\n */\nexport function createWebSocket<T = unknown>(\n url: string,\n options: CreateWebSocketOptions<T> = {},\n): WebSocketController {\n const {\n protocols,\n maxRetries = 10,\n initialBackoff = 1000,\n maxBackoff = 30000,\n pingInterval = 0,\n pingPayload = JSON.stringify({ type: \"ping\" }),\n parser = defaultParser<T>,\n onOpen,\n onMessage,\n onClose,\n onError,\n onStatusChange,\n } = options;\n\n let socket: WebSocket | null = null;\n let retryTimer: ReturnType<typeof setTimeout> | null = null;\n let pingTimer: ReturnType<typeof setInterval> | null = null;\n let retries = 0;\n let status: WebSocketStatus = \"idle\";\n let closed = false;\n\n function setStatus(next: WebSocketStatus): void {\n if (status === next) return;\n status = next;\n onStatusChange?.(next);\n }\n\n function clearPing(): void {\n if (pingTimer) {\n clearInterval(pingTimer);\n pingTimer = null;\n }\n }\n\n function startPing(): void {\n if (!pingInterval || pingInterval <= 0) return;\n clearPing();\n pingTimer = setInterval(() => {\n if (socket?.readyState === WebSocket.OPEN) {\n socket.send(pingPayload);\n }\n }, pingInterval);\n }\n\n function scheduleReconnect(): void {\n if (closed) return;\n if (retries >= maxRetries) {\n setStatus(\"error\");\n return;\n }\n const delay = Math.min(initialBackoff * 2 ** retries, maxBackoff);\n retries += 1;\n retryTimer = setTimeout(connect, delay);\n }\n\n function connect(): void {\n if (closed) return;\n if (socket) {\n socket.onopen = null;\n socket.onmessage = null;\n socket.onclose = null;\n socket.onerror = null;\n socket.close();\n }\n setStatus(\"connecting\");\n\n const ws = new WebSocket(url, protocols);\n socket = ws;\n\n ws.onopen = (event) => {\n retries = 0;\n setStatus(\"open\");\n startPing();\n onOpen?.(event);\n };\n\n ws.onmessage = (event) => {\n const raw = typeof event.data === \"string\" ? event.data : \"\";\n onMessage?.({\n data: parser(raw),\n raw: event,\n });\n };\n\n ws.onerror = (event) => {\n onError?.(event);\n };\n\n ws.onclose = (event) => {\n clearPing();\n onClose?.(event);\n socket = null;\n setStatus(\"closed\");\n if (!closed && !event.wasClean) {\n scheduleReconnect();\n }\n };\n }\n\n function send(payload: string | Blob | BufferSource): boolean {\n if (socket?.readyState !== WebSocket.OPEN) return false;\n socket.send(payload);\n return true;\n }\n\n function close(code?: number, reason?: string): void {\n closed = true;\n if (retryTimer) {\n clearTimeout(retryTimer);\n retryTimer = null;\n }\n clearPing();\n retries = 0;\n if (socket) {\n setStatus(\"closing\");\n socket.close(code, reason);\n socket = null;\n }\n setStatus(\"closed\");\n }\n\n function reconnect(): void {\n if (retryTimer) {\n clearTimeout(retryTimer);\n retryTimer = null;\n }\n retries = 0;\n closed = false;\n connect();\n }\n\n connect();\n\n return {\n send,\n close,\n reconnect,\n get status() {\n return status;\n },\n };\n}\n"],"mappings":";AA6CA,SAAS,EAAiB,GAAgB;CACtC,IAAI;EACA,OAAO,KAAK,MAAM,CAAG;CACzB,QAAQ;EACJ,OAAO;CACX;AACJ;AAUA,SAAgB,EACZ,GACA,IAAqC,CAAC,GACnB;CACnB,IAAM,EACF,cACA,gBAAa,IACb,oBAAiB,KACjB,gBAAa,KACb,kBAAe,GACf,iBAAc,KAAK,UAAU,EAAE,MAAM,OAAO,CAAC,GAC7C,YAAS,GACT,WACA,cACA,YACA,YACA,sBACA,GAEA,IAA2B,MAC3B,IAAmD,MACnD,IAAmD,MACnD,IAAU,GACV,IAA0B,QAC1B,IAAS;CAEb,SAAS,EAAU,GAA6B;EACxC,MAAW,MACf,IAAS,GACT,IAAiB,CAAI;CACzB;CAEA,SAAS,IAAkB;EACvB,AAEI,OADA,cAAc,CAAS,GACX;CAEpB;CAEA,SAAS,IAAkB;EACnB,CAAC,KAAgB,KAAgB,MACrC,EAAU,GACV,IAAY,kBAAkB;GAC1B,AAAI,GAAQ,eAAe,UAAU,QACjC,EAAO,KAAK,CAAW;EAE/B,GAAG,CAAY;CACnB;CAEA,SAAS,IAA0B;EAC/B,IAAI,GAAQ;EACZ,IAAI,KAAW,GAAY;GACvB,EAAU,OAAO;GACjB;EACJ;EACA,IAAM,IAAQ,KAAK,IAAI,IAAiB,KAAK,GAAS,CAAU;EAEhE,AADA,KAAW,GACX,IAAa,WAAW,GAAS,CAAK;CAC1C;CAEA,SAAS,IAAgB;EACrB,IAAI,GAAQ;EAQZ,AAPI,MACA,EAAO,SAAS,MAChB,EAAO,YAAY,MACnB,EAAO,UAAU,MACjB,EAAO,UAAU,MACjB,EAAO,MAAM,IAEjB,EAAU,YAAY;EAEtB,IAAM,IAAK,IAAI,UAAU,GAAK,CAAS;EAsBvC,AArBA,IAAS,GAET,EAAG,UAAU,MAAU;GAInB,AAHA,IAAU,GACV,EAAU,MAAM,GAChB,EAAU,GACV,IAAS,CAAK;EAClB,GAEA,EAAG,aAAa,MAAU;GACtB,IAAM,IAAM,OAAO,EAAM,QAAS,WAAW,EAAM,OAAO;GAC1D,IAAY;IACR,MAAM,EAAO,CAAG;IAChB,KAAK;GACT,CAAC;EACL,GAEA,EAAG,WAAW,MAAU;GACpB,IAAU,CAAK;EACnB,GAEA,EAAG,WAAW,MAAU;GAKpB,AAJA,EAAU,GACV,IAAU,CAAK,GACf,IAAS,MACT,EAAU,QAAQ,GACd,CAAC,KAAU,CAAC,EAAM,YAClB,EAAkB;EAE1B;CACJ;CAEA,SAAS,EAAK,GAAgD;EAG1D,OAFI,GAAQ,eAAe,UAAU,QACrC,EAAO,KAAK,CAAO,GACZ,MAF2C;CAGtD;CAEA,SAAS,EAAM,GAAe,GAAuB;EAajD,AAZA,IAAS,IACT,AAEI,OADA,aAAa,CAAU,GACV,OAEjB,EAAU,GACV,IAAU,GACV,AAGI,OAFA,EAAU,SAAS,GACnB,EAAO,MAAM,GAAM,CAAM,GAChB,OAEb,EAAU,QAAQ;CACtB;CAEA,SAAS,IAAkB;EAOvB,AANA,AAEI,OADA,aAAa,CAAU,GACV,OAEjB,IAAU,GACV,IAAS,IACT,EAAQ;CACZ;CAIA,OAFA,EAAQ,GAED;EACH;EACA;EACA;EACA,IAAI,SAAS;GACT,OAAO;EACX;CACJ;AACJ"}
1
+ {"version":3,"file":"create-web-socket.js","names":[],"sources":["../../src/ws/create-web-socket.ts"],"sourcesContent":["export type WebSocketStatus = \"idle\" | \"connecting\" | \"open\" | \"closing\" | \"closed\" | \"error\";\n\nexport interface WebSocketMessage<T> {\n /** Parsed payload — JSON-decoded when possible, raw string otherwise. */\n data: T;\n /** The original `MessageEvent`. */\n raw: MessageEvent;\n}\n\nexport interface CreateWebSocketOptions<T> {\n /** Subprotocol(s) forwarded to the `WebSocket` constructor. */\n protocols?: string | string[];\n /** Max reconnect attempts. Default: 10. Pass 0 to disable. */\n maxRetries?: number;\n /** Initial backoff (ms). Doubles each attempt, capped at `maxBackoff`. Default: 1000. */\n initialBackoff?: number;\n /** Maximum backoff (ms). Default: 30000. */\n maxBackoff?: number;\n /**\n * Ping interval (ms). When set, the client sends `pingPayload` periodically\n * to keep the socket alive. Default: 0 (disabled).\n *\n * Leave it off against a `tempest-fastapi-sdk` server: that server pings on\n * its own and answers a client-sent `{\"type\":\"ping\"}` with nothing, while a\n * strict handler rejects the unknown frame. What it needs from the client\n * is the `pong` reply, which `respondToPing` sends for you.\n */\n pingInterval?: number;\n /** Payload sent on each ping. Default: `JSON.stringify({ type: \"ping\" })`. */\n pingPayload?: string | Blob | BufferSource;\n /**\n * Reply to a server `{\"type\":\"ping\"}` with `pongPayload`. Default: true.\n *\n * `tempest-fastapi-sdk` closes a socket with code `4408` when no `pong`\n * arrives within `WS_HEARTBEAT_TIMEOUT_SECONDS`, so a client that stays\n * silent is dropped once per timeout. The ping is still forwarded to\n * `onMessage` — the reply is sent before your handler runs.\n */\n respondToPing?: boolean;\n /** Payload sent in reply to a server ping. Default: `JSON.stringify({ type: \"pong\" })`. */\n pongPayload?: string | Blob | BufferSource;\n /**\n * Buffer payloads sent while the socket is not open and flush them on the\n * next `open`. Default: false — `send()` returns false and drops.\n *\n * Without it, an action fired during reconnect backoff vanishes and the UI\n * cannot tell \"never sent\" from \"sent and ignored\".\n */\n queueWhileClosed?: boolean;\n /** Cap on buffered payloads when `queueWhileClosed` is on. Default: 100. */\n maxQueuedMessages?: number;\n /** Parse incoming frames. Default: JSON with raw-string fallback. */\n parser?: (raw: string) => T;\n onOpen?: (event: Event) => void;\n onMessage?: (message: WebSocketMessage<T>) => void;\n onClose?: (event: CloseEvent) => void;\n onError?: (event: Event) => void;\n onStatusChange?: (status: WebSocketStatus) => void;\n}\n\nexport interface WebSocketController {\n /** Send a payload over the current connection. No-op when not open. */\n send: (payload: string | Blob | BufferSource) => boolean;\n /** Close the connection and stop reconnecting. */\n close: (code?: number, reason?: string) => void;\n /** Force an immediate reconnect, resetting the retry counter. */\n reconnect: () => void;\n /** Current connection status. */\n readonly status: WebSocketStatus;\n}\n\nfunction defaultParser<T>(raw: string): T {\n try {\n return JSON.parse(raw) as T;\n } catch {\n return raw as unknown as T;\n }\n}\n\n/**\n * Open a WebSocket with automatic exponential-backoff reconnect, optional\n * heartbeat pings, and typed JSON parsing.\n *\n * @param url - Full ws:// or wss:// URL.\n * @param options - Connection configuration and callbacks.\n * @returns Controller exposing `send`, `close`, `reconnect`, and `status`.\n */\nexport function createWebSocket<T = unknown>(\n url: string,\n options: CreateWebSocketOptions<T> = {},\n): WebSocketController {\n const {\n protocols,\n maxRetries = 10,\n initialBackoff = 1000,\n maxBackoff = 30000,\n pingInterval = 0,\n pingPayload = JSON.stringify({ type: \"ping\" }),\n respondToPing = true,\n pongPayload = JSON.stringify({ type: \"pong\" }),\n queueWhileClosed = false,\n maxQueuedMessages = 100,\n parser = defaultParser<T>,\n onOpen,\n onMessage,\n onClose,\n onError,\n onStatusChange,\n } = options;\n\n let socket: WebSocket | null = null;\n let retryTimer: ReturnType<typeof setTimeout> | null = null;\n let pingTimer: ReturnType<typeof setInterval> | null = null;\n let retries = 0;\n let status: WebSocketStatus = \"idle\";\n let closed = false;\n const outbox: Array<string | Blob | BufferSource> = [];\n\n /** True for a decoded frame that is the server's heartbeat ping. */\n function isServerPing(data: unknown): boolean {\n return (\n typeof data === \"object\" &&\n data !== null &&\n (data as { type?: unknown }).type === \"ping\"\n );\n }\n\n /** Send everything buffered while the socket was down, oldest first. */\n function flushOutbox(ws: WebSocket): void {\n while (outbox.length > 0 && ws.readyState === WebSocket.OPEN) {\n ws.send(outbox.shift()!);\n }\n }\n\n function setStatus(next: WebSocketStatus): void {\n if (status === next) return;\n status = next;\n onStatusChange?.(next);\n }\n\n function clearPing(): void {\n if (pingTimer) {\n clearInterval(pingTimer);\n pingTimer = null;\n }\n }\n\n function startPing(): void {\n if (!pingInterval || pingInterval <= 0) return;\n clearPing();\n pingTimer = setInterval(() => {\n if (socket?.readyState === WebSocket.OPEN) {\n socket.send(pingPayload);\n }\n }, pingInterval);\n }\n\n function scheduleReconnect(): void {\n if (closed) return;\n if (retries >= maxRetries) {\n setStatus(\"error\");\n return;\n }\n const delay = Math.min(initialBackoff * 2 ** retries, maxBackoff);\n retries += 1;\n retryTimer = setTimeout(connect, delay);\n }\n\n function connect(): void {\n if (closed) return;\n if (socket) {\n const previous = socket;\n previous.onmessage = null;\n previous.onclose = null;\n previous.onerror = null;\n if (previous.readyState !== WebSocket.CONNECTING) previous.onopen = null;\n closeSocket(previous);\n }\n setStatus(\"connecting\");\n\n const ws = new WebSocket(url, protocols);\n socket = ws;\n\n ws.onopen = (event) => {\n retries = 0;\n setStatus(\"open\");\n startPing();\n flushOutbox(ws);\n onOpen?.(event);\n };\n\n ws.onmessage = (event) => {\n const raw = typeof event.data === \"string\" ? event.data : \"\";\n const data = parser(raw);\n if (respondToPing && isServerPing(data) && ws.readyState === WebSocket.OPEN) {\n ws.send(pongPayload);\n }\n onMessage?.({ data, raw: event });\n };\n\n ws.onerror = (event) => {\n onError?.(event);\n };\n\n ws.onclose = (event) => {\n clearPing();\n onClose?.(event);\n socket = null;\n setStatus(\"closed\");\n if (!closed && !event.wasClean) {\n scheduleReconnect();\n }\n };\n }\n\n function send(payload: string | Blob | BufferSource): boolean {\n if (socket?.readyState === WebSocket.OPEN) {\n socket.send(payload);\n return true;\n }\n if (!queueWhileClosed || closed) return false;\n if (outbox.length >= maxQueuedMessages) outbox.shift();\n outbox.push(payload);\n return true;\n }\n\n function close(code?: number, reason?: string): void {\n closed = true;\n if (retryTimer) {\n clearTimeout(retryTimer);\n retryTimer = null;\n }\n clearPing();\n retries = 0;\n outbox.length = 0;\n if (socket) {\n setStatus(\"closing\");\n closeSocket(socket, code, reason);\n socket = null;\n }\n setStatus(\"closed\");\n }\n\n /**\n * Close a socket without the \"closed before the connection is established\"\n * console warning.\n *\n * A socket still in `CONNECTING` cannot be closed cleanly — the browser\n * logs that warning on every attempt. React's StrictMode mounts, unmounts\n * and remounts each component in development, so the first socket is\n * always torn down mid-handshake and the message shows up in every dev\n * session of every app using the hook. Deferring the close to `onopen`\n * costs one round trip and keeps the console usable.\n */\n function closeSocket(ws: WebSocket, code?: number, reason?: string): void {\n if (ws.readyState === WebSocket.CONNECTING) {\n ws.onopen = () => ws.close(code, reason);\n ws.onmessage = null;\n ws.onerror = null;\n ws.onclose = null;\n return;\n }\n ws.close(code, reason);\n }\n\n function reconnect(): void {\n if (retryTimer) {\n clearTimeout(retryTimer);\n retryTimer = null;\n }\n retries = 0;\n closed = false;\n connect();\n }\n\n connect();\n\n return {\n send,\n close,\n reconnect,\n get status() {\n return status;\n },\n };\n}\n"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- const e=require("./create-web-socket.cjs");let t=require("react");function n(n,r={}){let{enabled:i=!0,onMessage:a,...o}=r,[s,c]=(0,t.useState)(`idle`),[l,u]=(0,t.useState)(null),d=(0,t.useRef)(null),f=(0,t.useRef)(a);return(0,t.useEffect)(()=>{f.current=a},[a]),(0,t.useEffect)(()=>{if(!i||!n){c(`idle`);return}let t=e.createWebSocket(n,{...o,onStatusChange:c,onMessage:e=>{u(e),f.current?.(e)}});return d.current=t,()=>{t.close(),d.current=null}},[n,i]),{status:s,lastMessage:l,send:(0,t.useCallback)(e=>d.current?.send(e)??!1,[]),reconnect:(0,t.useCallback)(()=>{d.current?.reconnect()},[])}}exports.useWebSocket=n;
1
+ const e=require("./create-web-socket.cjs");let t=require("react");function n(e){try{return JSON.parse(e)}catch{return e}}function r(r,i={}){let{enabled:a=!0,protocols:o,maxRetries:s,initialBackoff:c,maxBackoff:l,pingInterval:u,respondToPing:d,queueWhileClosed:f,maxQueuedMessages:p}=i,[m,h]=(0,t.useState)(`idle`),[g,_]=(0,t.useState)(null),v=(0,t.useRef)(null),y=(0,t.useRef)(i);return(0,t.useEffect)(()=>{y.current=i}),(0,t.useEffect)(()=>{if(!a||!r){h(`idle`);return}let t=e.createWebSocket(r,{protocols:y.current.protocols,maxRetries:s,initialBackoff:c,maxBackoff:l,pingInterval:u,pingPayload:y.current.pingPayload,respondToPing:d,pongPayload:y.current.pongPayload,queueWhileClosed:f,maxQueuedMessages:p,parser:e=>(y.current.parser??n)(e),onStatusChange:h,onOpen:e=>y.current.onOpen?.(e),onClose:e=>y.current.onClose?.(e),onError:e=>y.current.onError?.(e),onMessage:e=>{_(e),y.current.onMessage?.(e)}});return v.current=t,()=>{t.close(),v.current=null}},[r,a,Array.isArray(o)?o.join(`,`):o??``,s,c,l,u,d,f,p]),{status:m,lastMessage:g,send:(0,t.useCallback)(e=>v.current?.send(e)??!1,[]),reconnect:(0,t.useCallback)(()=>{v.current?.reconnect()},[])}}exports.useWebSocket=r;
2
2
  //# sourceMappingURL=use-web-socket.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"use-web-socket.cjs","names":[],"sources":["../../src/ws/use-web-socket.ts"],"sourcesContent":["import { useCallback, useEffect, useRef, useState } from \"react\";\nimport {\n createWebSocket,\n type CreateWebSocketOptions,\n type WebSocketController,\n type WebSocketMessage,\n type WebSocketStatus,\n} from \"./create-web-socket\";\n\nexport interface UseWebSocketOptions<T> extends Omit<CreateWebSocketOptions<T>, \"onStatusChange\"> {\n /** When false, the socket is not opened. Default: true. */\n enabled?: boolean;\n}\n\nexport interface UseWebSocketResult<T> {\n status: WebSocketStatus;\n /** Last decoded frame received. */\n lastMessage: WebSocketMessage<T> | null;\n /** Send a payload through the active connection. Returns false when not open. */\n send: (payload: string | Blob | BufferSource) => boolean;\n /** Force a reconnect, resetting the retry counter. */\n reconnect: () => void;\n}\n\n/**\n * React hook around {@link createWebSocket}. Manages the connection lifecycle\n * for the host component and tears it down on unmount.\n */\nexport function useWebSocket<T = unknown>(\n url: string,\n options: UseWebSocketOptions<T> = {},\n): UseWebSocketResult<T> {\n const { enabled = true, onMessage, ...rest } = options;\n const [status, setStatus] = useState<WebSocketStatus>(\"idle\");\n const [lastMessage, setLastMessage] = useState<WebSocketMessage<T> | null>(null);\n const controllerRef = useRef<WebSocketController | null>(null);\n\n const onMessageRef = useRef(onMessage);\n useEffect(() => {\n onMessageRef.current = onMessage;\n }, [onMessage]);\n\n useEffect(() => {\n if (!enabled || !url) {\n setStatus(\"idle\");\n return;\n }\n\n const controller = createWebSocket<T>(url, {\n ...rest,\n onStatusChange: setStatus,\n onMessage: (message) => {\n setLastMessage(message);\n onMessageRef.current?.(message);\n },\n });\n controllerRef.current = controller;\n\n return () => {\n controller.close();\n controllerRef.current = null;\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [url, enabled]);\n\n const send = useCallback((payload: string | Blob | BufferSource): boolean => {\n return controllerRef.current?.send(payload) ?? false;\n }, []);\n\n const reconnect = useCallback((): void => {\n controllerRef.current?.reconnect();\n }, []);\n\n return { status, lastMessage, send, reconnect };\n}\n"],"mappings":"kEA4BA,SAAgB,EACZ,EACA,EAAkC,CAAC,EACd,CACrB,GAAM,CAAE,UAAU,GAAM,YAAW,GAAG,GAAS,EACzC,CAAC,EAAQ,IAAA,EAAA,EAAA,SAAA,CAAuC,MAAM,EACtD,CAAC,EAAa,IAAA,EAAA,EAAA,SAAA,CAAuD,IAAI,EACzE,GAAA,EAAA,EAAA,OAAA,CAAmD,IAAI,EAEvD,GAAA,EAAA,EAAA,OAAA,CAAsB,CAAS,EAoCrC,OAnCA,EAAA,EAAA,UAAA,KAAgB,CACZ,EAAa,QAAU,CAC3B,EAAG,CAAC,CAAS,CAAC,GAEd,EAAA,EAAA,UAAA,KAAgB,CACZ,GAAI,CAAC,GAAW,CAAC,EAAK,CAClB,EAAU,MAAM,EAChB,MACJ,CAEA,IAAM,EAAa,EAAA,gBAAmB,EAAK,CACvC,GAAG,EACH,eAAgB,EAChB,UAAY,GAAY,CACpB,EAAe,CAAO,EACtB,EAAa,UAAU,CAAO,CAClC,CACJ,CAAC,EAGD,MAFA,GAAc,QAAU,MAEX,CACT,EAAW,MAAM,EACjB,EAAc,QAAU,IAC5B,CAEJ,EAAG,CAAC,EAAK,CAAO,CAAC,EAUV,CAAE,SAAQ,cAAa,MAAA,EAAA,EAAA,YAAA,CARJ,GACf,EAAc,SAAS,KAAK,CAAO,GAAK,GAChD,CAAC,CAM0B,EAAM,WAAA,EAAA,EAAA,YAAA,KAJM,CACtC,EAAc,SAAS,UAAU,CACrC,EAAG,CAAC,CAEgC,CAAU,CAClD"}
1
+ {"version":3,"file":"use-web-socket.cjs","names":[],"sources":["../../src/ws/use-web-socket.ts"],"sourcesContent":["import { useCallback, useEffect, useRef, useState } from \"react\";\nimport {\n createWebSocket,\n type CreateWebSocketOptions,\n type WebSocketController,\n type WebSocketMessage,\n type WebSocketStatus,\n} from \"./create-web-socket\";\n\nexport interface UseWebSocketOptions<T> extends Omit<CreateWebSocketOptions<T>, \"onStatusChange\"> {\n /** When false, the socket is not opened. Default: true. */\n enabled?: boolean;\n}\n\nexport interface UseWebSocketResult<T> {\n status: WebSocketStatus;\n /**\n * Last decoded frame received.\n *\n * A snapshot, not a stream: two frames arriving in the same tick collapse\n * into a single render and only the later one is ever visible. One server\n * action often emits several frames in a row, so anything that must see\n * every message has to use `onMessage`, which fires once per frame. Read\n * `lastMessage` for \"what is the current state\" rendering only.\n */\n lastMessage: WebSocketMessage<T> | null;\n /** Send a payload through the active connection. Returns false when not open. */\n send: (payload: string | Blob | BufferSource) => boolean;\n /** Force a reconnect, resetting the retry counter. */\n reconnect: () => void;\n}\n\n/** Mirror of `createWebSocket`'s default parser: JSON, raw string on failure. */\nfunction defaultParse<T>(raw: string): T {\n try {\n return JSON.parse(raw) as T;\n } catch {\n return raw as unknown as T;\n }\n}\n\n/**\n * React hook around {@link createWebSocket}. Manages the connection lifecycle\n * for the host component and tears it down on unmount.\n *\n * Every callback is read through a ref, so `onOpen` / `onMessage` / `onClose` /\n * `onError` always run the latest closure — an inline arrow function is fine\n * and never reopens the socket. Connection-shaping options (`protocols`,\n * `maxRetries`, `initialBackoff`, `maxBackoff`, `pingInterval`,\n * `queueWhileClosed`) are baked into the connection, so changing one reopens\n * it with the new value rather than being silently ignored.\n *\n * @param url - Full ws:// or wss:// URL.\n * @param options - Connection configuration and callbacks.\n * @returns Status, last frame, and the `send` / `reconnect` controls.\n */\nexport function useWebSocket<T = unknown>(\n url: string,\n options: UseWebSocketOptions<T> = {},\n): UseWebSocketResult<T> {\n const {\n enabled = true,\n protocols,\n maxRetries,\n initialBackoff,\n maxBackoff,\n pingInterval,\n respondToPing,\n queueWhileClosed,\n maxQueuedMessages,\n } = options;\n const [status, setStatus] = useState<WebSocketStatus>(\"idle\");\n const [lastMessage, setLastMessage] = useState<WebSocketMessage<T> | null>(null);\n const controllerRef = useRef<WebSocketController | null>(null);\n\n const optionsRef = useRef(options);\n useEffect(() => {\n optionsRef.current = options;\n });\n\n const protocolsKey = Array.isArray(protocols) ? protocols.join(\",\") : (protocols ?? \"\");\n\n useEffect(() => {\n if (!enabled || !url) {\n setStatus(\"idle\");\n return;\n }\n\n const controller = createWebSocket<T>(url, {\n protocols: optionsRef.current.protocols,\n maxRetries,\n initialBackoff,\n maxBackoff,\n pingInterval,\n pingPayload: optionsRef.current.pingPayload,\n respondToPing,\n pongPayload: optionsRef.current.pongPayload,\n queueWhileClosed,\n maxQueuedMessages,\n parser: (raw) => (optionsRef.current.parser ?? defaultParse<T>)(raw),\n onStatusChange: setStatus,\n onOpen: (event) => optionsRef.current.onOpen?.(event),\n onClose: (event) => optionsRef.current.onClose?.(event),\n onError: (event) => optionsRef.current.onError?.(event),\n onMessage: (message) => {\n setLastMessage(message);\n optionsRef.current.onMessage?.(message);\n },\n });\n controllerRef.current = controller;\n\n return () => {\n controller.close();\n controllerRef.current = null;\n };\n }, [\n url,\n enabled,\n protocolsKey,\n maxRetries,\n initialBackoff,\n maxBackoff,\n pingInterval,\n respondToPing,\n queueWhileClosed,\n maxQueuedMessages,\n ]);\n\n const send = useCallback((payload: string | Blob | BufferSource): boolean => {\n return controllerRef.current?.send(payload) ?? false;\n }, []);\n\n const reconnect = useCallback((): void => {\n controllerRef.current?.reconnect();\n }, []);\n\n return { status, lastMessage, send, reconnect };\n}\n"],"mappings":"kEAiCA,SAAS,EAAgB,EAAgB,CACrC,GAAI,CACA,OAAO,KAAK,MAAM,CAAG,CACzB,MAAQ,CACJ,OAAO,CACX,CACJ,CAiBA,SAAgB,EACZ,EACA,EAAkC,CAAC,EACd,CACrB,GAAM,CACF,UAAU,GACV,YACA,aACA,iBACA,aACA,eACA,gBACA,mBACA,qBACA,EACE,CAAC,EAAQ,IAAA,EAAA,EAAA,SAAA,CAAuC,MAAM,EACtD,CAAC,EAAa,IAAA,EAAA,EAAA,SAAA,CAAuD,IAAI,EACzE,GAAA,EAAA,EAAA,OAAA,CAAmD,IAAI,EAEvD,GAAA,EAAA,EAAA,OAAA,CAAoB,CAAO,EA6DjC,OA5DA,EAAA,EAAA,UAAA,KAAgB,CACZ,EAAW,QAAU,CACzB,CAAC,GAID,EAAA,EAAA,UAAA,KAAgB,CACZ,GAAI,CAAC,GAAW,CAAC,EAAK,CAClB,EAAU,MAAM,EAChB,MACJ,CAEA,IAAM,EAAa,EAAA,gBAAmB,EAAK,CACvC,UAAW,EAAW,QAAQ,UAC9B,aACA,iBACA,aACA,eACA,YAAa,EAAW,QAAQ,YAChC,gBACA,YAAa,EAAW,QAAQ,YAChC,mBACA,oBACA,OAAS,IAAS,EAAW,QAAQ,QAAU,EAAA,CAAiB,CAAG,EACnE,eAAgB,EAChB,OAAS,GAAU,EAAW,QAAQ,SAAS,CAAK,EACpD,QAAU,GAAU,EAAW,QAAQ,UAAU,CAAK,EACtD,QAAU,GAAU,EAAW,QAAQ,UAAU,CAAK,EACtD,UAAY,GAAY,CACpB,EAAe,CAAO,EACtB,EAAW,QAAQ,YAAY,CAAO,CAC1C,CACJ,CAAC,EAGD,MAFA,GAAc,QAAU,MAEX,CACT,EAAW,MAAM,EACjB,EAAc,QAAU,IAC5B,CACJ,EAAG,CACC,EACA,EArCiB,MAAM,QAAQ,CAAS,EAAI,EAAU,KAAK,GAAG,EAAK,GAAa,GAuChF,EACA,EACA,EACA,EACA,EACA,EACA,CACJ,CAAC,EAUM,CAAE,SAAQ,cAAa,MAAA,EAAA,EAAA,YAAA,CARJ,GACf,EAAc,SAAS,KAAK,CAAO,GAAK,GAChD,CAAC,CAM0B,EAAM,WAAA,EAAA,EAAA,YAAA,KAJM,CACtC,EAAc,SAAS,UAAU,CACrC,EAAG,CAAC,CAEgC,CAAU,CAClD"}