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.
- package/README.md +3 -3
- package/dist/components/AIChat/AIChat.cjs +2 -0
- package/dist/components/AIChat/AIChat.cjs.map +1 -0
- package/dist/components/AIChat/AIChat.js +141 -0
- package/dist/components/AIChat/AIChat.js.map +1 -0
- package/dist/components/AIChat/AIChat.module.cjs +2 -0
- package/dist/components/AIChat/AIChat.module.cjs.map +1 -0
- package/dist/components/AIChat/AIChat.module.js +58 -0
- package/dist/components/AIChat/AIChat.module.js.map +1 -0
- package/dist/components/AIChat/AIChatComposer.cjs +2 -0
- package/dist/components/AIChat/AIChatComposer.cjs.map +1 -0
- package/dist/components/AIChat/AIChatComposer.js +87 -0
- package/dist/components/AIChat/AIChatComposer.js.map +1 -0
- package/dist/components/AIChat/AIChatTurn.cjs +3 -0
- package/dist/components/AIChat/AIChatTurn.cjs.map +1 -0
- package/dist/components/AIChat/AIChatTurn.js +217 -0
- package/dist/components/AIChat/AIChatTurn.js.map +1 -0
- package/dist/components/AIChat/ai-chat-turns.cjs +2 -0
- package/dist/components/AIChat/ai-chat-turns.cjs.map +1 -0
- package/dist/components/AIChat/ai-chat-turns.js +90 -0
- package/dist/components/AIChat/ai-chat-turns.js.map +1 -0
- package/dist/styles.css +1 -1
- package/dist/tempest-react-sdk.cjs +1 -1
- package/dist/tempest-react-sdk.d.ts +415 -9
- package/dist/tempest-react-sdk.js +118 -114
- package/dist/ws/create-web-socket.cjs +1 -1
- package/dist/ws/create-web-socket.cjs.map +1 -1
- package/dist/ws/create-web-socket.js +54 -37
- package/dist/ws/create-web-socket.js.map +1 -1
- package/dist/ws/use-web-socket.cjs +1 -1
- package/dist/ws/use-web-socket.cjs.map +1 -1
- package/dist/ws/use-web-socket.js +49 -18
- package/dist/ws/use-web-socket.js.map +1 -1
- package/package.json +1 -1
- package/template/src/lib/api.ts +27 -5
- 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 {
|
|
179
|
-
import {
|
|
180
|
-
import {
|
|
181
|
-
import {
|
|
182
|
-
import {
|
|
183
|
-
import {
|
|
184
|
-
import {
|
|
185
|
-
import {
|
|
186
|
-
import {
|
|
187
|
-
import {
|
|
188
|
-
import {
|
|
189
|
-
import {
|
|
190
|
-
import {
|
|
191
|
-
import {
|
|
192
|
-
import {
|
|
193
|
-
import {
|
|
194
|
-
import {
|
|
195
|
-
import {
|
|
196
|
-
import {
|
|
197
|
-
import {
|
|
198
|
-
import {
|
|
199
|
-
import {
|
|
200
|
-
import {
|
|
201
|
-
import {
|
|
202
|
-
import {
|
|
203
|
-
import {
|
|
204
|
-
import {
|
|
205
|
-
import {
|
|
206
|
-
import {
|
|
207
|
-
import {
|
|
208
|
-
import {
|
|
209
|
-
import {
|
|
210
|
-
import {
|
|
211
|
-
import {
|
|
212
|
-
import {
|
|
213
|
-
import {
|
|
214
|
-
import {
|
|
215
|
-
import {
|
|
216
|
-
import {
|
|
217
|
-
import {
|
|
218
|
-
import {
|
|
219
|
-
import {
|
|
220
|
-
import {
|
|
221
|
-
import {
|
|
222
|
-
import {
|
|
223
|
-
import {
|
|
224
|
-
import {
|
|
225
|
-
import {
|
|
226
|
-
import {
|
|
227
|
-
import {
|
|
228
|
-
import {
|
|
229
|
-
import {
|
|
230
|
-
import {
|
|
231
|
-
import {
|
|
232
|
-
import {
|
|
233
|
-
import {
|
|
234
|
-
import {
|
|
235
|
-
import {
|
|
236
|
-
import {
|
|
237
|
-
import {
|
|
238
|
-
import {
|
|
239
|
-
import {
|
|
240
|
-
import {
|
|
241
|
-
import {
|
|
242
|
-
import {
|
|
243
|
-
import {
|
|
244
|
-
import {
|
|
245
|
-
import {
|
|
246
|
-
import {
|
|
247
|
-
import {
|
|
248
|
-
import {
|
|
249
|
-
import {
|
|
250
|
-
import {
|
|
251
|
-
import {
|
|
252
|
-
import {
|
|
253
|
-
import {
|
|
254
|
-
import {
|
|
255
|
-
import {
|
|
256
|
-
import {
|
|
257
|
-
import {
|
|
258
|
-
import {
|
|
259
|
-
import {
|
|
260
|
-
import {
|
|
261
|
-
import {
|
|
262
|
-
import {
|
|
263
|
-
import {
|
|
264
|
-
import {
|
|
265
|
-
import {
|
|
266
|
-
import {
|
|
267
|
-
import {
|
|
268
|
-
import {
|
|
269
|
-
import {
|
|
270
|
-
import {
|
|
271
|
-
import {
|
|
272
|
-
import {
|
|
273
|
-
import {
|
|
274
|
-
import {
|
|
275
|
-
import {
|
|
276
|
-
import {
|
|
277
|
-
import {
|
|
278
|
-
import {
|
|
279
|
-
import {
|
|
280
|
-
import {
|
|
281
|
-
import {
|
|
282
|
-
import {
|
|
283
|
-
import {
|
|
284
|
-
import {
|
|
285
|
-
import {
|
|
286
|
-
import {
|
|
287
|
-
import {
|
|
288
|
-
import {
|
|
289
|
-
import {
|
|
290
|
-
import {
|
|
291
|
-
|
|
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`}),
|
|
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" }),
|
|
11
|
-
function
|
|
12
|
-
|
|
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
|
|
15
|
-
|
|
17
|
+
function O(e) {
|
|
18
|
+
C !== e && (C = e, v?.(e));
|
|
16
19
|
}
|
|
17
|
-
function
|
|
18
|
-
|
|
19
|
-
|
|
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
|
|
23
|
-
if (
|
|
24
|
-
if (
|
|
25
|
-
|
|
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 **
|
|
29
|
-
|
|
34
|
+
let e = Math.min(a * 2 ** S, o);
|
|
35
|
+
S += 1, b = setTimeout(M, e);
|
|
30
36
|
}
|
|
31
|
-
function
|
|
32
|
-
if (
|
|
33
|
-
|
|
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
|
-
|
|
36
|
-
|
|
37
|
-
}, e.onmessage = (
|
|
38
|
-
let
|
|
39
|
-
|
|
40
|
-
data:
|
|
41
|
-
raw:
|
|
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
|
-
|
|
54
|
+
_?.(e);
|
|
45
55
|
}, e.onclose = (e) => {
|
|
46
|
-
|
|
56
|
+
k(), g?.(e), y = null, O("closed"), !w && !e.wasClean && j();
|
|
47
57
|
};
|
|
48
58
|
}
|
|
49
|
-
function
|
|
50
|
-
return
|
|
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
|
|
53
|
-
|
|
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
|
|
56
|
-
|
|
72
|
+
function I() {
|
|
73
|
+
b &&= (clearTimeout(b), null), S = 0, w = !1, M();
|
|
57
74
|
}
|
|
58
|
-
return
|
|
59
|
-
send:
|
|
60
|
-
close:
|
|
61
|
-
reconnect:
|
|
75
|
+
return M(), {
|
|
76
|
+
send: N,
|
|
77
|
+
close: P,
|
|
78
|
+
reconnect: I,
|
|
62
79
|
get status() {
|
|
63
|
-
return
|
|
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":";AAuEA,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,mBAAgB,IAChB,iBAAc,KAAK,UAAU,EAAE,MAAM,OAAO,CAAC,GAC7C,sBAAmB,IACnB,uBAAoB,KACpB,YAAS,GACT,WACA,cACA,YACA,YACA,sBACA,GAEA,IAA2B,MAC3B,IAAmD,MACnD,IAAmD,MACnD,IAAU,GACV,IAA0B,QAC1B,IAAS,IACP,IAA8C,CAAC;CAGrD,SAAS,EAAa,GAAwB;EAC1C,OACI,OAAO,KAAS,cAChB,KACC,EAA4B,SAAS;CAE9C;CAGA,SAAS,EAAY,GAAqB;EACtC,OAAO,EAAO,SAAS,KAAK,EAAG,eAAe,UAAU,OACpD,EAAG,KAAK,EAAO,MAAM,CAAE;CAE/B;CAEA,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;EACZ,IAAI,GAAQ;GACR,IAAM,IAAW;GAKjB,AAJA,EAAS,YAAY,MACrB,EAAS,UAAU,MACnB,EAAS,UAAU,MACf,EAAS,eAAe,UAAU,eAAY,EAAS,SAAS,OACpE,EAAY,CAAQ;EACxB;EACA,EAAU,YAAY;EAEtB,IAAM,IAAK,IAAI,UAAU,GAAK,CAAS;EAwBvC,AAvBA,IAAS,GAET,EAAG,UAAU,MAAU;GAKnB,AAJA,IAAU,GACV,EAAU,MAAM,GAChB,EAAU,GACV,EAAY,CAAE,GACd,IAAS,CAAK;EAClB,GAEA,EAAG,aAAa,MAAU;GACtB,IAAM,IAAM,OAAO,EAAM,QAAS,WAAW,EAAM,OAAO,IACpD,IAAO,EAAO,CAAG;GAIvB,AAHI,KAAiB,EAAa,CAAI,KAAK,EAAG,eAAe,UAAU,QACnE,EAAG,KAAK,CAAW,GAEvB,IAAY;IAAE;IAAM,KAAK;GAAM,CAAC;EACpC,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;EAQ1D,OAPI,GAAQ,eAAe,UAAU,QACjC,EAAO,KAAK,CAAO,GACZ,MAEP,CAAC,KAAoB,IAAe,MACpC,EAAO,UAAU,KAAmB,EAAO,MAAM,GACrD,EAAO,KAAK,CAAO,GACZ;CACX;CAEA,SAAS,EAAM,GAAe,GAAuB;EAcjD,AAbA,IAAS,IACT,AAEI,OADA,aAAa,CAAU,GACV,OAEjB,EAAU,GACV,IAAU,GACV,EAAO,SAAS,GAChB,AAGI,OAFA,EAAU,SAAS,GACnB,EAAY,GAAQ,GAAM,CAAM,GACvB,OAEb,EAAU,QAAQ;CACtB;CAaA,SAAS,EAAY,GAAe,GAAe,GAAuB;EACtE,IAAI,EAAG,eAAe,UAAU,YAAY;GAIxC,AAHA,EAAG,eAAe,EAAG,MAAM,GAAM,CAAM,GACvC,EAAG,YAAY,MACf,EAAG,UAAU,MACb,EAAG,UAAU;GACb;EACJ;EACA,EAAG,MAAM,GAAM,CAAM;CACzB;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,2 +1,2 @@
|
|
|
1
|
-
const e=require("./create-web-socket.cjs");let t=require("react");function 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
|
|
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"}
|