tempest-react-sdk 0.50.0 → 0.52.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/dist/audio/sfx-pool.cjs +1 -1
- package/dist/audio/sfx-pool.cjs.map +1 -1
- package/dist/audio/sfx-pool.js +36 -36
- package/dist/audio/sfx-pool.js.map +1 -1
- package/dist/auth/refresh-queue.cjs +1 -1
- package/dist/auth/refresh-queue.cjs.map +1 -1
- package/dist/auth/refresh-queue.js +6 -6
- package/dist/auth/refresh-queue.js.map +1 -1
- package/dist/components/BarList/BarList.cjs +1 -1
- package/dist/components/BarList/BarList.cjs.map +1 -1
- package/dist/components/BarList/BarList.js +6 -1
- package/dist/components/BarList/BarList.js.map +1 -1
- package/dist/components/BarList/bar-list-model.cjs +1 -1
- package/dist/components/BarList/bar-list-model.cjs.map +1 -1
- package/dist/components/BarList/bar-list-model.js +8 -8
- package/dist/components/BarList/bar-list-model.js.map +1 -1
- package/dist/components/Markdown/markdown-parse.cjs +5 -5
- package/dist/components/Markdown/markdown-parse.cjs.map +1 -1
- package/dist/components/Markdown/markdown-parse.js +66 -63
- package/dist/components/Markdown/markdown-parse.js.map +1 -1
- package/dist/components/Scheduler/Scheduler.cjs +1 -1
- package/dist/components/Scheduler/Scheduler.cjs.map +1 -1
- package/dist/components/Scheduler/Scheduler.js +1 -1
- package/dist/components/Scheduler/Scheduler.js.map +1 -1
- package/dist/components/Scheduler/Scheduler.module.cjs.map +1 -1
- package/dist/components/Scheduler/Scheduler.module.js.map +1 -1
- package/dist/components/Sidebar/Sidebar.cjs +1 -1
- package/dist/components/Sidebar/Sidebar.cjs.map +1 -1
- package/dist/components/Sidebar/Sidebar.js +25 -45
- package/dist/components/Sidebar/Sidebar.js.map +1 -1
- package/dist/components/Sidebar/Sidebar.module.cjs.map +1 -1
- package/dist/components/Sidebar/Sidebar.module.js.map +1 -1
- package/dist/components/Sidebar/SidebarEntry.cjs +2 -0
- package/dist/components/Sidebar/SidebarEntry.cjs.map +1 -0
- package/dist/components/Sidebar/SidebarEntry.js +40 -0
- package/dist/components/Sidebar/SidebarEntry.js.map +1 -0
- package/dist/http/api-client.cjs +1 -1
- package/dist/http/api-client.cjs.map +1 -1
- package/dist/http/api-client.js +84 -71
- package/dist/http/api-client.js.map +1 -1
- package/dist/http/describe-api-error.cjs +1 -1
- package/dist/http/describe-api-error.cjs.map +1 -1
- package/dist/http/describe-api-error.js +4 -2
- package/dist/http/describe-api-error.js.map +1 -1
- package/dist/http/timeout.cjs +2 -0
- package/dist/http/timeout.cjs.map +1 -0
- package/dist/http/timeout.js +22 -0
- package/dist/http/timeout.js.map +1 -0
- package/dist/http/use-describe-api-error.cjs +1 -1
- package/dist/http/use-describe-api-error.cjs.map +1 -1
- package/dist/http/use-describe-api-error.js +3 -2
- package/dist/http/use-describe-api-error.js.map +1 -1
- package/dist/icons/material-symbols.cjs.map +1 -1
- package/dist/icons/material-symbols.js.map +1 -1
- package/dist/offline/create-offline-database.cjs +2 -0
- package/dist/offline/create-offline-database.cjs.map +1 -0
- package/dist/offline/create-offline-database.js +29 -0
- package/dist/offline/create-offline-database.js.map +1 -0
- package/dist/offline/create-offline-store.cjs +1 -1
- package/dist/offline/create-offline-store.cjs.map +1 -1
- package/dist/offline/create-offline-store.js +29 -25
- package/dist/offline/create-offline-store.js.map +1 -1
- package/dist/styles.css +1 -1
- package/dist/tempest-react-sdk.cjs +1 -1
- package/dist/tempest-react-sdk.d.ts +286 -13
- package/dist/tempest-react-sdk.js +43 -42
- package/dist/utils/csv.cjs.map +1 -1
- package/dist/utils/csv.js.map +1 -1
- package/dist/utils/format.cjs +1 -1
- package/dist/utils/format.cjs.map +1 -1
- package/dist/utils/format.js +8 -3
- package/dist/utils/format.js.map +1 -1
- package/dist/vite/tempest-pwa-dev-sw.cjs +1 -1
- package/dist/vite/tempest-pwa-dev-sw.cjs.map +1 -1
- package/dist/vite/tempest-pwa-dev-sw.js +49 -41
- package/dist/vite/tempest-pwa-dev-sw.js.map +1 -1
- package/package.json +1 -1
|
@@ -289,45 +289,46 @@ import { createVideoRecorder as $o, isVideoRecordingSupported as es, pickVideoMi
|
|
|
289
289
|
import { useVideoRecorder as ns } from "./capture/use-video-recorder.js";
|
|
290
290
|
import { isScreenCaptureSupported as rs, useScreenCapture as is } from "./capture/use-screen-capture.js";
|
|
291
291
|
import { isSpeechRecognitionSupported as as, useSpeechRecognition as os } from "./capture/use-speech-recognition.js";
|
|
292
|
-
import {
|
|
293
|
-
import {
|
|
294
|
-
import {
|
|
295
|
-
import {
|
|
296
|
-
import {
|
|
297
|
-
import {
|
|
298
|
-
import {
|
|
299
|
-
import {
|
|
300
|
-
import {
|
|
301
|
-
import {
|
|
302
|
-
import {
|
|
303
|
-
import {
|
|
304
|
-
import {
|
|
305
|
-
import {
|
|
306
|
-
import {
|
|
307
|
-
import {
|
|
308
|
-
import {
|
|
309
|
-
import {
|
|
310
|
-
import {
|
|
311
|
-
import {
|
|
312
|
-
import {
|
|
313
|
-
import {
|
|
314
|
-
import {
|
|
315
|
-
import {
|
|
316
|
-
import {
|
|
317
|
-
import {
|
|
318
|
-
import {
|
|
319
|
-
import {
|
|
320
|
-
import {
|
|
321
|
-
import {
|
|
322
|
-
import {
|
|
323
|
-
import {
|
|
324
|
-
import {
|
|
325
|
-
import {
|
|
326
|
-
import {
|
|
327
|
-
import {
|
|
328
|
-
import {
|
|
329
|
-
import {
|
|
330
|
-
import {
|
|
331
|
-
import {
|
|
332
|
-
import {
|
|
333
|
-
export { Ar as AIChat, Dr as AIChatComposer, kr as AIChatTurn, dr as ALL_BARCODE_FORMATS, Bi as API_ERROR_OFFLINE_KEY, Vi as API_ERROR_VALIDATION_KEY, er as AUDIO_MIME_CANDIDATES, Ca as AccessControlProvider, t as Accordion, n as Alert, r as AppBar, Oo as AppProviders, io as AppRouter, o as AppShell, s as AspectRatio, Qn as AudioPlayer, ur as AudioRecorder, aa as AuthGuard, c as Avatar, l as AvatarGroup, i as BREAKPOINTS, u as Badge, d as Banner, Fr as BarList, yr as BarcodeScanner, f as BottomNavigation, p as BottomSheet, m as Breadcrumbs, oo as BrowserRouter, h as Button, ja as CACHE_TIME, bs as CEPInput, xs as CNPJInput, Ss as CPFInput, Mn as Calendar, Ea as Can, g as Card, Rn as Carousel, _ as Center, Yn as Chat, Jn as ChatComposer, v as Checkbox, y as ChipInput, hn as ClickOutside, ln as CodeBlock, En as Collapsible, b as Combobox, kn as Command, gn as ConditionalWrapper, S as ConfirmDialog, I as Container, Dn as ContextMenu, Es as Controller, cn as CopyButton, Hi as DEFAULT_API_ERROR_STRINGS, fr as DEFAULT_BARCODE_FORMATS, Gs as DEFAULT_CAR_SPEED_KMH, $i as DEFAULT_CHUNK_SIZE, Ks as DEFAULT_CIRCUITY_FACTOR, qs as DEFAULT_MODE_DURATION_FACTORS, ua as DEFAULT_PUB_KEY_CRED_PARAMS, Fc as DIVERGING_STEP_COUNT, bn as DataList, zn as DataTable, w as DatePicker, Fn as DateRangePicker, xn as DescriptionList, T as Divider, E as Drawer, D as DropdownMenu, O as Dropzone, Bs as EARTH_RADIUS_KM, k as EmptyState, Do as ErrorBoundary, A as ErrorState, vn as ErrorText, kc as FeatureFlagsProvider, j as FileUpload, Br as FilterBar, Vn as FloatingActionButton, _n as For, M as Form, N as FormActions, ds as FormField, Ds as FormProvider, P as FormRow, F as FormSection, ka as GoogleSignIn, L as Grid, so as HashRouter, St as Hide, On as HoverCard, Gi as I18nProvider, yn as Image, Jt as ImageCropper, C as Input, B as InstallBanner, V as InstallButton, rt as Kanban, at as Kbd, Tn as Label, Kn as Lightbox, co as Link, Bn as ListTile, Or as Markdown, Mr as Masonry, lo as MemoryRouter, Ln as Menubar, x as Modal, ot as ModalsProvider, dn as Money, Cs as MoneyInput, Pn as MultiSelect, lt as NProgressBar, uo as NavLink, ct as Navbar, fo as Navigate, In as NavigationMenu, Hn as NavigationRail, Qt as NotificationCenter, Ic as ORDINAL_START_STEP, Ze as OfflineIndicator, po as Outlet, dt as Page, ft as Pagination, da as PasskeyError, pt as PasswordInput, ws as PhoneInput, ht as PinInput, gt as Popover, mn as Portal, _t as Progress, rn as QRCapacityError, sn as QRCode, Ia as QueryProvider, Ma as REFETCH_TIME, vt as Radio, yt as RadioGroup, bt as RangeSlider, xt as RatingStars, Wn as RefreshIndicator, un as RelativeTime, jn as Resizable, mo as Route, ro as RouteGuard, ho as Routes, Lc as SEQUENTIAL_STEP_COUNT, Na as STALE_TIME, wt as SafeArea, Xt as Scheduler, An as ScrollArea, Tt as SearchBar, Et as SegmentedControl, Dt as Select, Ct as Show, Ot as Sidebar, qn as SignaturePad, kt as Skeleton, Nn as Slider, At as Spacer, Yt as Sparkline, jt as Spinner, R as Stack, Mt as Stat, Nt as Stepper, Rt as StepperInput, zt as Switch, tt as SyncStatusBadge, Vt as Table, Ht as Tabs, Bt as Tag, wc as TelemetryProvider, Fi as TempestApiError, Ja as TempestDataProvider, Ut as Textarea, To as ThemeProvider, Un as TimePicker, Wt as Timeline, Kt as ToastProvider, Sn as Toggle, Cn as ToggleGroup, wn as ToggleGroupItem, Gt as Tooltip, Nr as Tour, ic as TrajectoryMap, jr as Transfer, fn as TreeView, pn as TruncateText, nt as UpdatePrompt, en as VirtualList, nn as VirtualTable, et as VisuallyHidden, No as WebPushClient, Po as WebPushPermissionDeniedError, Fo as WebPushUnsupportedError, Gn as Wizard, br as aiChatStrings, se as announce, Jr as applyFilters, it as applyKanbanMove, ac as applyTheme, gi as assertNever, fa as base64UrlToBytes, Vs as bearingDeg, cr as blobToWav, Ys as boundingBox, Xs as boundsCenter, Pi as buildApiUrl, Pr as buildBarListRows, Ne as buildOpenInChromeIntent, pa as bytesToBase64Url, Vc as cachedResponseBytes, ni as camelCase, ri as capitalize, ci as chunk, Pt as clamp, Fs as clampLatitude, ma as classifyPasskeyError, ce as clearAnnouncer, Go as clearCaches, e as cn, tn as compareValues, Qr as compressToString, $r as compressedStorage, ei as compressedStorageCodec, Sc as consoleSink, Ec as consoleTelemetryAdapter, sc as contrastRatio, zi as createApiClient, qo as createAudioPlayer, tr as createAudioRecorder, ia as createAuthStore, pr as createBarcodeDetector, cc as createColorScale, qa as createDataProvider, ko as createEventStream, Nc as createGrowthBookFeatureFlagsAdapter, Wi as createI18n, Mc as createInMemoryFlags, Wc as createInferenceProfiler, Xr as createJsonStorage, Pc as createLaunchDarklyFeatureFlagsAdapter, ir as createLevelMeter, ea as createLocalUploadStorage, Cc as createLogger, $n as createMediaRecorder, tc as createOSRMBackend, Ga as createOfflineStore, ss as createOfflineSync, Bo as createPartialResponse, ha as createPasskeyClient, nc as createPositionTracker, Oc as createPostHogTelemetryAdapter, Pa as createQueryKeys, la as createRefreshQueue, ta as createResumableUpload, Da as createRoleAccessControl, wo as createSelectors, Dc as createSentryTelemetryAdapter, Zo as createSfxPool, Co as createStore, Sa as createTempestAuth, gc as createTheme, $o as createVideoRecorder, Ns as createWebSocket, xi as debounce, oa as decodeJWT, ti as decompressFromString, fi as deepMerge, ao as defineRoutes, Ui as describeApiError, Ir as describeFilter, Rc as divergingScale, Mi as downloadCsv, La as emptyOffsetPage, an as encodeQR, lr as encodeWav, mt as estimatePasswordStrength, Ue as estimateStorage, Js as estimateTravel, Zs as expandBounds, Lr as filtersFromSearchParams, Yr as filtersToQueryParams, Rr as filtersToSearchParams, Qs as fitProjection, Ft as formatBytes, hs as formatCEP, gs as formatCNPJ, Vr as formatCPF, It as formatCompactNumber, Hr as formatCurrency, Ur as formatDate, Wr as formatDateForInput, Gr as formatDateTime, Uc as formatDurationMs, Kr as formatPercent, qr as formatPhone, Qi as generateIdempotencyKey, bc as getInitialTheme, mr as getSupportedBarcodeFormats, vc as getThemePreset, li as groupBy, Hs as haversineKm, lc as hexToOklch, uc as hexToRgb, dc as hexToRgbaString, cs as higherVersionWins, Ko as inspectCaches, Uo as installBackgroundSync, Lo as installNotificationClickHandler, Vo as installPrecache, Ro as installPushHandler, Ho as installRuntimeCache, zo as installSkipWaitingListener, Pe as isAndroid, Fe as isAndroidWithoutPromptApi, Ii as isApiError, Xn as isAudioOutputSelectionSupported, nr as isAudioRecordingSupported, hr as isBarcodeDetectionSupported, ga as isConditionalMediationAvailable, Is as isCoordinate, Ra as isCursorPage, _i as isDefined, pi as isEmpty, xr as isGenerating, Ie as isIOS, sa as isJWTExpired, or as isMediaCaptureSupported, vi as isNumber, za as isOffsetPage, _a as isPasskeySupported, yi as isPlainObject, va as isPlatformAuthenticatorAvailable, jo as isPushSupported, Li as isRetriableStatus, rs as isScreenCaptureSupported, ki as isShareSupported, as as isSpeechRecognitionSupported, Le as isStandalone, bi as isString, Ls as isValidLatitude, Rs as isValidLongitude, es as isVideoRecordingSupported, ii as kebabCase, Sr as lastAssistantId, ls as lastWriteWins, ca as lazyWithRetry, Xa as listQueryKey, on as matrixToPath, Si as memoizeOne, Ke as moveItem, gr as normalizeBarcode, zs as normalizeLongitude, ut as nprogress, fc as oklchToHex, mi as omit, Ci as once, Za as oneQueryKey, zr as operatorsFor, Xi as parseResponse, Us as pathLengthKm, Lt as percentOf, Oa as permissionsFromToken, Ka as persistQueryClientOffline, hi as pick, rr as pickAudioMimeType, ts as pickVideoMimeType, Jo as playAudio, ai as pluralize, $s as projectMercator, Di as randomId, ui as range, Hc as readDeviceProfile, oc as readThemeToken, pc as readableForeground, go as redirect, Wo as registerPeriodicSync, ze as registerServiceWorker, mc as relativeLuminance, Zt as relativeTime, Ua as removeById, We as requestPersistentStorage, Ri as retry, hc as rgbToHex, Cr as roleLabel, zc as scaleSteps, Bc as sequentialScale, Zn as setAudioOutput, Ai as share, ji as shareOrDownloadBlob, Fa as shouldRetryQuery, Be as skipWaiting, Ti as sleep, oi as slugify, Yo as stopAudio, Zr as storage, wr as tailSignature, _c as themeContrast, xc as themeInitScript, yc as themePresets, wi as throttle, Ni as toCsv, Ws as toRadians, si as truncate, Tr as turnTime, di as uniqueBy, _s as unmask, ec as unprojectMercator, Ve as unregisterAllServiceWorkers, na as uploadFingerprint, Zi as uploadWithProgress, Wa as upsertById, Mo as urlBase64ToUint8Array, wa as useAccessControl, le as useAnnounce, X as useAsync, Xo as useAudio, ar as useAudioRecorder, vr as useBarcodeScanner, z as useBeforeInstallPrompt, a as useBreakpoint, Ta as useCan, ke as useClickOutside, W as useClientFilter, te as useClipboard, Se as useCountdown, xe as useCounter, Qa as useCreate, Va as useCursorQuery, Ya as useDataProvider, H as useDebounce, de as useDeepMemo, $a as useDelete, Yi as useDescribeApiError, ye as useDisclosure, we as useDocumentTitle, Q as useDocumentVisibility, us as useErrorHandler, q as useEventListener, Ao as useEventStream, Te as useFavicon, Ac as useFeatureFlag, Os as useFieldArray, jc as useFlagValue, oe as useFocusTrap, ks as useForm, As as useFormContext, js as useFormState, ie as useGeolocation, _e as useHover, Ki as useI18n, re as useIdle, Re as useInstallPrompt, $ as useIntersectionObserver, pe as useInterval, Ae as useIsFirstRender, ne as useKeyboardShortcut, K as useLatestRef, eo as useList, be as useListState, J as useLocalStorage, _o as useLocation, ve as useLongPress, Me as useLongPressHandlers, Ee as useMap, vo as useMatch, Xe as useMediaDevices, Ye as useMediaPermission, G as useMediaQuery, sr as useMicrophone, st as useModals, yo as useNavigate, $t as useNotificationInbox, Aa as useOAuthCallback, je as useObjectUrl, Ha as useOfflineMutation, Qe as useOfflineSync, to as useOne, Z as useOnline, qi as useOptionalI18n, Ba as usePaginatedQuery, U as usePagination, bo as useParams, ya as usePasskeyCapabilities, ba as usePasskeyRegistration, xa as usePasskeySignIn, ra as usePoll, rc as usePositionTracker, fe as usePrevious, Io as usePushSubscription, Oe as useQueue, ee as useResizeObserver, xo as useRouteError, is as useScreenCapture, ae as useScrollLock, Je as useScrollOverflow, So as useSearchParams, He as useServiceWorkerUpdate, De as useSet, Qo as useSfxPool, qe as useSortable, os as useSpeechRecognition, ue as useStableCallback, Ge as useStorageEstimate, $e as useSyncStatus, Tc as useTelemetry, Eo as useTheme, he as useThrottle, me as useTimeout, qt as useToast, Y as useToggle, _r as useTorch, Ji as useTranslate, Ce as useTypewriter, no as useUpdate, Ts as useViaCEP, ns as useVideoRecorder, Ms as useWatch, Ps as useWebSocket, ge as useWindowSize, ms as useZodForm, vs as validateCNPJ, ys as validateCPF, fs as validateForm, Er as visibleTurns, Ei as withTimeout, Oi as writeXlsx, ps as zodResolver };
|
|
292
|
+
import { createOfflineDatabase as ss } from "./offline/create-offline-database.js";
|
|
293
|
+
import { createOfflineSync as cs } from "./offline/create-offline-sync.js";
|
|
294
|
+
import { higherVersionWins as ls, lastWriteWins as us } from "./offline/conflict.js";
|
|
295
|
+
import { useErrorHandler as ds } from "./error-boundary/use-error-handler.js";
|
|
296
|
+
import { FormField as fs } from "./forms/FormField.js";
|
|
297
|
+
import { validateForm as ps } from "./forms/validate-form.js";
|
|
298
|
+
import { zodResolver as ms } from "./forms/zod-resolver.js";
|
|
299
|
+
import { useZodForm as hs } from "./forms/use-zod-form.js";
|
|
300
|
+
import { formatCEP as gs, formatCNPJ as _s, unmask as vs, validateCNPJ as ys, validateCPF as bs } from "./forms/br-validators.js";
|
|
301
|
+
import { CEPInput as xs, CNPJInput as Ss, CPFInput as Cs, MoneyInput as ws, PhoneInput as Ts } from "./forms/masked-inputs.js";
|
|
302
|
+
import { useViaCEP as Es } from "./forms/use-viacep.js";
|
|
303
|
+
import { Controller as Ds, FormProvider as Os, useFieldArray as ks, useForm as As, useFormContext as js, useFormState as Ms, useWatch as Ns } from "./forms/index.js";
|
|
304
|
+
import { createWebSocket as Ps } from "./ws/create-web-socket.js";
|
|
305
|
+
import { useWebSocket as Fs } from "./ws/use-web-socket.js";
|
|
306
|
+
import { clampLatitude as Is, isCoordinate as Ls, isValidLatitude as Rs, isValidLongitude as zs, normalizeLongitude as Bs } from "./geo/types.js";
|
|
307
|
+
import { EARTH_RADIUS_KM as Vs, bearingDeg as Hs, haversineKm as Us, pathLengthKm as Ws, toRadians as Gs } from "./geo/distance.js";
|
|
308
|
+
import { DEFAULT_CAR_SPEED_KMH as Ks, DEFAULT_CIRCUITY_FACTOR as qs, DEFAULT_MODE_DURATION_FACTORS as Js, estimateTravel as Ys } from "./geo/estimate.js";
|
|
309
|
+
import { boundingBox as Xs, boundsCenter as Zs, expandBounds as Qs } from "./geo/bounds.js";
|
|
310
|
+
import { fitProjection as $s, projectMercator as ec, unprojectMercator as tc } from "./geo/projection.js";
|
|
311
|
+
import { createOSRMBackend as nc } from "./geo/routing.js";
|
|
312
|
+
import { createPositionTracker as rc } from "./geo/create-position-tracker.js";
|
|
313
|
+
import { usePositionTracker as ic } from "./geo/use-position-tracker.js";
|
|
314
|
+
import { TrajectoryMap as ac } from "./geo/TrajectoryMap.js";
|
|
315
|
+
import { applyTheme as oc, readThemeToken as sc } from "./theme/apply-theme.js";
|
|
316
|
+
import { contrastRatio as cc, createColorScale as lc, hexToOklch as uc, hexToRgb as dc, hexToRgbaString as fc, oklchToHex as pc, readableForeground as mc, relativeLuminance as hc, rgbToHex as gc } from "./theme/color.js";
|
|
317
|
+
import { createTheme as _c, themeContrast as vc } from "./theme/create-theme.js";
|
|
318
|
+
import { getThemePreset as yc, themePresets as bc } from "./theme/theme-presets.js";
|
|
319
|
+
import { getInitialTheme as xc, themeInitScript as Sc } from "./theme/initial-theme.js";
|
|
320
|
+
import { consoleSink as Cc, createLogger as wc } from "./logger/logger.js";
|
|
321
|
+
import { TelemetryProvider as Tc, useTelemetry as Ec } from "./telemetry/TelemetryProvider.js";
|
|
322
|
+
import { consoleTelemetryAdapter as Dc } from "./telemetry/console-adapter.js";
|
|
323
|
+
import { createSentryTelemetryAdapter as Oc } from "./telemetry/sentry-adapter.js";
|
|
324
|
+
import { createPostHogTelemetryAdapter as kc } from "./telemetry/posthog-adapter.js";
|
|
325
|
+
import { FeatureFlagsProvider as Ac, useFeatureFlag as jc, useFlagValue as Mc } from "./feature-flags/FeatureFlagsProvider.js";
|
|
326
|
+
import { createInMemoryFlags as Nc } from "./feature-flags/in-memory-adapter.js";
|
|
327
|
+
import { createGrowthBookFeatureFlagsAdapter as Pc } from "./feature-flags/growthbook-adapter.js";
|
|
328
|
+
import { createLaunchDarklyFeatureFlagsAdapter as Fc } from "./feature-flags/launchdarkly-adapter.js";
|
|
329
|
+
import { DIVERGING_STEP_COUNT as Ic, ORDINAL_START_STEP as Lc, SEQUENTIAL_STEP_COUNT as Rc, divergingScale as zc, scaleSteps as Bc, sequentialScale as Vc } from "./charts/scales.js";
|
|
330
|
+
import { cachedResponseBytes as Hc } from "./perf/cache-size.js";
|
|
331
|
+
import { readDeviceProfile as Uc } from "./perf/device.js";
|
|
332
|
+
import { formatDurationMs as Wc } from "./perf/format.js";
|
|
333
|
+
import { createInferenceProfiler as Gc } from "./perf/profiler.js";
|
|
334
|
+
export { Ar as AIChat, Dr as AIChatComposer, kr as AIChatTurn, dr as ALL_BARCODE_FORMATS, Bi as API_ERROR_OFFLINE_KEY, Vi as API_ERROR_VALIDATION_KEY, er as AUDIO_MIME_CANDIDATES, Ca as AccessControlProvider, t as Accordion, n as Alert, r as AppBar, Oo as AppProviders, io as AppRouter, o as AppShell, s as AspectRatio, Qn as AudioPlayer, ur as AudioRecorder, aa as AuthGuard, c as Avatar, l as AvatarGroup, i as BREAKPOINTS, u as Badge, d as Banner, Fr as BarList, yr as BarcodeScanner, f as BottomNavigation, p as BottomSheet, m as Breadcrumbs, oo as BrowserRouter, h as Button, ja as CACHE_TIME, xs as CEPInput, Ss as CNPJInput, Cs as CPFInput, Mn as Calendar, Ea as Can, g as Card, Rn as Carousel, _ as Center, Yn as Chat, Jn as ChatComposer, v as Checkbox, y as ChipInput, hn as ClickOutside, ln as CodeBlock, En as Collapsible, b as Combobox, kn as Command, gn as ConditionalWrapper, S as ConfirmDialog, I as Container, Dn as ContextMenu, Ds as Controller, cn as CopyButton, Hi as DEFAULT_API_ERROR_STRINGS, fr as DEFAULT_BARCODE_FORMATS, Ks as DEFAULT_CAR_SPEED_KMH, $i as DEFAULT_CHUNK_SIZE, qs as DEFAULT_CIRCUITY_FACTOR, Js as DEFAULT_MODE_DURATION_FACTORS, ua as DEFAULT_PUB_KEY_CRED_PARAMS, Ic as DIVERGING_STEP_COUNT, bn as DataList, zn as DataTable, w as DatePicker, Fn as DateRangePicker, xn as DescriptionList, T as Divider, E as Drawer, D as DropdownMenu, O as Dropzone, Vs as EARTH_RADIUS_KM, k as EmptyState, Do as ErrorBoundary, A as ErrorState, vn as ErrorText, Ac as FeatureFlagsProvider, j as FileUpload, Br as FilterBar, Vn as FloatingActionButton, _n as For, M as Form, N as FormActions, fs as FormField, Os as FormProvider, P as FormRow, F as FormSection, ka as GoogleSignIn, L as Grid, so as HashRouter, St as Hide, On as HoverCard, Gi as I18nProvider, yn as Image, Jt as ImageCropper, C as Input, B as InstallBanner, V as InstallButton, rt as Kanban, at as Kbd, Tn as Label, Kn as Lightbox, co as Link, Bn as ListTile, Or as Markdown, Mr as Masonry, lo as MemoryRouter, Ln as Menubar, x as Modal, ot as ModalsProvider, dn as Money, ws as MoneyInput, Pn as MultiSelect, lt as NProgressBar, uo as NavLink, ct as Navbar, fo as Navigate, In as NavigationMenu, Hn as NavigationRail, Qt as NotificationCenter, Lc as ORDINAL_START_STEP, Ze as OfflineIndicator, po as Outlet, dt as Page, ft as Pagination, da as PasskeyError, pt as PasswordInput, Ts as PhoneInput, ht as PinInput, gt as Popover, mn as Portal, _t as Progress, rn as QRCapacityError, sn as QRCode, Ia as QueryProvider, Ma as REFETCH_TIME, vt as Radio, yt as RadioGroup, bt as RangeSlider, xt as RatingStars, Wn as RefreshIndicator, un as RelativeTime, jn as Resizable, mo as Route, ro as RouteGuard, ho as Routes, Rc as SEQUENTIAL_STEP_COUNT, Na as STALE_TIME, wt as SafeArea, Xt as Scheduler, An as ScrollArea, Tt as SearchBar, Et as SegmentedControl, Dt as Select, Ct as Show, Ot as Sidebar, qn as SignaturePad, kt as Skeleton, Nn as Slider, At as Spacer, Yt as Sparkline, jt as Spinner, R as Stack, Mt as Stat, Nt as Stepper, Rt as StepperInput, zt as Switch, tt as SyncStatusBadge, Vt as Table, Ht as Tabs, Bt as Tag, Tc as TelemetryProvider, Fi as TempestApiError, Ja as TempestDataProvider, Ut as Textarea, To as ThemeProvider, Un as TimePicker, Wt as Timeline, Kt as ToastProvider, Sn as Toggle, Cn as ToggleGroup, wn as ToggleGroupItem, Gt as Tooltip, Nr as Tour, ac as TrajectoryMap, jr as Transfer, fn as TreeView, pn as TruncateText, nt as UpdatePrompt, en as VirtualList, nn as VirtualTable, et as VisuallyHidden, No as WebPushClient, Po as WebPushPermissionDeniedError, Fo as WebPushUnsupportedError, Gn as Wizard, br as aiChatStrings, se as announce, Jr as applyFilters, it as applyKanbanMove, oc as applyTheme, gi as assertNever, fa as base64UrlToBytes, Hs as bearingDeg, cr as blobToWav, Xs as boundingBox, Zs as boundsCenter, Pi as buildApiUrl, Pr as buildBarListRows, Ne as buildOpenInChromeIntent, pa as bytesToBase64Url, Hc as cachedResponseBytes, ni as camelCase, ri as capitalize, ci as chunk, Pt as clamp, Is as clampLatitude, ma as classifyPasskeyError, ce as clearAnnouncer, Go as clearCaches, e as cn, tn as compareValues, Qr as compressToString, $r as compressedStorage, ei as compressedStorageCodec, Cc as consoleSink, Dc as consoleTelemetryAdapter, cc as contrastRatio, zi as createApiClient, qo as createAudioPlayer, tr as createAudioRecorder, ia as createAuthStore, pr as createBarcodeDetector, lc as createColorScale, qa as createDataProvider, ko as createEventStream, Pc as createGrowthBookFeatureFlagsAdapter, Wi as createI18n, Nc as createInMemoryFlags, Gc as createInferenceProfiler, Xr as createJsonStorage, Fc as createLaunchDarklyFeatureFlagsAdapter, ir as createLevelMeter, ea as createLocalUploadStorage, wc as createLogger, $n as createMediaRecorder, nc as createOSRMBackend, ss as createOfflineDatabase, Ga as createOfflineStore, cs as createOfflineSync, Bo as createPartialResponse, ha as createPasskeyClient, rc as createPositionTracker, kc as createPostHogTelemetryAdapter, Pa as createQueryKeys, la as createRefreshQueue, ta as createResumableUpload, Da as createRoleAccessControl, wo as createSelectors, Oc as createSentryTelemetryAdapter, Zo as createSfxPool, Co as createStore, Sa as createTempestAuth, _c as createTheme, $o as createVideoRecorder, Ps as createWebSocket, xi as debounce, oa as decodeJWT, ti as decompressFromString, fi as deepMerge, ao as defineRoutes, Ui as describeApiError, Ir as describeFilter, zc as divergingScale, Mi as downloadCsv, La as emptyOffsetPage, an as encodeQR, lr as encodeWav, mt as estimatePasswordStrength, Ue as estimateStorage, Ys as estimateTravel, Qs as expandBounds, Lr as filtersFromSearchParams, Yr as filtersToQueryParams, Rr as filtersToSearchParams, $s as fitProjection, Ft as formatBytes, gs as formatCEP, _s as formatCNPJ, Vr as formatCPF, It as formatCompactNumber, Hr as formatCurrency, Ur as formatDate, Wr as formatDateForInput, Gr as formatDateTime, Wc as formatDurationMs, Kr as formatPercent, qr as formatPhone, Qi as generateIdempotencyKey, xc as getInitialTheme, mr as getSupportedBarcodeFormats, yc as getThemePreset, li as groupBy, Us as haversineKm, uc as hexToOklch, dc as hexToRgb, fc as hexToRgbaString, ls as higherVersionWins, Ko as inspectCaches, Uo as installBackgroundSync, Lo as installNotificationClickHandler, Vo as installPrecache, Ro as installPushHandler, Ho as installRuntimeCache, zo as installSkipWaitingListener, Pe as isAndroid, Fe as isAndroidWithoutPromptApi, Ii as isApiError, Xn as isAudioOutputSelectionSupported, nr as isAudioRecordingSupported, hr as isBarcodeDetectionSupported, ga as isConditionalMediationAvailable, Ls as isCoordinate, Ra as isCursorPage, _i as isDefined, pi as isEmpty, xr as isGenerating, Ie as isIOS, sa as isJWTExpired, or as isMediaCaptureSupported, vi as isNumber, za as isOffsetPage, _a as isPasskeySupported, yi as isPlainObject, va as isPlatformAuthenticatorAvailable, jo as isPushSupported, Li as isRetriableStatus, rs as isScreenCaptureSupported, ki as isShareSupported, as as isSpeechRecognitionSupported, Le as isStandalone, bi as isString, Rs as isValidLatitude, zs as isValidLongitude, es as isVideoRecordingSupported, ii as kebabCase, Sr as lastAssistantId, us as lastWriteWins, ca as lazyWithRetry, Xa as listQueryKey, on as matrixToPath, Si as memoizeOne, Ke as moveItem, gr as normalizeBarcode, Bs as normalizeLongitude, ut as nprogress, pc as oklchToHex, mi as omit, Ci as once, Za as oneQueryKey, zr as operatorsFor, Xi as parseResponse, Ws as pathLengthKm, Lt as percentOf, Oa as permissionsFromToken, Ka as persistQueryClientOffline, hi as pick, rr as pickAudioMimeType, ts as pickVideoMimeType, Jo as playAudio, ai as pluralize, ec as projectMercator, Di as randomId, ui as range, Uc as readDeviceProfile, sc as readThemeToken, mc as readableForeground, go as redirect, Wo as registerPeriodicSync, ze as registerServiceWorker, hc as relativeLuminance, Zt as relativeTime, Ua as removeById, We as requestPersistentStorage, Ri as retry, gc as rgbToHex, Cr as roleLabel, Bc as scaleSteps, Vc as sequentialScale, Zn as setAudioOutput, Ai as share, ji as shareOrDownloadBlob, Fa as shouldRetryQuery, Be as skipWaiting, Ti as sleep, oi as slugify, Yo as stopAudio, Zr as storage, wr as tailSignature, vc as themeContrast, Sc as themeInitScript, bc as themePresets, wi as throttle, Ni as toCsv, Gs as toRadians, si as truncate, Tr as turnTime, di as uniqueBy, vs as unmask, tc as unprojectMercator, Ve as unregisterAllServiceWorkers, na as uploadFingerprint, Zi as uploadWithProgress, Wa as upsertById, Mo as urlBase64ToUint8Array, wa as useAccessControl, le as useAnnounce, X as useAsync, Xo as useAudio, ar as useAudioRecorder, vr as useBarcodeScanner, z as useBeforeInstallPrompt, a as useBreakpoint, Ta as useCan, ke as useClickOutside, W as useClientFilter, te as useClipboard, Se as useCountdown, xe as useCounter, Qa as useCreate, Va as useCursorQuery, Ya as useDataProvider, H as useDebounce, de as useDeepMemo, $a as useDelete, Yi as useDescribeApiError, ye as useDisclosure, we as useDocumentTitle, Q as useDocumentVisibility, ds as useErrorHandler, q as useEventListener, Ao as useEventStream, Te as useFavicon, jc as useFeatureFlag, ks as useFieldArray, Mc as useFlagValue, oe as useFocusTrap, As as useForm, js as useFormContext, Ms as useFormState, ie as useGeolocation, _e as useHover, Ki as useI18n, re as useIdle, Re as useInstallPrompt, $ as useIntersectionObserver, pe as useInterval, Ae as useIsFirstRender, ne as useKeyboardShortcut, K as useLatestRef, eo as useList, be as useListState, J as useLocalStorage, _o as useLocation, ve as useLongPress, Me as useLongPressHandlers, Ee as useMap, vo as useMatch, Xe as useMediaDevices, Ye as useMediaPermission, G as useMediaQuery, sr as useMicrophone, st as useModals, yo as useNavigate, $t as useNotificationInbox, Aa as useOAuthCallback, je as useObjectUrl, Ha as useOfflineMutation, Qe as useOfflineSync, to as useOne, Z as useOnline, qi as useOptionalI18n, Ba as usePaginatedQuery, U as usePagination, bo as useParams, ya as usePasskeyCapabilities, ba as usePasskeyRegistration, xa as usePasskeySignIn, ra as usePoll, ic as usePositionTracker, fe as usePrevious, Io as usePushSubscription, Oe as useQueue, ee as useResizeObserver, xo as useRouteError, is as useScreenCapture, ae as useScrollLock, Je as useScrollOverflow, So as useSearchParams, He as useServiceWorkerUpdate, De as useSet, Qo as useSfxPool, qe as useSortable, os as useSpeechRecognition, ue as useStableCallback, Ge as useStorageEstimate, $e as useSyncStatus, Ec as useTelemetry, Eo as useTheme, he as useThrottle, me as useTimeout, qt as useToast, Y as useToggle, _r as useTorch, Ji as useTranslate, Ce as useTypewriter, no as useUpdate, Es as useViaCEP, ns as useVideoRecorder, Ns as useWatch, Fs as useWebSocket, ge as useWindowSize, hs as useZodForm, ys as validateCNPJ, bs as validateCPF, ps as validateForm, Er as visibleTurns, Ei as withTimeout, Oi as writeXlsx, ms as zodResolver };
|
package/dist/utils/csv.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"csv.cjs","names":[],"sources":["../../src/utils/csv.ts"],"sourcesContent":["// CSV that survives contact with real data. Every app writes this by hand and\n// every hand-written version gets the same two things wrong: a value containing\n// the delimiter splits the row, and a value containing a quote breaks the quoting\n// it was supposed to be protected by. Both are one-liners in RFC 4180 and neither\n// is obvious until a customer's name has a comma in it.\n\nimport { shareOrDownloadBlob } from \"../share/share-or-download\";\n\n/**\n * Byte order mark, as the character `String.prototype` sees it.\n *\n * Excel on a pt-BR install reads a BOM-less UTF-8 file as Latin-1 and turns\n * every accented name into mojibake. It is three bytes to avoid a support\n * ticket, so it is on by default.\n */\nconst BOM = \"\\uFEFF\";\n\n/** One column of the exported file. */\nexport interface CsvColumn<T> {\n /** Property of the row this column reads from. Doubles as the column key. */\n key: keyof T;\n /** Column heading, written to the first line. */\n header: string;\n /**\n * Value for the file. Defaults to `String(row[key])`, with nullish becoming an\n * empty field.\n *\n * A `DataTableColumn` renders cells to `ReactNode`, which cannot be written to\n * a text file — a badge or a link would serialize as `[object Object]`. Give\n * the column this accessor and the export says what the badge said.\n */\n csv?: (row: T) => string | number | boolean | null | undefined;\n}\n\n/** Options for {@link toCsv}. */\nexport interface CsvOptions {\n /**\n * Field separator. Default `\",\"`.\n *\n * Use `\";\"` for Excel on a locale whose decimal separator is the comma —\n * which is every pt-BR install — otherwise it opens the file in one column.\n */\n delimiter?: \",\" | \";\";\n /** Prefix the output with a UTF-8 BOM. Default `true`. */\n bom?: boolean;\n}\n\n/**\n * Quote one field per RFC 4180.\n *\n * A field is quoted when it contains the delimiter, a double quote, or a line\n * break; inside a quoted field, each double quote is doubled. Fields that need\n * none of this are written bare, which keeps the common file readable.\n *\n * @param value - The already-stringified field.\n * @param delimiter - The separator in use, which decides part of the quoting.\n * @returns The field, quoted if it has to be.\n */\nfunction escapeField(value: string, delimiter: string): string {\n const mustQuote =\n value.includes(delimiter) ||\n value.includes('\"') ||\n value.includes(\"\\n\") ||\n value.includes(\"\\r\");\n if (!mustQuote) return value;\n return `\"${value.replaceAll('\"', '\"\"')}\"`;\n}\n\n/**\n * Stringify one cell, keeping the difference between \"no value\" and \"zero\".\n *\n * `null` and `undefined` become an empty field; `0` and `false` are values\n * somebody chose and are written out. Getting this backwards is how an export\n * ends up under-reporting every row that legitimately holds a zero.\n *\n * @param value - The raw cell value.\n * @returns The text to write.\n */\nfunction cellText(value: unknown): string {\n if (value === null || value === undefined) return \"\";\n if (value instanceof Date) return value.toISOString();\n return String(value);\n}\n\n/**\n * Serialize rows to CSV text, RFC 4180 style.\n *\n * Rows are separated by `\\r\\n` — the RFC's terminator and the one Excel is least\n * surprised by. A row is emitted for the header even when `rows` is empty, so the\n * person who opens the file sees which columns they asked for instead of a blank\n * document.\n *\n * @example\n * const csv = toCsv(users, [\n * { key: \"name\", header: \"Nome\" },\n * { key: \"email\", header: \"E-mail\" },\n * { key: \"plan\", header: \"Plano\", csv: (user) => user.plan.label },\n * ]);\n *\n * @param rows - The rows to export.\n * @param columns - Columns, in the order they should appear.\n * @param options - Delimiter and BOM.\n * @returns The complete file contents.\n */\nexport function toCsv<T>(\n rows: readonly T[],\n columns: readonly CsvColumn<T>[],\n options: CsvOptions = {},\n): string {\n const { delimiter = \",\", bom = true } = options;\n\n const lines = [columns.map((column) => escapeField(column.header, delimiter)).join(delimiter)];\n\n for (const row of rows) {\n const cells = columns.map((column) => {\n const raw = column.csv\n ? column.csv(row)\n : (row as Record<string, unknown>)[column.key as string];\n return escapeField(cellText(raw), delimiter);\n });\n lines.push(cells.join(delimiter));\n }\n\n return `${bom ? BOM : \"\"}${lines.join(\"\\r\\n\")}`;\n}\n\n/**\n * Build a CSV and hand it to the user.\n *\n * Goes through {@link shareOrDownloadBlob}, so on a phone it opens the native\n * share sheet and everywhere else it downloads — the same path every other\n * generated artifact in the SDK takes, instead of a fourth hand-rolled `<a\n * download>`.\n *\n * @example\n * await downloadCsv(users, COLUMNS, \"usuarios.csv\");\n *\n * @param rows - The rows to export.\n * @param columns - Columns, in the order they should appear.\n * @param fileName - File name offered to the user. Default `\"export.csv\"`.\n * @param options - Delimiter and BOM, forwarded to {@link toCsv}.\n * @returns A promise that resolves once the share or download completes.\n */\nexport async function downloadCsv<T>(\n rows: readonly T[],\n columns: readonly CsvColumn<T>[],\n fileName = \"export.csv\",\n options: CsvOptions = {},\n): Promise<void> {\n const blob = new Blob([toCsv(rows, columns, options)], {\n type: \"text/csv;charset=utf-8\",\n });\n await shareOrDownloadBlob(blob, fileName);\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"csv.cjs","names":[],"sources":["../../src/utils/csv.ts"],"sourcesContent":["/**\n * @tempest-limits param-count — `downloadCsv(rows, columns, \"usuarios.csv\")` reads\n * in the order the sentence does, and its two trailing parameters have defaults,\n * so the common call passes three. Wrapping them in an object would break every\n * caller of a published export for no gain in what the call site says.\n */\n// CSV that survives contact with real data. Every app writes this by hand and\n// every hand-written version gets the same two things wrong: a value containing\n// the delimiter splits the row, and a value containing a quote breaks the quoting\n// it was supposed to be protected by. Both are one-liners in RFC 4180 and neither\n// is obvious until a customer's name has a comma in it.\n\nimport { shareOrDownloadBlob } from \"../share/share-or-download\";\n\n/**\n * Byte order mark, as the character `String.prototype` sees it.\n *\n * Excel on a pt-BR install reads a BOM-less UTF-8 file as Latin-1 and turns\n * every accented name into mojibake. It is three bytes to avoid a support\n * ticket, so it is on by default.\n */\nconst BOM = \"\\uFEFF\";\n\n/** One column of the exported file. */\nexport interface CsvColumn<T> {\n /** Property of the row this column reads from. Doubles as the column key. */\n key: keyof T;\n /** Column heading, written to the first line. */\n header: string;\n /**\n * Value for the file. Defaults to `String(row[key])`, with nullish becoming an\n * empty field.\n *\n * A `DataTableColumn` renders cells to `ReactNode`, which cannot be written to\n * a text file — a badge or a link would serialize as `[object Object]`. Give\n * the column this accessor and the export says what the badge said.\n */\n csv?: (row: T) => string | number | boolean | null | undefined;\n}\n\n/** Options for {@link toCsv}. */\nexport interface CsvOptions {\n /**\n * Field separator. Default `\",\"`.\n *\n * Use `\";\"` for Excel on a locale whose decimal separator is the comma —\n * which is every pt-BR install — otherwise it opens the file in one column.\n */\n delimiter?: \",\" | \";\";\n /** Prefix the output with a UTF-8 BOM. Default `true`. */\n bom?: boolean;\n}\n\n/**\n * Quote one field per RFC 4180.\n *\n * A field is quoted when it contains the delimiter, a double quote, or a line\n * break; inside a quoted field, each double quote is doubled. Fields that need\n * none of this are written bare, which keeps the common file readable.\n *\n * @param value - The already-stringified field.\n * @param delimiter - The separator in use, which decides part of the quoting.\n * @returns The field, quoted if it has to be.\n */\nfunction escapeField(value: string, delimiter: string): string {\n const mustQuote =\n value.includes(delimiter) ||\n value.includes('\"') ||\n value.includes(\"\\n\") ||\n value.includes(\"\\r\");\n if (!mustQuote) return value;\n return `\"${value.replaceAll('\"', '\"\"')}\"`;\n}\n\n/**\n * Stringify one cell, keeping the difference between \"no value\" and \"zero\".\n *\n * `null` and `undefined` become an empty field; `0` and `false` are values\n * somebody chose and are written out. Getting this backwards is how an export\n * ends up under-reporting every row that legitimately holds a zero.\n *\n * @param value - The raw cell value.\n * @returns The text to write.\n */\nfunction cellText(value: unknown): string {\n if (value === null || value === undefined) return \"\";\n if (value instanceof Date) return value.toISOString();\n return String(value);\n}\n\n/**\n * Serialize rows to CSV text, RFC 4180 style.\n *\n * Rows are separated by `\\r\\n` — the RFC's terminator and the one Excel is least\n * surprised by. A row is emitted for the header even when `rows` is empty, so the\n * person who opens the file sees which columns they asked for instead of a blank\n * document.\n *\n * @example\n * const csv = toCsv(users, [\n * { key: \"name\", header: \"Nome\" },\n * { key: \"email\", header: \"E-mail\" },\n * { key: \"plan\", header: \"Plano\", csv: (user) => user.plan.label },\n * ]);\n *\n * @param rows - The rows to export.\n * @param columns - Columns, in the order they should appear.\n * @param options - Delimiter and BOM.\n * @returns The complete file contents.\n */\nexport function toCsv<T>(\n rows: readonly T[],\n columns: readonly CsvColumn<T>[],\n options: CsvOptions = {},\n): string {\n const { delimiter = \",\", bom = true } = options;\n\n const lines = [columns.map((column) => escapeField(column.header, delimiter)).join(delimiter)];\n\n for (const row of rows) {\n const cells = columns.map((column) => {\n const raw = column.csv\n ? column.csv(row)\n : (row as Record<string, unknown>)[column.key as string];\n return escapeField(cellText(raw), delimiter);\n });\n lines.push(cells.join(delimiter));\n }\n\n return `${bom ? BOM : \"\"}${lines.join(\"\\r\\n\")}`;\n}\n\n/**\n * Build a CSV and hand it to the user.\n *\n * Goes through {@link shareOrDownloadBlob}, so on a phone it opens the native\n * share sheet and everywhere else it downloads — the same path every other\n * generated artifact in the SDK takes, instead of a fourth hand-rolled `<a\n * download>`.\n *\n * @example\n * await downloadCsv(users, COLUMNS, \"usuarios.csv\");\n *\n * @param rows - The rows to export.\n * @param columns - Columns, in the order they should appear.\n * @param fileName - File name offered to the user. Default `\"export.csv\"`.\n * @param options - Delimiter and BOM, forwarded to {@link toCsv}.\n * @returns A promise that resolves once the share or download completes.\n */\nexport async function downloadCsv<T>(\n rows: readonly T[],\n columns: readonly CsvColumn<T>[],\n fileName = \"export.csv\",\n options: CsvOptions = {},\n): Promise<void> {\n const blob = new Blob([toCsv(rows, columns, options)], {\n type: \"text/csv;charset=utf-8\",\n });\n await shareOrDownloadBlob(blob, fileName);\n}\n"],"mappings":"kDAqBA,IAAM,EAAM,IA2CZ,SAAS,EAAY,EAAe,EAA2B,CAO3D,OALI,EAAM,SAAS,CAAS,GACxB,EAAM,SAAS,GAAG,GAClB,EAAM,SAAS;CAAI,GACnB,EAAM,SAAS,IAAI,EAEhB,IAAI,EAAM,WAAW,IAAK,IAAI,EAAE,GADhB,CAE3B,CAYA,SAAS,EAAS,EAAwB,CAGtC,OAFI,GAAU,KAAoC,GAC9C,aAAiB,KAAa,EAAM,YAAY,EAC7C,OAAO,CAAK,CACvB,CAsBA,SAAgB,EACZ,EACA,EACA,EAAsB,CAAC,EACjB,CACN,GAAM,CAAE,YAAY,IAAK,MAAM,IAAS,EAElC,EAAQ,CAAC,EAAQ,IAAK,GAAW,EAAY,EAAO,OAAQ,CAAS,CAAC,CAAC,CAAC,KAAK,CAAS,CAAC,EAE7F,IAAK,IAAM,KAAO,EAAM,CACpB,IAAM,EAAQ,EAAQ,IAAK,GAIhB,EAAY,EAHP,EAAO,IACb,EAAO,IAAI,CAAG,EACb,EAAgC,EAAO,IACf,EAAG,CAAS,CAC9C,EACD,EAAM,KAAK,EAAM,KAAK,CAAS,CAAC,CACpC,CAEA,MAAO,GAAG,EAAM,EAAM,KAAK,EAAM,KAAK;CAAM,GAChD,CAmBA,eAAsB,EAClB,EACA,EACA,EAAW,aACX,EAAsB,CAAC,EACV,CACb,IAAM,EAAO,IAAI,KAAK,CAAC,EAAM,EAAM,EAAS,CAAO,CAAC,EAAG,CACnD,KAAM,wBACV,CAAC,EACD,MAAM,EAAA,oBAAoB,EAAM,CAAQ,CAC5C"}
|
package/dist/utils/csv.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"csv.js","names":[],"sources":["../../src/utils/csv.ts"],"sourcesContent":["// CSV that survives contact with real data. Every app writes this by hand and\n// every hand-written version gets the same two things wrong: a value containing\n// the delimiter splits the row, and a value containing a quote breaks the quoting\n// it was supposed to be protected by. Both are one-liners in RFC 4180 and neither\n// is obvious until a customer's name has a comma in it.\n\nimport { shareOrDownloadBlob } from \"../share/share-or-download\";\n\n/**\n * Byte order mark, as the character `String.prototype` sees it.\n *\n * Excel on a pt-BR install reads a BOM-less UTF-8 file as Latin-1 and turns\n * every accented name into mojibake. It is three bytes to avoid a support\n * ticket, so it is on by default.\n */\nconst BOM = \"\\uFEFF\";\n\n/** One column of the exported file. */\nexport interface CsvColumn<T> {\n /** Property of the row this column reads from. Doubles as the column key. */\n key: keyof T;\n /** Column heading, written to the first line. */\n header: string;\n /**\n * Value for the file. Defaults to `String(row[key])`, with nullish becoming an\n * empty field.\n *\n * A `DataTableColumn` renders cells to `ReactNode`, which cannot be written to\n * a text file — a badge or a link would serialize as `[object Object]`. Give\n * the column this accessor and the export says what the badge said.\n */\n csv?: (row: T) => string | number | boolean | null | undefined;\n}\n\n/** Options for {@link toCsv}. */\nexport interface CsvOptions {\n /**\n * Field separator. Default `\",\"`.\n *\n * Use `\";\"` for Excel on a locale whose decimal separator is the comma —\n * which is every pt-BR install — otherwise it opens the file in one column.\n */\n delimiter?: \",\" | \";\";\n /** Prefix the output with a UTF-8 BOM. Default `true`. */\n bom?: boolean;\n}\n\n/**\n * Quote one field per RFC 4180.\n *\n * A field is quoted when it contains the delimiter, a double quote, or a line\n * break; inside a quoted field, each double quote is doubled. Fields that need\n * none of this are written bare, which keeps the common file readable.\n *\n * @param value - The already-stringified field.\n * @param delimiter - The separator in use, which decides part of the quoting.\n * @returns The field, quoted if it has to be.\n */\nfunction escapeField(value: string, delimiter: string): string {\n const mustQuote =\n value.includes(delimiter) ||\n value.includes('\"') ||\n value.includes(\"\\n\") ||\n value.includes(\"\\r\");\n if (!mustQuote) return value;\n return `\"${value.replaceAll('\"', '\"\"')}\"`;\n}\n\n/**\n * Stringify one cell, keeping the difference between \"no value\" and \"zero\".\n *\n * `null` and `undefined` become an empty field; `0` and `false` are values\n * somebody chose and are written out. Getting this backwards is how an export\n * ends up under-reporting every row that legitimately holds a zero.\n *\n * @param value - The raw cell value.\n * @returns The text to write.\n */\nfunction cellText(value: unknown): string {\n if (value === null || value === undefined) return \"\";\n if (value instanceof Date) return value.toISOString();\n return String(value);\n}\n\n/**\n * Serialize rows to CSV text, RFC 4180 style.\n *\n * Rows are separated by `\\r\\n` — the RFC's terminator and the one Excel is least\n * surprised by. A row is emitted for the header even when `rows` is empty, so the\n * person who opens the file sees which columns they asked for instead of a blank\n * document.\n *\n * @example\n * const csv = toCsv(users, [\n * { key: \"name\", header: \"Nome\" },\n * { key: \"email\", header: \"E-mail\" },\n * { key: \"plan\", header: \"Plano\", csv: (user) => user.plan.label },\n * ]);\n *\n * @param rows - The rows to export.\n * @param columns - Columns, in the order they should appear.\n * @param options - Delimiter and BOM.\n * @returns The complete file contents.\n */\nexport function toCsv<T>(\n rows: readonly T[],\n columns: readonly CsvColumn<T>[],\n options: CsvOptions = {},\n): string {\n const { delimiter = \",\", bom = true } = options;\n\n const lines = [columns.map((column) => escapeField(column.header, delimiter)).join(delimiter)];\n\n for (const row of rows) {\n const cells = columns.map((column) => {\n const raw = column.csv\n ? column.csv(row)\n : (row as Record<string, unknown>)[column.key as string];\n return escapeField(cellText(raw), delimiter);\n });\n lines.push(cells.join(delimiter));\n }\n\n return `${bom ? BOM : \"\"}${lines.join(\"\\r\\n\")}`;\n}\n\n/**\n * Build a CSV and hand it to the user.\n *\n * Goes through {@link shareOrDownloadBlob}, so on a phone it opens the native\n * share sheet and everywhere else it downloads — the same path every other\n * generated artifact in the SDK takes, instead of a fourth hand-rolled `<a\n * download>`.\n *\n * @example\n * await downloadCsv(users, COLUMNS, \"usuarios.csv\");\n *\n * @param rows - The rows to export.\n * @param columns - Columns, in the order they should appear.\n * @param fileName - File name offered to the user. Default `\"export.csv\"`.\n * @param options - Delimiter and BOM, forwarded to {@link toCsv}.\n * @returns A promise that resolves once the share or download completes.\n */\nexport async function downloadCsv<T>(\n rows: readonly T[],\n columns: readonly CsvColumn<T>[],\n fileName = \"export.csv\",\n options: CsvOptions = {},\n): Promise<void> {\n const blob = new Blob([toCsv(rows, columns, options)], {\n type: \"text/csv;charset=utf-8\",\n });\n await shareOrDownloadBlob(blob, fileName);\n}\n"],"mappings":";;
|
|
1
|
+
{"version":3,"file":"csv.js","names":[],"sources":["../../src/utils/csv.ts"],"sourcesContent":["/**\n * @tempest-limits param-count — `downloadCsv(rows, columns, \"usuarios.csv\")` reads\n * in the order the sentence does, and its two trailing parameters have defaults,\n * so the common call passes three. Wrapping them in an object would break every\n * caller of a published export for no gain in what the call site says.\n */\n// CSV that survives contact with real data. Every app writes this by hand and\n// every hand-written version gets the same two things wrong: a value containing\n// the delimiter splits the row, and a value containing a quote breaks the quoting\n// it was supposed to be protected by. Both are one-liners in RFC 4180 and neither\n// is obvious until a customer's name has a comma in it.\n\nimport { shareOrDownloadBlob } from \"../share/share-or-download\";\n\n/**\n * Byte order mark, as the character `String.prototype` sees it.\n *\n * Excel on a pt-BR install reads a BOM-less UTF-8 file as Latin-1 and turns\n * every accented name into mojibake. It is three bytes to avoid a support\n * ticket, so it is on by default.\n */\nconst BOM = \"\\uFEFF\";\n\n/** One column of the exported file. */\nexport interface CsvColumn<T> {\n /** Property of the row this column reads from. Doubles as the column key. */\n key: keyof T;\n /** Column heading, written to the first line. */\n header: string;\n /**\n * Value for the file. Defaults to `String(row[key])`, with nullish becoming an\n * empty field.\n *\n * A `DataTableColumn` renders cells to `ReactNode`, which cannot be written to\n * a text file — a badge or a link would serialize as `[object Object]`. Give\n * the column this accessor and the export says what the badge said.\n */\n csv?: (row: T) => string | number | boolean | null | undefined;\n}\n\n/** Options for {@link toCsv}. */\nexport interface CsvOptions {\n /**\n * Field separator. Default `\",\"`.\n *\n * Use `\";\"` for Excel on a locale whose decimal separator is the comma —\n * which is every pt-BR install — otherwise it opens the file in one column.\n */\n delimiter?: \",\" | \";\";\n /** Prefix the output with a UTF-8 BOM. Default `true`. */\n bom?: boolean;\n}\n\n/**\n * Quote one field per RFC 4180.\n *\n * A field is quoted when it contains the delimiter, a double quote, or a line\n * break; inside a quoted field, each double quote is doubled. Fields that need\n * none of this are written bare, which keeps the common file readable.\n *\n * @param value - The already-stringified field.\n * @param delimiter - The separator in use, which decides part of the quoting.\n * @returns The field, quoted if it has to be.\n */\nfunction escapeField(value: string, delimiter: string): string {\n const mustQuote =\n value.includes(delimiter) ||\n value.includes('\"') ||\n value.includes(\"\\n\") ||\n value.includes(\"\\r\");\n if (!mustQuote) return value;\n return `\"${value.replaceAll('\"', '\"\"')}\"`;\n}\n\n/**\n * Stringify one cell, keeping the difference between \"no value\" and \"zero\".\n *\n * `null` and `undefined` become an empty field; `0` and `false` are values\n * somebody chose and are written out. Getting this backwards is how an export\n * ends up under-reporting every row that legitimately holds a zero.\n *\n * @param value - The raw cell value.\n * @returns The text to write.\n */\nfunction cellText(value: unknown): string {\n if (value === null || value === undefined) return \"\";\n if (value instanceof Date) return value.toISOString();\n return String(value);\n}\n\n/**\n * Serialize rows to CSV text, RFC 4180 style.\n *\n * Rows are separated by `\\r\\n` — the RFC's terminator and the one Excel is least\n * surprised by. A row is emitted for the header even when `rows` is empty, so the\n * person who opens the file sees which columns they asked for instead of a blank\n * document.\n *\n * @example\n * const csv = toCsv(users, [\n * { key: \"name\", header: \"Nome\" },\n * { key: \"email\", header: \"E-mail\" },\n * { key: \"plan\", header: \"Plano\", csv: (user) => user.plan.label },\n * ]);\n *\n * @param rows - The rows to export.\n * @param columns - Columns, in the order they should appear.\n * @param options - Delimiter and BOM.\n * @returns The complete file contents.\n */\nexport function toCsv<T>(\n rows: readonly T[],\n columns: readonly CsvColumn<T>[],\n options: CsvOptions = {},\n): string {\n const { delimiter = \",\", bom = true } = options;\n\n const lines = [columns.map((column) => escapeField(column.header, delimiter)).join(delimiter)];\n\n for (const row of rows) {\n const cells = columns.map((column) => {\n const raw = column.csv\n ? column.csv(row)\n : (row as Record<string, unknown>)[column.key as string];\n return escapeField(cellText(raw), delimiter);\n });\n lines.push(cells.join(delimiter));\n }\n\n return `${bom ? BOM : \"\"}${lines.join(\"\\r\\n\")}`;\n}\n\n/**\n * Build a CSV and hand it to the user.\n *\n * Goes through {@link shareOrDownloadBlob}, so on a phone it opens the native\n * share sheet and everywhere else it downloads — the same path every other\n * generated artifact in the SDK takes, instead of a fourth hand-rolled `<a\n * download>`.\n *\n * @example\n * await downloadCsv(users, COLUMNS, \"usuarios.csv\");\n *\n * @param rows - The rows to export.\n * @param columns - Columns, in the order they should appear.\n * @param fileName - File name offered to the user. Default `\"export.csv\"`.\n * @param options - Delimiter and BOM, forwarded to {@link toCsv}.\n * @returns A promise that resolves once the share or download completes.\n */\nexport async function downloadCsv<T>(\n rows: readonly T[],\n columns: readonly CsvColumn<T>[],\n fileName = \"export.csv\",\n options: CsvOptions = {},\n): Promise<void> {\n const blob = new Blob([toCsv(rows, columns, options)], {\n type: \"text/csv;charset=utf-8\",\n });\n await shareOrDownloadBlob(blob, fileName);\n}\n"],"mappings":";;AAqBA,IAAM,IAAM;AA2CZ,SAAS,EAAY,GAAe,GAA2B;CAO3D,OALI,EAAM,SAAS,CAAS,KACxB,EAAM,SAAS,IAAG,KAClB,EAAM,SAAS,IAAI,KACnB,EAAM,SAAS,IAAI,IAEhB,IAAI,EAAM,WAAW,MAAK,MAAI,EAAE,KADhB;AAE3B;AAYA,SAAS,EAAS,GAAwB;CAGtC,OAFI,KAAU,OAAoC,KAC9C,aAAiB,OAAa,EAAM,YAAY,IAC7C,OAAO,CAAK;AACvB;AAsBA,SAAgB,EACZ,GACA,GACA,IAAsB,CAAC,GACjB;CACN,IAAM,EAAE,eAAY,KAAK,SAAM,OAAS,GAElC,IAAQ,CAAC,EAAQ,KAAK,MAAW,EAAY,EAAO,QAAQ,CAAS,CAAC,CAAC,CAAC,KAAK,CAAS,CAAC;CAE7F,KAAK,IAAM,KAAO,GAAM;EACpB,IAAM,IAAQ,EAAQ,KAAK,MAIhB,EAAY,EAHP,EAAO,MACb,EAAO,IAAI,CAAG,IACb,EAAgC,EAAO,IACf,GAAG,CAAS,CAC9C;EACD,EAAM,KAAK,EAAM,KAAK,CAAS,CAAC;CACpC;CAEA,OAAO,GAAG,IAAM,IAAM,KAAK,EAAM,KAAK,MAAM;AAChD;AAmBA,eAAsB,EAClB,GACA,GACA,IAAW,cACX,IAAsB,CAAC,GACV;CACb,IAAM,IAAO,IAAI,KAAK,CAAC,EAAM,GAAM,GAAS,CAAO,CAAC,GAAG,EACnD,MAAM,yBACV,CAAC;CACD,MAAM,EAAoB,GAAM,CAAQ;AAC5C"}
|
package/dist/utils/format.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
function e(e){return new Intl.NumberFormat(`pt-BR`,{style:`currency`,currency:`BRL`}).format(e)}function t(e){let t=typeof e==`string`?new Date(e):e;return Number.isNaN(t.getTime())?``:new Intl.DateTimeFormat(`pt-BR`).format(t)}function n(e){if(typeof e==`string`&&/^\d{4}-\d{2}-\d{2}$/.test(e))return e;let t=typeof e==`string`?new Date(e):e;if(Number.isNaN(t.getTime()))return``;let n=`${t.getMonth()+1}`.padStart(2,`0`),r=`${t.getDate()}`.padStart(2,`0`);return`${t.getFullYear()}-${n}-${r}`}function r(e){let t=typeof e==`string`?new Date(e):e;return Number.isNaN(t.getTime())?``:new Intl.DateTimeFormat(`pt-BR`,{dateStyle:`short`,timeStyle:`short`}).format(t)}function i(e){let
|
|
1
|
+
function e(e){return new Intl.NumberFormat(`pt-BR`,{style:`currency`,currency:`BRL`}).format(e)}function t(e){let t=typeof e==`string`?new Date(e):e;return Number.isNaN(t.getTime())?``:new Intl.DateTimeFormat(`pt-BR`).format(t)}function n(e){if(typeof e==`string`&&/^\d{4}-\d{2}-\d{2}$/.test(e))return e;let t=typeof e==`string`?new Date(e):e;if(Number.isNaN(t.getTime()))return``;let n=`${t.getMonth()+1}`.padStart(2,`0`),r=`${t.getDate()}`.padStart(2,`0`);return`${t.getFullYear()}-${n}-${r}`}function r(e){let t=typeof e==`string`?new Date(e):e;return Number.isNaN(t.getTime())?``:new Intl.DateTimeFormat(`pt-BR`,{dateStyle:`short`,timeStyle:`short`}).format(t)}function i(e,t={}){let n=e.replace(/\D/g,``).slice(0,11);if(!t.mobile)return n.length<=10?n.replace(/(\d{2})(\d)/,`($1) $2`).replace(/(\d{4})(\d)/,`$1-$2`):n.replace(/(\d{2})(\d)/,`($1) $2`).replace(/(\d{5})(\d)/,`$1-$2`);if(n.length<=2)return n;let r=n.slice(0,2),i=n.slice(2);i[0]!==`9`&&(i=`9${i}`),i=i.slice(0,9);let a=i.slice(0,5),o=i.slice(5);return o?`(${r}) ${a}-${o}`:`(${r}) ${a}`}function a(e){return e.replace(/\D/g,``).slice(0,11).replace(/(\d{3})(\d)/,`$1.$2`).replace(/(\d{3})(\d)/,`$1.$2`).replace(/(\d{3})(\d{1,2})$/,`$1-$2`)}function o(e){return new Intl.NumberFormat(`pt-BR`,{style:`percent`,minimumFractionDigits:1,maximumFractionDigits:1}).format(e)}exports.formatCPF=a,exports.formatCurrency=e,exports.formatDate=t,exports.formatDateForInput=n,exports.formatDateTime=r,exports.formatPercent=o,exports.formatPhone=i;
|
|
2
2
|
//# sourceMappingURL=format.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"format.cjs","names":[],"sources":["../../src/utils/format.ts"],"sourcesContent":["/**\n * Format a number as Brazilian Real currency.\n *\n * @param value - The amount in BRL.\n * @returns A locale-formatted string, e.g. \"R$ 1.234,56\".\n */\nexport function formatCurrency(value: number): string {\n return new Intl.NumberFormat(\"pt-BR\", {\n style: \"currency\",\n currency: \"BRL\",\n }).format(value);\n}\n\n/**\n * Format an ISO date or Date instance as `dd/MM/yyyy`.\n *\n * @param value - ISO string or Date.\n * @returns Formatted date string, or empty string when input is invalid.\n */\nexport function formatDate(value: string | Date): string {\n const date = typeof value === \"string\" ? new Date(value) : value;\n if (Number.isNaN(date.getTime())) return \"\";\n return new Intl.DateTimeFormat(\"pt-BR\").format(date);\n}\n\n/**\n * Format an ISO date or Date instance as `yyyy-MM-dd`, the value an\n * `<input type=\"date\">` accepts.\n *\n * Built from the **local** calendar parts rather than `toISOString().slice(0, 10)`,\n * which is the reflex and which is wrong: `toISOString` converts to UTC first, so\n * anything after 21:00 in UTC-3 reports the next day and the form opens on the\n * wrong date. `formatDate` cannot fill this role because a date input rejects\n * `dd/MM/yyyy` outright.\n *\n * A value that is already `yyyy-MM-dd` is returned untouched, and that shortcut\n * is load-bearing rather than an optimisation: `new Date(\"2026-05-16\")` is parsed\n * as **UTC** midnight, which in UTC-3 is the 15th at 21:00, so round-tripping the\n * exact value a backend sent would move it back a day.\n *\n * @example\n * <input type=\"date\" defaultValue={formatDateForInput(order.createdAt)} />\n *\n * @param value - ISO string or Date.\n * @returns The `yyyy-MM-dd` value, or an empty string when the input is invalid —\n * which is what a date input reads as \"no value\", unlike `\"Invalid Date\"`.\n */\nexport function formatDateForInput(value: string | Date): string {\n if (typeof value === \"string\" && /^\\d{4}-\\d{2}-\\d{2}$/.test(value)) return value;\n const date = typeof value === \"string\" ? new Date(value) : value;\n if (Number.isNaN(date.getTime())) return \"\";\n const month = `${date.getMonth() + 1}`.padStart(2, \"0\");\n const day = `${date.getDate()}`.padStart(2, \"0\");\n return `${date.getFullYear()}-${month}-${day}`;\n}\n\n/**\n * Format an ISO date or Date instance as `dd/MM/yyyy HH:mm`.\n *\n * @param value - ISO string or Date.\n * @returns Formatted datetime string, or empty string when input is invalid.\n */\nexport function formatDateTime(value: string | Date): string {\n const date = typeof value === \"string\" ? new Date(value) : value;\n if (Number.isNaN(date.getTime())) return \"\";\n return new Intl.DateTimeFormat(\"pt-BR\", {\n dateStyle: \"short\",\n timeStyle: \"short\",\n }).format(date);\n}\n\n/**\n * Apply the Brazilian phone mask `(XX) XXXXX-XXXX` or `(XX) XXXX-XXXX`.\n *\n * @param value - Raw digits or partially masked string.\n * @returns Masked phone string.\n */\nexport function formatPhone(value: string): string {\n const digits = value.replace(/\\D/g, \"\").slice(0, 11);\n if (digits.length <= 10) {\n
|
|
1
|
+
{"version":3,"file":"format.cjs","names":[],"sources":["../../src/utils/format.ts"],"sourcesContent":["/**\n * Format a number as Brazilian Real currency.\n *\n * @param value - The amount in BRL.\n * @returns A locale-formatted string, e.g. \"R$ 1.234,56\".\n */\nexport function formatCurrency(value: number): string {\n return new Intl.NumberFormat(\"pt-BR\", {\n style: \"currency\",\n currency: \"BRL\",\n }).format(value);\n}\n\n/**\n * Format an ISO date or Date instance as `dd/MM/yyyy`.\n *\n * @param value - ISO string or Date.\n * @returns Formatted date string, or empty string when input is invalid.\n */\nexport function formatDate(value: string | Date): string {\n const date = typeof value === \"string\" ? new Date(value) : value;\n if (Number.isNaN(date.getTime())) return \"\";\n return new Intl.DateTimeFormat(\"pt-BR\").format(date);\n}\n\n/**\n * Format an ISO date or Date instance as `yyyy-MM-dd`, the value an\n * `<input type=\"date\">` accepts.\n *\n * Built from the **local** calendar parts rather than `toISOString().slice(0, 10)`,\n * which is the reflex and which is wrong: `toISOString` converts to UTC first, so\n * anything after 21:00 in UTC-3 reports the next day and the form opens on the\n * wrong date. `formatDate` cannot fill this role because a date input rejects\n * `dd/MM/yyyy` outright.\n *\n * A value that is already `yyyy-MM-dd` is returned untouched, and that shortcut\n * is load-bearing rather than an optimisation: `new Date(\"2026-05-16\")` is parsed\n * as **UTC** midnight, which in UTC-3 is the 15th at 21:00, so round-tripping the\n * exact value a backend sent would move it back a day.\n *\n * @example\n * <input type=\"date\" defaultValue={formatDateForInput(order.createdAt)} />\n *\n * @param value - ISO string or Date.\n * @returns The `yyyy-MM-dd` value, or an empty string when the input is invalid —\n * which is what a date input reads as \"no value\", unlike `\"Invalid Date\"`.\n */\nexport function formatDateForInput(value: string | Date): string {\n if (typeof value === \"string\" && /^\\d{4}-\\d{2}-\\d{2}$/.test(value)) return value;\n const date = typeof value === \"string\" ? new Date(value) : value;\n if (Number.isNaN(date.getTime())) return \"\";\n const month = `${date.getMonth() + 1}`.padStart(2, \"0\");\n const day = `${date.getDate()}`.padStart(2, \"0\");\n return `${date.getFullYear()}-${month}-${day}`;\n}\n\n/**\n * Format an ISO date or Date instance as `dd/MM/yyyy HH:mm`.\n *\n * @param value - ISO string or Date.\n * @returns Formatted datetime string, or empty string when input is invalid.\n */\nexport function formatDateTime(value: string | Date): string {\n const date = typeof value === \"string\" ? new Date(value) : value;\n if (Number.isNaN(date.getTime())) return \"\";\n return new Intl.DateTimeFormat(\"pt-BR\", {\n dateStyle: \"short\",\n timeStyle: \"short\",\n }).format(date);\n}\n\nexport interface FormatPhoneOptions {\n /**\n * Treat the number as a mobile line: insert the mandatory `9` after the area\n * code when it is missing, and group the subscriber part `5+4` from the\n * first digit typed instead of waiting for the eleventh.\n *\n * Default `false`, which keeps the length-based behaviour: `4+4` up to ten\n * digits, `5+4` at eleven.\n */\n mobile?: boolean;\n}\n\n/**\n * Apply the Brazilian phone mask `(XX) XXXXX-XXXX` or `(XX) XXXX-XXXX`.\n *\n * By default the grouping is decided by **length**, which is what a field\n * accepting both landlines and mobiles needs.\n *\n * `mobile: true` is for a field that only accepts mobile numbers, and it exists\n * because the default is wrong as an as-you-type mask there. Reading anything up\n * to ten digits as a landline puts the hyphen after the fourth subscriber digit,\n * so a half-typed mobile renders `(11) 9123-4`; it only becomes `(11) 91234-5`\n * once the eleventh digit lands. The separator visibly jumps backwards while the\n * user is still typing. With `mobile`, the same input reads `(11) 91234` and the\n * hyphen never moves. It also inserts the leading `9` every Brazilian mobile\n * carries, so a ten-digit number gets corrected rather than masked as a landline.\n *\n * @param value - Raw digits or partially masked string.\n * @param options - Masking options.\n * @returns Masked phone string.\n *\n * @example\n * formatPhone(\"1191234\"); // \"(11) 9123-4\"\n * formatPhone(\"1191234\", { mobile: true }); // \"(11) 91234\"\n * formatPhone(\"1112345678\", { mobile: true }); // \"(11) 91234-5678\" — 9 inserted\n */\nexport function formatPhone(value: string, options: FormatPhoneOptions = {}): string {\n const digits = value.replace(/\\D/g, \"\").slice(0, 11);\n\n if (!options.mobile) {\n if (digits.length <= 10) {\n return digits.replace(/(\\d{2})(\\d)/, \"($1) $2\").replace(/(\\d{4})(\\d)/, \"$1-$2\");\n }\n return digits.replace(/(\\d{2})(\\d)/, \"($1) $2\").replace(/(\\d{5})(\\d)/, \"$1-$2\");\n }\n\n if (digits.length <= 2) return digits;\n\n const area = digits.slice(0, 2);\n let subscriber = digits.slice(2);\n if (subscriber[0] !== \"9\") subscriber = `9${subscriber}`;\n subscriber = subscriber.slice(0, 9);\n\n const prefix = subscriber.slice(0, 5);\n const suffix = subscriber.slice(5);\n return suffix ? `(${area}) ${prefix}-${suffix}` : `(${area}) ${prefix}`;\n}\n\n/**\n * Apply the Brazilian CPF mask `XXX.XXX.XXX-XX`.\n *\n * @param value - Raw digits or partially masked string.\n * @returns Masked CPF string.\n */\nexport function formatCPF(value: string): string {\n return value\n .replace(/\\D/g, \"\")\n .slice(0, 11)\n .replace(/(\\d{3})(\\d)/, \"$1.$2\")\n .replace(/(\\d{3})(\\d)/, \"$1.$2\")\n .replace(/(\\d{3})(\\d{1,2})$/, \"$1-$2\");\n}\n\n/**\n * Format a fraction (0-1) as a percentage with one decimal.\n *\n * @param value - Fraction between 0 and 1.\n * @returns Formatted percent string, e.g. \"12,5%\".\n */\nexport function formatPercent(value: number): string {\n return new Intl.NumberFormat(\"pt-BR\", {\n style: \"percent\",\n minimumFractionDigits: 1,\n maximumFractionDigits: 1,\n }).format(value);\n}\n"],"mappings":"AAMA,SAAgB,EAAe,EAAuB,CAClD,OAAO,IAAI,KAAK,aAAa,QAAS,CAClC,MAAO,WACP,SAAU,KACd,CAAC,CAAC,CAAC,OAAO,CAAK,CACnB,CAQA,SAAgB,EAAW,EAA8B,CACrD,IAAM,EAAO,OAAO,GAAU,SAAW,IAAI,KAAK,CAAK,EAAI,EAE3D,OADI,OAAO,MAAM,EAAK,QAAQ,CAAC,EAAU,GAClC,IAAI,KAAK,eAAe,OAAO,CAAC,CAAC,OAAO,CAAI,CACvD,CAwBA,SAAgB,EAAmB,EAA8B,CAC7D,GAAI,OAAO,GAAU,UAAY,sBAAsB,KAAK,CAAK,EAAG,OAAO,EAC3E,IAAM,EAAO,OAAO,GAAU,SAAW,IAAI,KAAK,CAAK,EAAI,EAC3D,GAAI,OAAO,MAAM,EAAK,QAAQ,CAAC,EAAG,MAAO,GACzC,IAAM,EAAQ,GAAG,EAAK,SAAS,EAAI,IAAI,SAAS,EAAG,GAAG,EAChD,EAAM,GAAG,EAAK,QAAQ,IAAI,SAAS,EAAG,GAAG,EAC/C,MAAO,GAAG,EAAK,YAAY,EAAE,GAAG,EAAM,GAAG,GAC7C,CAQA,SAAgB,EAAe,EAA8B,CACzD,IAAM,EAAO,OAAO,GAAU,SAAW,IAAI,KAAK,CAAK,EAAI,EAE3D,OADI,OAAO,MAAM,EAAK,QAAQ,CAAC,EAAU,GAClC,IAAI,KAAK,eAAe,QAAS,CACpC,UAAW,QACX,UAAW,OACf,CAAC,CAAC,CAAC,OAAO,CAAI,CAClB,CAsCA,SAAgB,EAAY,EAAe,EAA8B,CAAC,EAAW,CACjF,IAAM,EAAS,EAAM,QAAQ,MAAO,EAAE,CAAC,CAAC,MAAM,EAAG,EAAE,EAEnD,GAAI,CAAC,EAAQ,OAIT,OAHI,EAAO,QAAU,GACV,EAAO,QAAQ,cAAe,SAAS,CAAC,CAAC,QAAQ,cAAe,OAAO,EAE3E,EAAO,QAAQ,cAAe,SAAS,CAAC,CAAC,QAAQ,cAAe,OAAO,EAGlF,GAAI,EAAO,QAAU,EAAG,OAAO,EAE/B,IAAM,EAAO,EAAO,MAAM,EAAG,CAAC,EAC1B,EAAa,EAAO,MAAM,CAAC,EAC3B,EAAW,KAAO,MAAK,EAAa,IAAI,KAC5C,EAAa,EAAW,MAAM,EAAG,CAAC,EAElC,IAAM,EAAS,EAAW,MAAM,EAAG,CAAC,EAC9B,EAAS,EAAW,MAAM,CAAC,EACjC,OAAO,EAAS,IAAI,EAAK,IAAI,EAAO,GAAG,IAAW,IAAI,EAAK,IAAI,GACnE,CAQA,SAAgB,EAAU,EAAuB,CAC7C,OAAO,EACF,QAAQ,MAAO,EAAE,CAAC,CAClB,MAAM,EAAG,EAAE,CAAC,CACZ,QAAQ,cAAe,OAAO,CAAC,CAC/B,QAAQ,cAAe,OAAO,CAAC,CAC/B,QAAQ,oBAAqB,OAAO,CAC7C,CAQA,SAAgB,EAAc,EAAuB,CACjD,OAAO,IAAI,KAAK,aAAa,QAAS,CAClC,MAAO,UACP,sBAAuB,EACvB,sBAAuB,CAC3B,CAAC,CAAC,CAAC,OAAO,CAAK,CACnB"}
|
package/dist/utils/format.js
CHANGED
|
@@ -23,9 +23,14 @@ function r(e) {
|
|
|
23
23
|
timeStyle: "short"
|
|
24
24
|
}).format(t);
|
|
25
25
|
}
|
|
26
|
-
function i(e) {
|
|
27
|
-
let
|
|
28
|
-
|
|
26
|
+
function i(e, t = {}) {
|
|
27
|
+
let n = e.replace(/\D/g, "").slice(0, 11);
|
|
28
|
+
if (!t.mobile) return n.length <= 10 ? n.replace(/(\d{2})(\d)/, "($1) $2").replace(/(\d{4})(\d)/, "$1-$2") : n.replace(/(\d{2})(\d)/, "($1) $2").replace(/(\d{5})(\d)/, "$1-$2");
|
|
29
|
+
if (n.length <= 2) return n;
|
|
30
|
+
let r = n.slice(0, 2), i = n.slice(2);
|
|
31
|
+
i[0] !== "9" && (i = `9${i}`), i = i.slice(0, 9);
|
|
32
|
+
let a = i.slice(0, 5), o = i.slice(5);
|
|
33
|
+
return o ? `(${r}) ${a}-${o}` : `(${r}) ${a}`;
|
|
29
34
|
}
|
|
30
35
|
function a(e) {
|
|
31
36
|
return e.replace(/\D/g, "").slice(0, 11).replace(/(\d{3})(\d)/, "$1.$2").replace(/(\d{3})(\d)/, "$1.$2").replace(/(\d{3})(\d{1,2})$/, "$1-$2");
|
package/dist/utils/format.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"format.js","names":[],"sources":["../../src/utils/format.ts"],"sourcesContent":["/**\n * Format a number as Brazilian Real currency.\n *\n * @param value - The amount in BRL.\n * @returns A locale-formatted string, e.g. \"R$ 1.234,56\".\n */\nexport function formatCurrency(value: number): string {\n return new Intl.NumberFormat(\"pt-BR\", {\n style: \"currency\",\n currency: \"BRL\",\n }).format(value);\n}\n\n/**\n * Format an ISO date or Date instance as `dd/MM/yyyy`.\n *\n * @param value - ISO string or Date.\n * @returns Formatted date string, or empty string when input is invalid.\n */\nexport function formatDate(value: string | Date): string {\n const date = typeof value === \"string\" ? new Date(value) : value;\n if (Number.isNaN(date.getTime())) return \"\";\n return new Intl.DateTimeFormat(\"pt-BR\").format(date);\n}\n\n/**\n * Format an ISO date or Date instance as `yyyy-MM-dd`, the value an\n * `<input type=\"date\">` accepts.\n *\n * Built from the **local** calendar parts rather than `toISOString().slice(0, 10)`,\n * which is the reflex and which is wrong: `toISOString` converts to UTC first, so\n * anything after 21:00 in UTC-3 reports the next day and the form opens on the\n * wrong date. `formatDate` cannot fill this role because a date input rejects\n * `dd/MM/yyyy` outright.\n *\n * A value that is already `yyyy-MM-dd` is returned untouched, and that shortcut\n * is load-bearing rather than an optimisation: `new Date(\"2026-05-16\")` is parsed\n * as **UTC** midnight, which in UTC-3 is the 15th at 21:00, so round-tripping the\n * exact value a backend sent would move it back a day.\n *\n * @example\n * <input type=\"date\" defaultValue={formatDateForInput(order.createdAt)} />\n *\n * @param value - ISO string or Date.\n * @returns The `yyyy-MM-dd` value, or an empty string when the input is invalid —\n * which is what a date input reads as \"no value\", unlike `\"Invalid Date\"`.\n */\nexport function formatDateForInput(value: string | Date): string {\n if (typeof value === \"string\" && /^\\d{4}-\\d{2}-\\d{2}$/.test(value)) return value;\n const date = typeof value === \"string\" ? new Date(value) : value;\n if (Number.isNaN(date.getTime())) return \"\";\n const month = `${date.getMonth() + 1}`.padStart(2, \"0\");\n const day = `${date.getDate()}`.padStart(2, \"0\");\n return `${date.getFullYear()}-${month}-${day}`;\n}\n\n/**\n * Format an ISO date or Date instance as `dd/MM/yyyy HH:mm`.\n *\n * @param value - ISO string or Date.\n * @returns Formatted datetime string, or empty string when input is invalid.\n */\nexport function formatDateTime(value: string | Date): string {\n const date = typeof value === \"string\" ? new Date(value) : value;\n if (Number.isNaN(date.getTime())) return \"\";\n return new Intl.DateTimeFormat(\"pt-BR\", {\n dateStyle: \"short\",\n timeStyle: \"short\",\n }).format(date);\n}\n\n/**\n * Apply the Brazilian phone mask `(XX) XXXXX-XXXX` or `(XX) XXXX-XXXX`.\n *\n * @param value - Raw digits or partially masked string.\n * @returns Masked phone string.\n */\nexport function formatPhone(value: string): string {\n const digits = value.replace(/\\D/g, \"\").slice(0, 11);\n if (digits.length <= 10) {\n
|
|
1
|
+
{"version":3,"file":"format.js","names":[],"sources":["../../src/utils/format.ts"],"sourcesContent":["/**\n * Format a number as Brazilian Real currency.\n *\n * @param value - The amount in BRL.\n * @returns A locale-formatted string, e.g. \"R$ 1.234,56\".\n */\nexport function formatCurrency(value: number): string {\n return new Intl.NumberFormat(\"pt-BR\", {\n style: \"currency\",\n currency: \"BRL\",\n }).format(value);\n}\n\n/**\n * Format an ISO date or Date instance as `dd/MM/yyyy`.\n *\n * @param value - ISO string or Date.\n * @returns Formatted date string, or empty string when input is invalid.\n */\nexport function formatDate(value: string | Date): string {\n const date = typeof value === \"string\" ? new Date(value) : value;\n if (Number.isNaN(date.getTime())) return \"\";\n return new Intl.DateTimeFormat(\"pt-BR\").format(date);\n}\n\n/**\n * Format an ISO date or Date instance as `yyyy-MM-dd`, the value an\n * `<input type=\"date\">` accepts.\n *\n * Built from the **local** calendar parts rather than `toISOString().slice(0, 10)`,\n * which is the reflex and which is wrong: `toISOString` converts to UTC first, so\n * anything after 21:00 in UTC-3 reports the next day and the form opens on the\n * wrong date. `formatDate` cannot fill this role because a date input rejects\n * `dd/MM/yyyy` outright.\n *\n * A value that is already `yyyy-MM-dd` is returned untouched, and that shortcut\n * is load-bearing rather than an optimisation: `new Date(\"2026-05-16\")` is parsed\n * as **UTC** midnight, which in UTC-3 is the 15th at 21:00, so round-tripping the\n * exact value a backend sent would move it back a day.\n *\n * @example\n * <input type=\"date\" defaultValue={formatDateForInput(order.createdAt)} />\n *\n * @param value - ISO string or Date.\n * @returns The `yyyy-MM-dd` value, or an empty string when the input is invalid —\n * which is what a date input reads as \"no value\", unlike `\"Invalid Date\"`.\n */\nexport function formatDateForInput(value: string | Date): string {\n if (typeof value === \"string\" && /^\\d{4}-\\d{2}-\\d{2}$/.test(value)) return value;\n const date = typeof value === \"string\" ? new Date(value) : value;\n if (Number.isNaN(date.getTime())) return \"\";\n const month = `${date.getMonth() + 1}`.padStart(2, \"0\");\n const day = `${date.getDate()}`.padStart(2, \"0\");\n return `${date.getFullYear()}-${month}-${day}`;\n}\n\n/**\n * Format an ISO date or Date instance as `dd/MM/yyyy HH:mm`.\n *\n * @param value - ISO string or Date.\n * @returns Formatted datetime string, or empty string when input is invalid.\n */\nexport function formatDateTime(value: string | Date): string {\n const date = typeof value === \"string\" ? new Date(value) : value;\n if (Number.isNaN(date.getTime())) return \"\";\n return new Intl.DateTimeFormat(\"pt-BR\", {\n dateStyle: \"short\",\n timeStyle: \"short\",\n }).format(date);\n}\n\nexport interface FormatPhoneOptions {\n /**\n * Treat the number as a mobile line: insert the mandatory `9` after the area\n * code when it is missing, and group the subscriber part `5+4` from the\n * first digit typed instead of waiting for the eleventh.\n *\n * Default `false`, which keeps the length-based behaviour: `4+4` up to ten\n * digits, `5+4` at eleven.\n */\n mobile?: boolean;\n}\n\n/**\n * Apply the Brazilian phone mask `(XX) XXXXX-XXXX` or `(XX) XXXX-XXXX`.\n *\n * By default the grouping is decided by **length**, which is what a field\n * accepting both landlines and mobiles needs.\n *\n * `mobile: true` is for a field that only accepts mobile numbers, and it exists\n * because the default is wrong as an as-you-type mask there. Reading anything up\n * to ten digits as a landline puts the hyphen after the fourth subscriber digit,\n * so a half-typed mobile renders `(11) 9123-4`; it only becomes `(11) 91234-5`\n * once the eleventh digit lands. The separator visibly jumps backwards while the\n * user is still typing. With `mobile`, the same input reads `(11) 91234` and the\n * hyphen never moves. It also inserts the leading `9` every Brazilian mobile\n * carries, so a ten-digit number gets corrected rather than masked as a landline.\n *\n * @param value - Raw digits or partially masked string.\n * @param options - Masking options.\n * @returns Masked phone string.\n *\n * @example\n * formatPhone(\"1191234\"); // \"(11) 9123-4\"\n * formatPhone(\"1191234\", { mobile: true }); // \"(11) 91234\"\n * formatPhone(\"1112345678\", { mobile: true }); // \"(11) 91234-5678\" — 9 inserted\n */\nexport function formatPhone(value: string, options: FormatPhoneOptions = {}): string {\n const digits = value.replace(/\\D/g, \"\").slice(0, 11);\n\n if (!options.mobile) {\n if (digits.length <= 10) {\n return digits.replace(/(\\d{2})(\\d)/, \"($1) $2\").replace(/(\\d{4})(\\d)/, \"$1-$2\");\n }\n return digits.replace(/(\\d{2})(\\d)/, \"($1) $2\").replace(/(\\d{5})(\\d)/, \"$1-$2\");\n }\n\n if (digits.length <= 2) return digits;\n\n const area = digits.slice(0, 2);\n let subscriber = digits.slice(2);\n if (subscriber[0] !== \"9\") subscriber = `9${subscriber}`;\n subscriber = subscriber.slice(0, 9);\n\n const prefix = subscriber.slice(0, 5);\n const suffix = subscriber.slice(5);\n return suffix ? `(${area}) ${prefix}-${suffix}` : `(${area}) ${prefix}`;\n}\n\n/**\n * Apply the Brazilian CPF mask `XXX.XXX.XXX-XX`.\n *\n * @param value - Raw digits or partially masked string.\n * @returns Masked CPF string.\n */\nexport function formatCPF(value: string): string {\n return value\n .replace(/\\D/g, \"\")\n .slice(0, 11)\n .replace(/(\\d{3})(\\d)/, \"$1.$2\")\n .replace(/(\\d{3})(\\d)/, \"$1.$2\")\n .replace(/(\\d{3})(\\d{1,2})$/, \"$1-$2\");\n}\n\n/**\n * Format a fraction (0-1) as a percentage with one decimal.\n *\n * @param value - Fraction between 0 and 1.\n * @returns Formatted percent string, e.g. \"12,5%\".\n */\nexport function formatPercent(value: number): string {\n return new Intl.NumberFormat(\"pt-BR\", {\n style: \"percent\",\n minimumFractionDigits: 1,\n maximumFractionDigits: 1,\n }).format(value);\n}\n"],"mappings":";AAMA,SAAgB,EAAe,GAAuB;CAClD,OAAO,IAAI,KAAK,aAAa,SAAS;EAClC,OAAO;EACP,UAAU;CACd,CAAC,CAAC,CAAC,OAAO,CAAK;AACnB;AAQA,SAAgB,EAAW,GAA8B;CACrD,IAAM,IAAO,OAAO,KAAU,WAAW,IAAI,KAAK,CAAK,IAAI;CAE3D,OADI,OAAO,MAAM,EAAK,QAAQ,CAAC,IAAU,KAClC,IAAI,KAAK,eAAe,OAAO,CAAC,CAAC,OAAO,CAAI;AACvD;AAwBA,SAAgB,EAAmB,GAA8B;CAC7D,IAAI,OAAO,KAAU,YAAY,sBAAsB,KAAK,CAAK,GAAG,OAAO;CAC3E,IAAM,IAAO,OAAO,KAAU,WAAW,IAAI,KAAK,CAAK,IAAI;CAC3D,IAAI,OAAO,MAAM,EAAK,QAAQ,CAAC,GAAG,OAAO;CACzC,IAAM,IAAQ,GAAG,EAAK,SAAS,IAAI,IAAI,SAAS,GAAG,GAAG,GAChD,IAAM,GAAG,EAAK,QAAQ,IAAI,SAAS,GAAG,GAAG;CAC/C,OAAO,GAAG,EAAK,YAAY,EAAE,GAAG,EAAM,GAAG;AAC7C;AAQA,SAAgB,EAAe,GAA8B;CACzD,IAAM,IAAO,OAAO,KAAU,WAAW,IAAI,KAAK,CAAK,IAAI;CAE3D,OADI,OAAO,MAAM,EAAK,QAAQ,CAAC,IAAU,KAClC,IAAI,KAAK,eAAe,SAAS;EACpC,WAAW;EACX,WAAW;CACf,CAAC,CAAC,CAAC,OAAO,CAAI;AAClB;AAsCA,SAAgB,EAAY,GAAe,IAA8B,CAAC,GAAW;CACjF,IAAM,IAAS,EAAM,QAAQ,OAAO,EAAE,CAAC,CAAC,MAAM,GAAG,EAAE;CAEnD,IAAI,CAAC,EAAQ,QAIT,OAHI,EAAO,UAAU,KACV,EAAO,QAAQ,eAAe,SAAS,CAAC,CAAC,QAAQ,eAAe,OAAO,IAE3E,EAAO,QAAQ,eAAe,SAAS,CAAC,CAAC,QAAQ,eAAe,OAAO;CAGlF,IAAI,EAAO,UAAU,GAAG,OAAO;CAE/B,IAAM,IAAO,EAAO,MAAM,GAAG,CAAC,GAC1B,IAAa,EAAO,MAAM,CAAC;CAE/B,AADI,EAAW,OAAO,QAAK,IAAa,IAAI,MAC5C,IAAa,EAAW,MAAM,GAAG,CAAC;CAElC,IAAM,IAAS,EAAW,MAAM,GAAG,CAAC,GAC9B,IAAS,EAAW,MAAM,CAAC;CACjC,OAAO,IAAS,IAAI,EAAK,IAAI,EAAO,GAAG,MAAW,IAAI,EAAK,IAAI;AACnE;AAQA,SAAgB,EAAU,GAAuB;CAC7C,OAAO,EACF,QAAQ,OAAO,EAAE,CAAC,CAClB,MAAM,GAAG,EAAE,CAAC,CACZ,QAAQ,eAAe,OAAO,CAAC,CAC/B,QAAQ,eAAe,OAAO,CAAC,CAC/B,QAAQ,qBAAqB,OAAO;AAC7C;AAQA,SAAgB,EAAc,GAAuB;CACjD,OAAO,IAAI,KAAK,aAAa,SAAS;EAClC,OAAO;EACP,uBAAuB;EACvB,uBAAuB;CAC3B,CAAC,CAAC,CAAC,OAAO,CAAK;AACnB"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const e=require("./base-url.cjs");let t=require("node:path");function n(n={}){let{swSrc:
|
|
1
|
+
const e=require("./base-url.cjs");let t=require("node:path");function n(e){return async(t,n,r)=>{let i=(t.url??``).split(`?`)[0];if(e.matches(i,e.swUrl)){try{let t=await e.buildWorker();n.setHeader(`Content-Type`,`application/javascript`),n.setHeader(`Service-Worker-Allowed`,`/`),n.setHeader(`Cache-Control`,`no-cache`),n.end(t)}catch(e){n.statusCode=500,n.end(`// SW dev build failed:\n// ${String(e)}`)}return}if(e.matches(i,e.manifestUrl)){n.setHeader(`Content-Type`,`application/json`),n.setHeader(`Cache-Control`,`no-cache`),n.end(JSON.stringify({version:`dev`,urls:[]}));return}r()}}function r(r={}){let{swSrc:i=`src/sw.ts`,swUrl:a=`/sw.js`,manifestUrl:o=`/precache-manifest.json`,enabled:s=!0}=r,c=process.cwd(),l=`/`;function u(t,n){if(t===n)return!0;let r=e.basePrefix(l);return r!==`/`&&t===`${r}${n.replace(/^\//,``)}`}let d=null,f=null;async function p(){return d||(d=await(await import(`esbuild`)).context({entryPoints:[(0,t.resolve)(c,i)],bundle:!0,format:`iife`,platform:`browser`,target:`es2020`,write:!1,absWorkingDir:c,logLevel:`silent`}),d)}function m(){if(f)return f;let e=(async()=>{let e=(await(await p()).rebuild()).outputFiles?.[0]?.text;if(e===void 0)throw Error(`esbuild produced no service-worker output`);return e})();f=e;let t=()=>{f=null};return e.then(t,t),e}async function h(){let e=d;d=null,f=null,await e?.dispose()}return{name:`tempest-pwa-dev-sw`,apply:`serve`,async buildEnd(){await h()},configResolved(e){c=e.root??process.cwd(),l=e.base??`/`},configureServer(e){s&&(e.httpServer?.once(`close`,()=>void h()),e.middlewares.use(n({swUrl:a,manifestUrl:o,matches:u,buildWorker:m})))}}}exports.tempestPwaDevSw=r;
|
|
2
2
|
//# sourceMappingURL=tempest-pwa-dev-sw.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tempest-pwa-dev-sw.cjs","names":[],"sources":["../../src/vite/tempest-pwa-dev-sw.ts"],"sourcesContent":["import { resolve } from \"node:path\";\nimport type { Plugin } from \"vite\";\nimport { basePrefix } from \"./base-url\";\nimport type { TempestVitePlugin } from \"./tempest-pwa-manifest\";\n\n/** Options for {@link tempestPwaDevSw}. */\nexport interface TempestPwaDevSwOptions {\n /** Service-worker entry, relative to the project root. Default `src/sw.ts`. */\n swSrc?: string;\n /**\n * URL the worker is served at (must match `registerServiceWorker`).\n * Default `/sw.js`.\n *\n * Write it relative to the site root; requests are matched against both the\n * bare path and the path prefixed with the resolved Vite `base`, so a\n * project served from a subpath is handled without extra configuration.\n */\n swUrl?: string;\n /**\n * Dev URL of the precache manifest. Default `/precache-manifest.json`.\n * Matched the same way as {@link TempestPwaDevSwOptions.swUrl}.\n */\n manifestUrl?: string;\n /** Serve the worker in dev. Default `true`; set `false` to opt out. */\n enabled?: boolean;\n}\n\n/**\n * Dev-server plugin that makes the service worker available under `npm run dev`.\n *\n * The production worker is bundled at build time (`vite.sw.config.ts`), so in\n * dev there is no `/sw.js` to register. This plugin compiles `swSrc` on the fly\n * with esbuild — through one incremental context, not a cold build per request —\n * and serves it as a classic worker, plus an empty `precache-manifest.json` (there are no hashed build assets to precache in\n * dev — push and runtime caching still work). It closes the \"SW in dev\" gap\n * that otherwise only `vite-plugin-pwa`'s `devOptions` covered.\n *\n * @example\n * // vite.config.ts\n * import { createViteConfig, tempestPwaDevSw } from \"tempest-react-sdk/vite\";\n *\n * export default createViteConfig({ plugins: [tempestPwaDevSw()] });\n */\nexport function tempestPwaDevSw(options: TempestPwaDevSwOptions = {}): TempestVitePlugin {\n const {\n swSrc = \"src/sw.ts\",\n swUrl = \"/sw.js\",\n manifestUrl = \"/precache-manifest.json\",\n enabled = true,\n } = options;\n\n let root = process.cwd();\n let base = \"/\";\n\n /**\n * Whether a request path addresses `target`.\n *\n * Both the bare path and the base-prefixed one are accepted. Which of the\n * two arrives depends on where this middleware lands relative to Vite's own\n * base handling, and a project served from a subpath would otherwise never\n * match — the browser asks for `/app/sw.js` while the option says `/sw.js`.\n */\n function matches(url: string, target: string): boolean {\n if (url === target) return true;\n const prefix = basePrefix(base);\n if (prefix === \"/\") return false;\n return url === `${prefix}${target.replace(/^\\//, \"\")}`;\n }\n\n /**\n * The slice of esbuild's incremental API this plugin uses.\n *\n * Typed structurally rather than imported: `esbuild` is not a dependency of\n * this package. It is resolved at runtime from the app's own tree, where Vite\n * already brings it, so importing its types would add a build-time dependency\n * the runtime does not have.\n */\n interface BuildContext {\n rebuild(): Promise<{ outputFiles?: readonly { text: string }[] }>;\n dispose(): Promise<void>;\n }\n\n let context: BuildContext | null = null;\n let pending: Promise<string> | null = null;\n\n /**\n * The incremental build context, created on the first request that needs it.\n *\n * Lazy because a project that never registers a worker should not pay for an\n * esbuild child process, and because `apply: \"serve\"` still loads this module\n * in a build.\n *\n * @returns The context, reused across requests.\n */\n async function ensureContext(): Promise<BuildContext> {\n if (context) return context;\n const esbuild = (await import(\"esbuild\")) as unknown as {\n context(options: Record<string, unknown>): Promise<BuildContext>;\n };\n context = await esbuild.context({\n entryPoints: [resolve(root, swSrc)],\n bundle: true,\n format: \"iife\",\n platform: \"browser\",\n target: \"es2020\",\n write: false,\n absWorkingDir: root,\n logLevel: \"silent\",\n });\n return context;\n }\n\n /**\n * Bundle the worker, reusing the previous graph.\n *\n * `esbuild.build()` per request was a cold bundle every time, and the request\n * count is not one per session: `Cache-Control: no-cache` guarantees one per\n * page load, and Chrome re-fetches the worker script on every navigation and\n * on its own update checks. `rebuild()` reuses the graph and only redoes what\n * changed on disk.\n *\n * Single-flight, because those requests arrive in bursts and esbuild does not\n * promise anything about concurrent `rebuild()` calls on one context. Callers\n * that land while a bundle is in flight share its result, which is the right\n * answer anyway — they asked for the same file at the same moment.\n *\n * @returns The bundled worker source.\n */\n function buildWorker(): Promise<string> {\n if (pending) return pending;\n\n const run = (async (): Promise<string> => {\n const ctx = await ensureContext();\n const result = await ctx.rebuild();\n const text = result.outputFiles?.[0]?.text;\n if (text === undefined) throw new Error(\"esbuild produced no service-worker output\");\n return text;\n })();\n\n pending = run;\n const clear = (): void => {\n pending = null;\n };\n run.then(clear, clear);\n return run;\n }\n\n /**\n * Tear the context down, releasing esbuild's child process.\n *\n * Idempotent, because it is wired to both the dev server closing and the\n * plugin's `buildEnd`. Without it, restarting the dev server leaks one esbuild\n * process per restart.\n */\n async function disposeContext(): Promise<void> {\n const current = context;\n context = null;\n pending = null;\n await current?.dispose();\n }\n\n const plugin: Plugin = {\n name: \"tempest-pwa-dev-sw\",\n apply: \"serve\",\n async buildEnd() {\n await disposeContext();\n },\n configResolved(config) {\n root = config.root ?? process.cwd();\n base = config.base ?? \"/\";\n },\n configureServer(server) {\n if (!enabled) return;\n\n server.httpServer?.once(\"close\", () => void disposeContext());\n\n server.middlewares.use(async (req, res, next) => {\n const url = (req.url ?? \"\").split(\"?\")[0];\n\n if (matches(url, swUrl)) {\n try {\n const source = await buildWorker();\n res.setHeader(\"Content-Type\", \"application/javascript\");\n res.setHeader(\"Service-Worker-Allowed\", \"/\");\n res.setHeader(\"Cache-Control\", \"no-cache\");\n res.end(source);\n } catch (error) {\n res.statusCode = 500;\n res.end(`// SW dev build failed:\\n// ${String(error)}`);\n }\n return;\n }\n\n if (matches(url, manifestUrl)) {\n res.setHeader(\"Content-Type\", \"application/json\");\n res.setHeader(\"Cache-Control\", \"no-cache\");\n res.end(JSON.stringify({ version: \"dev\", urls: [] }));\n return;\n }\n\n next();\n });\n },\n };\n\n return plugin as TempestVitePlugin;\n}\n"],"mappings":"6DA2CA,SAAgB,EAAgB,EAAkC,CAAC,EAAsB,CACrF,GAAM,CACF,QAAQ,YACR,QAAQ,SACR,cAAc,0BACd,UAAU,IACV,EAEA,EAAO,QAAQ,IAAI,EACnB,EAAO,IAUX,SAAS,EAAQ,EAAa,EAAyB,CACnD,GAAI,IAAQ,EAAQ,MAAO,GAC3B,IAAM,EAAS,EAAA,WAAW,CAAI,EAE9B,OADI,IAAW,KACR,IAAQ,GAAG,IAAS,EAAO,QAAQ,MAAO,EAAE,GACvD,CAeA,IAAI,EAA+B,KAC/B,EAAkC,KAWtC,eAAe,GAAuC,CAelD,OAdI,IAIJ,EAAU,MAAM,MAHO,OAAO,WAAA,CAGN,QAAQ,CAC5B,YAAa,EAAA,EAAC,EAAA,QAAA,CAAQ,EAAM,CAAK,CAAC,EAClC,OAAQ,GACR,OAAQ,OACR,SAAU,UACV,OAAQ,SACR,MAAO,GACP,cAAe,EACf,SAAU,QACd,CAAC,EACM,EACX,CAkBA,SAAS,GAA+B,CACpC,GAAI,EAAS,OAAO,EAEpB,IAAM,GAAO,SAA6B,CAGtC,IAAM,GAAO,MADQ,MADH,EAAc,EAAA,CACP,QAAQ,EAAA,CACb,cAAc,EAAE,EAAE,KACtC,GAAI,IAAS,IAAA,GAAW,MAAU,MAAM,2CAA2C,EACnF,OAAO,CACX,EAAA,CAAG,EAEH,EAAU,EACV,IAAM,MAAoB,CACtB,EAAU,IACd,EAEA,OADA,EAAI,KAAK,EAAO,CAAK,EACd,CACX,CASA,eAAe,GAAgC,CAC3C,IAAM,EAAU,EAChB,EAAU,KACV,EAAU,KACV,MAAM,GAAS,QAAQ,CAC3B,CA8CA,MAAO,CA3CH,KAAM,qBACN,MAAO,QACP,MAAM,UAAW,CACb,MAAM,EAAe,CACzB,EACA,eAAe,EAAQ,CACnB,EAAO,EAAO,MAAQ,QAAQ,IAAI,EAClC,EAAO,EAAO,MAAQ,GAC1B,EACA,gBAAgB,EAAQ,CACf,IAEL,EAAO,YAAY,KAAK,YAAe,KAAK,EAAe,CAAC,EAE5D,EAAO,YAAY,IAAI,MAAO,EAAK,EAAK,IAAS,CAC7C,IAAM,GAAO,EAAI,KAAO,GAAA,CAAI,MAAM,GAAG,CAAC,CAAC,GAEvC,GAAI,EAAQ,EAAK,CAAK,EAAG,CACrB,GAAI,CACA,IAAM,EAAS,MAAM,EAAY,EACjC,EAAI,UAAU,eAAgB,wBAAwB,EACtD,EAAI,UAAU,yBAA0B,GAAG,EAC3C,EAAI,UAAU,gBAAiB,UAAU,EACzC,EAAI,IAAI,CAAM,CAClB,OAAS,EAAO,CACZ,EAAI,WAAa,IACjB,EAAI,IAAI,+BAA+B,OAAO,CAAK,GAAG,CAC1D,CACA,MACJ,CAEA,GAAI,EAAQ,EAAK,CAAW,EAAG,CAC3B,EAAI,UAAU,eAAgB,kBAAkB,EAChD,EAAI,UAAU,gBAAiB,UAAU,EACzC,EAAI,IAAI,KAAK,UAAU,CAAE,QAAS,MAAO,KAAM,CAAC,CAAE,CAAC,CAAC,EACpD,MACJ,CAEA,EAAK,CACT,CAAC,EACL,CAGG,CACX"}
|
|
1
|
+
{"version":3,"file":"tempest-pwa-dev-sw.cjs","names":[],"sources":["../../src/vite/tempest-pwa-dev-sw.ts"],"sourcesContent":["import { resolve } from \"node:path\";\nimport type { Plugin } from \"vite\";\nimport { basePrefix } from \"./base-url\";\nimport type { TempestVitePlugin } from \"./tempest-pwa-manifest\";\n\n/** Options for {@link tempestPwaDevSw}. */\nexport interface TempestPwaDevSwOptions {\n /** Service-worker entry, relative to the project root. Default `src/sw.ts`. */\n swSrc?: string;\n /**\n * URL the worker is served at (must match `registerServiceWorker`).\n * Default `/sw.js`.\n *\n * Write it relative to the site root; requests are matched against both the\n * bare path and the path prefixed with the resolved Vite `base`, so a\n * project served from a subpath is handled without extra configuration.\n */\n swUrl?: string;\n /**\n * Dev URL of the precache manifest. Default `/precache-manifest.json`.\n * Matched the same way as {@link TempestPwaDevSwOptions.swUrl}.\n */\n manifestUrl?: string;\n /** Serve the worker in dev. Default `true`; set `false` to opt out. */\n enabled?: boolean;\n}\n\n/**\n * The dev middleware: serve the compiled worker, and a placeholder manifest.\n *\n * Extracted from the plugin so the request handling reads on its own — the\n * plugin body is then only wiring. The manifest is deliberately empty in dev:\n * precaching a set of URLs Vite rewrites on every change would serve stale\n * modules, and the worker's runtime routes still work without it.\n *\n * @param deps - The two URLs, the path matcher and the incremental build.\n * @returns A connect-style middleware.\n */\nfunction createDevSwMiddleware(deps: {\n swUrl: string;\n manifestUrl: string;\n matches: (url: string, target: string) => boolean;\n buildWorker: () => Promise<string>;\n}): (\n req: { url?: string },\n res: {\n statusCode?: number;\n setHeader: (name: string, value: string) => void;\n end: (body: string) => void;\n },\n next: () => void,\n) => Promise<void> {\n return async (req, res, next) => {\n const url = (req.url ?? \"\").split(\"?\")[0];\n\n if (deps.matches(url, deps.swUrl)) {\n try {\n const source = await deps.buildWorker();\n res.setHeader(\"Content-Type\", \"application/javascript\");\n res.setHeader(\"Service-Worker-Allowed\", \"/\");\n res.setHeader(\"Cache-Control\", \"no-cache\");\n res.end(source);\n } catch (error) {\n res.statusCode = 500;\n res.end(`// SW dev build failed:\\n// ${String(error)}`);\n }\n return;\n }\n\n if (deps.matches(url, deps.manifestUrl)) {\n res.setHeader(\"Content-Type\", \"application/json\");\n res.setHeader(\"Cache-Control\", \"no-cache\");\n res.end(JSON.stringify({ version: \"dev\", urls: [] }));\n return;\n }\n\n next();\n };\n}\n\n/**\n * Dev-server plugin that makes the service worker available under `npm run dev`.\n *\n * The production worker is bundled at build time (`vite.sw.config.ts`), so in\n * dev there is no `/sw.js` to register. This plugin compiles `swSrc` on the fly\n * with esbuild — through one incremental context, not a cold build per request —\n * and serves it as a classic worker, plus an empty `precache-manifest.json` (there are no hashed build assets to precache in\n * dev — push and runtime caching still work). It closes the \"SW in dev\" gap\n * that otherwise only `vite-plugin-pwa`'s `devOptions` covered.\n *\n * @example\n * // vite.config.ts\n * import { createViteConfig, tempestPwaDevSw } from \"tempest-react-sdk/vite\";\n *\n * export default createViteConfig({ plugins: [tempestPwaDevSw()] });\n */\nexport function tempestPwaDevSw(options: TempestPwaDevSwOptions = {}): TempestVitePlugin {\n const {\n swSrc = \"src/sw.ts\",\n swUrl = \"/sw.js\",\n manifestUrl = \"/precache-manifest.json\",\n enabled = true,\n } = options;\n\n let root = process.cwd();\n let base = \"/\";\n\n /**\n * Whether a request path addresses `target`.\n *\n * Both the bare path and the base-prefixed one are accepted. Which of the\n * two arrives depends on where this middleware lands relative to Vite's own\n * base handling, and a project served from a subpath would otherwise never\n * match — the browser asks for `/app/sw.js` while the option says `/sw.js`.\n */\n function matches(url: string, target: string): boolean {\n if (url === target) return true;\n const prefix = basePrefix(base);\n if (prefix === \"/\") return false;\n return url === `${prefix}${target.replace(/^\\//, \"\")}`;\n }\n\n /**\n * The slice of esbuild's incremental API this plugin uses.\n *\n * Typed structurally rather than imported: `esbuild` is not a dependency of\n * this package. It is resolved at runtime from the app's own tree, where Vite\n * already brings it, so importing its types would add a build-time dependency\n * the runtime does not have.\n */\n interface BuildContext {\n rebuild(): Promise<{ outputFiles?: readonly { text: string }[] }>;\n dispose(): Promise<void>;\n }\n\n let context: BuildContext | null = null;\n let pending: Promise<string> | null = null;\n\n /**\n * The incremental build context, created on the first request that needs it.\n *\n * Lazy because a project that never registers a worker should not pay for an\n * esbuild child process, and because `apply: \"serve\"` still loads this module\n * in a build.\n *\n * @returns The context, reused across requests.\n */\n async function ensureContext(): Promise<BuildContext> {\n if (context) return context;\n const esbuild = (await import(\"esbuild\")) as unknown as {\n context(options: Record<string, unknown>): Promise<BuildContext>;\n };\n context = await esbuild.context({\n entryPoints: [resolve(root, swSrc)],\n bundle: true,\n format: \"iife\",\n platform: \"browser\",\n target: \"es2020\",\n write: false,\n absWorkingDir: root,\n logLevel: \"silent\",\n });\n return context;\n }\n\n /**\n * Bundle the worker, reusing the previous graph.\n *\n * `esbuild.build()` per request was a cold bundle every time, and the request\n * count is not one per session: `Cache-Control: no-cache` guarantees one per\n * page load, and Chrome re-fetches the worker script on every navigation and\n * on its own update checks. `rebuild()` reuses the graph and only redoes what\n * changed on disk.\n *\n * Single-flight, because those requests arrive in bursts and esbuild does not\n * promise anything about concurrent `rebuild()` calls on one context. Callers\n * that land while a bundle is in flight share its result, which is the right\n * answer anyway — they asked for the same file at the same moment.\n *\n * @returns The bundled worker source.\n */\n function buildWorker(): Promise<string> {\n if (pending) return pending;\n\n const run = (async (): Promise<string> => {\n const ctx = await ensureContext();\n const result = await ctx.rebuild();\n const text = result.outputFiles?.[0]?.text;\n if (text === undefined) throw new Error(\"esbuild produced no service-worker output\");\n return text;\n })();\n\n pending = run;\n const clear = (): void => {\n pending = null;\n };\n run.then(clear, clear);\n return run;\n }\n\n /**\n * Tear the context down, releasing esbuild's child process.\n *\n * Idempotent, because it is wired to both the dev server closing and the\n * plugin's `buildEnd`. Without it, restarting the dev server leaks one esbuild\n * process per restart.\n */\n async function disposeContext(): Promise<void> {\n const current = context;\n context = null;\n pending = null;\n await current?.dispose();\n }\n\n const plugin: Plugin = {\n name: \"tempest-pwa-dev-sw\",\n apply: \"serve\",\n async buildEnd() {\n await disposeContext();\n },\n configResolved(config) {\n root = config.root ?? process.cwd();\n base = config.base ?? \"/\";\n },\n configureServer(server) {\n if (!enabled) return;\n\n server.httpServer?.once(\"close\", () => void disposeContext());\n\n server.middlewares.use(\n createDevSwMiddleware({ swUrl, manifestUrl, matches, buildWorker }),\n );\n },\n };\n\n return plugin as TempestVitePlugin;\n}\n"],"mappings":"6DAsCA,SAAS,EAAsB,EAaZ,CACf,OAAO,MAAO,EAAK,EAAK,IAAS,CAC7B,IAAM,GAAO,EAAI,KAAO,GAAA,CAAI,MAAM,GAAG,CAAC,CAAC,GAEvC,GAAI,EAAK,QAAQ,EAAK,EAAK,KAAK,EAAG,CAC/B,GAAI,CACA,IAAM,EAAS,MAAM,EAAK,YAAY,EACtC,EAAI,UAAU,eAAgB,wBAAwB,EACtD,EAAI,UAAU,yBAA0B,GAAG,EAC3C,EAAI,UAAU,gBAAiB,UAAU,EACzC,EAAI,IAAI,CAAM,CAClB,OAAS,EAAO,CACZ,EAAI,WAAa,IACjB,EAAI,IAAI,+BAA+B,OAAO,CAAK,GAAG,CAC1D,CACA,MACJ,CAEA,GAAI,EAAK,QAAQ,EAAK,EAAK,WAAW,EAAG,CACrC,EAAI,UAAU,eAAgB,kBAAkB,EAChD,EAAI,UAAU,gBAAiB,UAAU,EACzC,EAAI,IAAI,KAAK,UAAU,CAAE,QAAS,MAAO,KAAM,CAAC,CAAE,CAAC,CAAC,EACpD,MACJ,CAEA,EAAK,CACT,CACJ,CAkBA,SAAgB,EAAgB,EAAkC,CAAC,EAAsB,CACrF,GAAM,CACF,QAAQ,YACR,QAAQ,SACR,cAAc,0BACd,UAAU,IACV,EAEA,EAAO,QAAQ,IAAI,EACnB,EAAO,IAUX,SAAS,EAAQ,EAAa,EAAyB,CACnD,GAAI,IAAQ,EAAQ,MAAO,GAC3B,IAAM,EAAS,EAAA,WAAW,CAAI,EAE9B,OADI,IAAW,KACR,IAAQ,GAAG,IAAS,EAAO,QAAQ,MAAO,EAAE,GACvD,CAeA,IAAI,EAA+B,KAC/B,EAAkC,KAWtC,eAAe,GAAuC,CAelD,OAdI,IAIJ,EAAU,MAAM,MAHO,OAAO,WAAA,CAGN,QAAQ,CAC5B,YAAa,EAAA,EAAC,EAAA,QAAA,CAAQ,EAAM,CAAK,CAAC,EAClC,OAAQ,GACR,OAAQ,OACR,SAAU,UACV,OAAQ,SACR,MAAO,GACP,cAAe,EACf,SAAU,QACd,CAAC,EACM,EACX,CAkBA,SAAS,GAA+B,CACpC,GAAI,EAAS,OAAO,EAEpB,IAAM,GAAO,SAA6B,CAGtC,IAAM,GAAO,MADQ,MADH,EAAc,EAAA,CACP,QAAQ,EAAA,CACb,cAAc,EAAE,EAAE,KACtC,GAAI,IAAS,IAAA,GAAW,MAAU,MAAM,2CAA2C,EACnF,OAAO,CACX,EAAA,CAAG,EAEH,EAAU,EACV,IAAM,MAAoB,CACtB,EAAU,IACd,EAEA,OADA,EAAI,KAAK,EAAO,CAAK,EACd,CACX,CASA,eAAe,GAAgC,CAC3C,IAAM,EAAU,EAChB,EAAU,KACV,EAAU,KACV,MAAM,GAAS,QAAQ,CAC3B,CAuBA,MAAO,CApBH,KAAM,qBACN,MAAO,QACP,MAAM,UAAW,CACb,MAAM,EAAe,CACzB,EACA,eAAe,EAAQ,CACnB,EAAO,EAAO,MAAQ,QAAQ,IAAI,EAClC,EAAO,EAAO,MAAQ,GAC1B,EACA,gBAAgB,EAAQ,CACf,IAEL,EAAO,YAAY,KAAK,YAAe,KAAK,EAAe,CAAC,EAE5D,EAAO,YAAY,IACf,EAAsB,CAAE,QAAO,cAAa,UAAS,aAAY,CAAC,CACtE,EACJ,CAGG,CACX"}
|
|
@@ -1,77 +1,85 @@
|
|
|
1
1
|
import { basePrefix as e } from "./base-url.js";
|
|
2
2
|
import { resolve as t } from "node:path";
|
|
3
3
|
//#region src/vite/tempest-pwa-dev-sw.ts
|
|
4
|
-
function n(
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
function n(e) {
|
|
5
|
+
return async (t, n, r) => {
|
|
6
|
+
let i = (t.url ?? "").split("?")[0];
|
|
7
|
+
if (e.matches(i, e.swUrl)) {
|
|
8
|
+
try {
|
|
9
|
+
let t = await e.buildWorker();
|
|
10
|
+
n.setHeader("Content-Type", "application/javascript"), n.setHeader("Service-Worker-Allowed", "/"), n.setHeader("Cache-Control", "no-cache"), n.end(t);
|
|
11
|
+
} catch (e) {
|
|
12
|
+
n.statusCode = 500, n.end(`// SW dev build failed:\n// ${String(e)}`);
|
|
13
|
+
}
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
if (e.matches(i, e.manifestUrl)) {
|
|
17
|
+
n.setHeader("Content-Type", "application/json"), n.setHeader("Cache-Control", "no-cache"), n.end(JSON.stringify({
|
|
18
|
+
version: "dev",
|
|
19
|
+
urls: []
|
|
20
|
+
}));
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
r();
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
function r(r = {}) {
|
|
27
|
+
let { swSrc: i = "src/sw.ts", swUrl: a = "/sw.js", manifestUrl: o = "/precache-manifest.json", enabled: s = !0 } = r, c = process.cwd(), l = "/";
|
|
28
|
+
function u(t, n) {
|
|
7
29
|
if (t === n) return !0;
|
|
8
|
-
let r = e(
|
|
30
|
+
let r = e(l);
|
|
9
31
|
return r !== "/" && t === `${r}${n.replace(/^\//, "")}`;
|
|
10
32
|
}
|
|
11
|
-
let
|
|
12
|
-
async function
|
|
13
|
-
return
|
|
14
|
-
entryPoints: [t(
|
|
33
|
+
let d = null, f = null;
|
|
34
|
+
async function p() {
|
|
35
|
+
return d || (d = await (await import("esbuild")).context({
|
|
36
|
+
entryPoints: [t(c, i)],
|
|
15
37
|
bundle: !0,
|
|
16
38
|
format: "iife",
|
|
17
39
|
platform: "browser",
|
|
18
40
|
target: "es2020",
|
|
19
41
|
write: !1,
|
|
20
|
-
absWorkingDir:
|
|
42
|
+
absWorkingDir: c,
|
|
21
43
|
logLevel: "silent"
|
|
22
|
-
}),
|
|
44
|
+
}), d);
|
|
23
45
|
}
|
|
24
|
-
function
|
|
25
|
-
if (
|
|
46
|
+
function m() {
|
|
47
|
+
if (f) return f;
|
|
26
48
|
let e = (async () => {
|
|
27
|
-
let e = (await (await
|
|
49
|
+
let e = (await (await p()).rebuild()).outputFiles?.[0]?.text;
|
|
28
50
|
if (e === void 0) throw Error("esbuild produced no service-worker output");
|
|
29
51
|
return e;
|
|
30
52
|
})();
|
|
31
|
-
|
|
53
|
+
f = e;
|
|
32
54
|
let t = () => {
|
|
33
|
-
|
|
55
|
+
f = null;
|
|
34
56
|
};
|
|
35
57
|
return e.then(t, t), e;
|
|
36
58
|
}
|
|
37
|
-
async function
|
|
38
|
-
let e =
|
|
39
|
-
|
|
59
|
+
async function h() {
|
|
60
|
+
let e = d;
|
|
61
|
+
d = null, f = null, await e?.dispose();
|
|
40
62
|
}
|
|
41
63
|
return {
|
|
42
64
|
name: "tempest-pwa-dev-sw",
|
|
43
65
|
apply: "serve",
|
|
44
66
|
async buildEnd() {
|
|
45
|
-
await
|
|
67
|
+
await h();
|
|
46
68
|
},
|
|
47
69
|
configResolved(e) {
|
|
48
|
-
|
|
70
|
+
c = e.root ?? process.cwd(), l = e.base ?? "/";
|
|
49
71
|
},
|
|
50
72
|
configureServer(e) {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
} catch (e) {
|
|
58
|
-
t.statusCode = 500, t.end(`// SW dev build failed:\n// ${String(e)}`);
|
|
59
|
-
}
|
|
60
|
-
return;
|
|
61
|
-
}
|
|
62
|
-
if (l(r, a)) {
|
|
63
|
-
t.setHeader("Content-Type", "application/json"), t.setHeader("Cache-Control", "no-cache"), t.end(JSON.stringify({
|
|
64
|
-
version: "dev",
|
|
65
|
-
urls: []
|
|
66
|
-
}));
|
|
67
|
-
return;
|
|
68
|
-
}
|
|
69
|
-
n();
|
|
70
|
-
}));
|
|
73
|
+
s && (e.httpServer?.once("close", () => void h()), e.middlewares.use(n({
|
|
74
|
+
swUrl: a,
|
|
75
|
+
manifestUrl: o,
|
|
76
|
+
matches: u,
|
|
77
|
+
buildWorker: m
|
|
78
|
+
})));
|
|
71
79
|
}
|
|
72
80
|
};
|
|
73
81
|
}
|
|
74
82
|
//#endregion
|
|
75
|
-
export {
|
|
83
|
+
export { r as tempestPwaDevSw };
|
|
76
84
|
|
|
77
85
|
//# sourceMappingURL=tempest-pwa-dev-sw.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tempest-pwa-dev-sw.js","names":[],"sources":["../../src/vite/tempest-pwa-dev-sw.ts"],"sourcesContent":["import { resolve } from \"node:path\";\nimport type { Plugin } from \"vite\";\nimport { basePrefix } from \"./base-url\";\nimport type { TempestVitePlugin } from \"./tempest-pwa-manifest\";\n\n/** Options for {@link tempestPwaDevSw}. */\nexport interface TempestPwaDevSwOptions {\n /** Service-worker entry, relative to the project root. Default `src/sw.ts`. */\n swSrc?: string;\n /**\n * URL the worker is served at (must match `registerServiceWorker`).\n * Default `/sw.js`.\n *\n * Write it relative to the site root; requests are matched against both the\n * bare path and the path prefixed with the resolved Vite `base`, so a\n * project served from a subpath is handled without extra configuration.\n */\n swUrl?: string;\n /**\n * Dev URL of the precache manifest. Default `/precache-manifest.json`.\n * Matched the same way as {@link TempestPwaDevSwOptions.swUrl}.\n */\n manifestUrl?: string;\n /** Serve the worker in dev. Default `true`; set `false` to opt out. */\n enabled?: boolean;\n}\n\n/**\n * Dev-server plugin that makes the service worker available under `npm run dev`.\n *\n * The production worker is bundled at build time (`vite.sw.config.ts`), so in\n * dev there is no `/sw.js` to register. This plugin compiles `swSrc` on the fly\n * with esbuild — through one incremental context, not a cold build per request —\n * and serves it as a classic worker, plus an empty `precache-manifest.json` (there are no hashed build assets to precache in\n * dev — push and runtime caching still work). It closes the \"SW in dev\" gap\n * that otherwise only `vite-plugin-pwa`'s `devOptions` covered.\n *\n * @example\n * // vite.config.ts\n * import { createViteConfig, tempestPwaDevSw } from \"tempest-react-sdk/vite\";\n *\n * export default createViteConfig({ plugins: [tempestPwaDevSw()] });\n */\nexport function tempestPwaDevSw(options: TempestPwaDevSwOptions = {}): TempestVitePlugin {\n const {\n swSrc = \"src/sw.ts\",\n swUrl = \"/sw.js\",\n manifestUrl = \"/precache-manifest.json\",\n enabled = true,\n } = options;\n\n let root = process.cwd();\n let base = \"/\";\n\n /**\n * Whether a request path addresses `target`.\n *\n * Both the bare path and the base-prefixed one are accepted. Which of the\n * two arrives depends on where this middleware lands relative to Vite's own\n * base handling, and a project served from a subpath would otherwise never\n * match — the browser asks for `/app/sw.js` while the option says `/sw.js`.\n */\n function matches(url: string, target: string): boolean {\n if (url === target) return true;\n const prefix = basePrefix(base);\n if (prefix === \"/\") return false;\n return url === `${prefix}${target.replace(/^\\//, \"\")}`;\n }\n\n /**\n * The slice of esbuild's incremental API this plugin uses.\n *\n * Typed structurally rather than imported: `esbuild` is not a dependency of\n * this package. It is resolved at runtime from the app's own tree, where Vite\n * already brings it, so importing its types would add a build-time dependency\n * the runtime does not have.\n */\n interface BuildContext {\n rebuild(): Promise<{ outputFiles?: readonly { text: string }[] }>;\n dispose(): Promise<void>;\n }\n\n let context: BuildContext | null = null;\n let pending: Promise<string> | null = null;\n\n /**\n * The incremental build context, created on the first request that needs it.\n *\n * Lazy because a project that never registers a worker should not pay for an\n * esbuild child process, and because `apply: \"serve\"` still loads this module\n * in a build.\n *\n * @returns The context, reused across requests.\n */\n async function ensureContext(): Promise<BuildContext> {\n if (context) return context;\n const esbuild = (await import(\"esbuild\")) as unknown as {\n context(options: Record<string, unknown>): Promise<BuildContext>;\n };\n context = await esbuild.context({\n entryPoints: [resolve(root, swSrc)],\n bundle: true,\n format: \"iife\",\n platform: \"browser\",\n target: \"es2020\",\n write: false,\n absWorkingDir: root,\n logLevel: \"silent\",\n });\n return context;\n }\n\n /**\n * Bundle the worker, reusing the previous graph.\n *\n * `esbuild.build()` per request was a cold bundle every time, and the request\n * count is not one per session: `Cache-Control: no-cache` guarantees one per\n * page load, and Chrome re-fetches the worker script on every navigation and\n * on its own update checks. `rebuild()` reuses the graph and only redoes what\n * changed on disk.\n *\n * Single-flight, because those requests arrive in bursts and esbuild does not\n * promise anything about concurrent `rebuild()` calls on one context. Callers\n * that land while a bundle is in flight share its result, which is the right\n * answer anyway — they asked for the same file at the same moment.\n *\n * @returns The bundled worker source.\n */\n function buildWorker(): Promise<string> {\n if (pending) return pending;\n\n const run = (async (): Promise<string> => {\n const ctx = await ensureContext();\n const result = await ctx.rebuild();\n const text = result.outputFiles?.[0]?.text;\n if (text === undefined) throw new Error(\"esbuild produced no service-worker output\");\n return text;\n })();\n\n pending = run;\n const clear = (): void => {\n pending = null;\n };\n run.then(clear, clear);\n return run;\n }\n\n /**\n * Tear the context down, releasing esbuild's child process.\n *\n * Idempotent, because it is wired to both the dev server closing and the\n * plugin's `buildEnd`. Without it, restarting the dev server leaks one esbuild\n * process per restart.\n */\n async function disposeContext(): Promise<void> {\n const current = context;\n context = null;\n pending = null;\n await current?.dispose();\n }\n\n const plugin: Plugin = {\n name: \"tempest-pwa-dev-sw\",\n apply: \"serve\",\n async buildEnd() {\n await disposeContext();\n },\n configResolved(config) {\n root = config.root ?? process.cwd();\n base = config.base ?? \"/\";\n },\n configureServer(server) {\n if (!enabled) return;\n\n server.httpServer?.once(\"close\", () => void disposeContext());\n\n server.middlewares.use(async (req, res, next) => {\n const url = (req.url ?? \"\").split(\"?\")[0];\n\n if (matches(url, swUrl)) {\n try {\n const source = await buildWorker();\n res.setHeader(\"Content-Type\", \"application/javascript\");\n res.setHeader(\"Service-Worker-Allowed\", \"/\");\n res.setHeader(\"Cache-Control\", \"no-cache\");\n res.end(source);\n } catch (error) {\n res.statusCode = 500;\n res.end(`// SW dev build failed:\\n// ${String(error)}`);\n }\n return;\n }\n\n if (matches(url, manifestUrl)) {\n res.setHeader(\"Content-Type\", \"application/json\");\n res.setHeader(\"Cache-Control\", \"no-cache\");\n res.end(JSON.stringify({ version: \"dev\", urls: [] }));\n return;\n }\n\n next();\n });\n },\n };\n\n return plugin as TempestVitePlugin;\n}\n"],"mappings":";;;AA2CA,SAAgB,EAAgB,IAAkC,CAAC,GAAsB;CACrF,IAAM,EACF,WAAQ,aACR,WAAQ,UACR,iBAAc,2BACd,aAAU,OACV,GAEA,IAAO,QAAQ,IAAI,GACnB,IAAO;CAUX,SAAS,EAAQ,GAAa,GAAyB;EACnD,IAAI,MAAQ,GAAQ,OAAO;EAC3B,IAAM,IAAS,EAAW,CAAI;EAE9B,OADI,MAAW,OACR,MAAQ,GAAG,IAAS,EAAO,QAAQ,OAAO,EAAE;CACvD;CAeA,IAAI,IAA+B,MAC/B,IAAkC;CAWtC,eAAe,IAAuC;EAelD,OAdI,MAIJ,IAAU,OAAM,MAHO,OAAO,WAAA,CAGN,QAAQ;GAC5B,aAAa,CAAC,EAAQ,GAAM,CAAK,CAAC;GAClC,QAAQ;GACR,QAAQ;GACR,UAAU;GACV,QAAQ;GACR,OAAO;GACP,eAAe;GACf,UAAU;EACd,CAAC,GACM;CACX;CAkBA,SAAS,IAA+B;EACpC,IAAI,GAAS,OAAO;EAEpB,IAAM,KAAO,YAA6B;GAGtC,IAAM,KAAO,OADQ,MADH,EAAc,EAAA,CACP,QAAQ,EAAA,CACb,cAAc,EAAE,EAAE;GACtC,IAAI,MAAS,KAAA,GAAW,MAAU,MAAM,2CAA2C;GACnF,OAAO;EACX,EAAA,CAAG;EAEH,IAAU;EACV,IAAM,UAAoB;GACtB,IAAU;EACd;EAEA,OADA,EAAI,KAAK,GAAO,CAAK,GACd;CACX;CASA,eAAe,IAAgC;EAC3C,IAAM,IAAU;EAGhB,AAFA,IAAU,MACV,IAAU,MACV,MAAM,GAAS,QAAQ;CAC3B;CA8CA,OAAO;EA3CH,MAAM;EACN,OAAO;EACP,MAAM,WAAW;GACb,MAAM,EAAe;EACzB;EACA,eAAe,GAAQ;GAEnB,AADA,IAAO,EAAO,QAAQ,QAAQ,IAAI,GAClC,IAAO,EAAO,QAAQ;EAC1B;EACA,gBAAgB,GAAQ;GACf,MAEL,EAAO,YAAY,KAAK,eAAe,KAAK,EAAe,CAAC,GAE5D,EAAO,YAAY,IAAI,OAAO,GAAK,GAAK,MAAS;IAC7C,IAAM,KAAO,EAAI,OAAO,GAAA,CAAI,MAAM,GAAG,CAAC,CAAC;IAEvC,IAAI,EAAQ,GAAK,CAAK,GAAG;KACrB,IAAI;MACA,IAAM,IAAS,MAAM,EAAY;MAIjC,AAHA,EAAI,UAAU,gBAAgB,wBAAwB,GACtD,EAAI,UAAU,0BAA0B,GAAG,GAC3C,EAAI,UAAU,iBAAiB,UAAU,GACzC,EAAI,IAAI,CAAM;KAClB,SAAS,GAAO;MAEZ,AADA,EAAI,aAAa,KACjB,EAAI,IAAI,+BAA+B,OAAO,CAAK,GAAG;KAC1D;KACA;IACJ;IAEA,IAAI,EAAQ,GAAK,CAAW,GAAG;KAG3B,AAFA,EAAI,UAAU,gBAAgB,kBAAkB,GAChD,EAAI,UAAU,iBAAiB,UAAU,GACzC,EAAI,IAAI,KAAK,UAAU;MAAE,SAAS;MAAO,MAAM,CAAC;KAAE,CAAC,CAAC;KACpD;IACJ;IAEA,EAAK;GACT,CAAC;EACL;CAGG;AACX"}
|
|
1
|
+
{"version":3,"file":"tempest-pwa-dev-sw.js","names":[],"sources":["../../src/vite/tempest-pwa-dev-sw.ts"],"sourcesContent":["import { resolve } from \"node:path\";\nimport type { Plugin } from \"vite\";\nimport { basePrefix } from \"./base-url\";\nimport type { TempestVitePlugin } from \"./tempest-pwa-manifest\";\n\n/** Options for {@link tempestPwaDevSw}. */\nexport interface TempestPwaDevSwOptions {\n /** Service-worker entry, relative to the project root. Default `src/sw.ts`. */\n swSrc?: string;\n /**\n * URL the worker is served at (must match `registerServiceWorker`).\n * Default `/sw.js`.\n *\n * Write it relative to the site root; requests are matched against both the\n * bare path and the path prefixed with the resolved Vite `base`, so a\n * project served from a subpath is handled without extra configuration.\n */\n swUrl?: string;\n /**\n * Dev URL of the precache manifest. Default `/precache-manifest.json`.\n * Matched the same way as {@link TempestPwaDevSwOptions.swUrl}.\n */\n manifestUrl?: string;\n /** Serve the worker in dev. Default `true`; set `false` to opt out. */\n enabled?: boolean;\n}\n\n/**\n * The dev middleware: serve the compiled worker, and a placeholder manifest.\n *\n * Extracted from the plugin so the request handling reads on its own — the\n * plugin body is then only wiring. The manifest is deliberately empty in dev:\n * precaching a set of URLs Vite rewrites on every change would serve stale\n * modules, and the worker's runtime routes still work without it.\n *\n * @param deps - The two URLs, the path matcher and the incremental build.\n * @returns A connect-style middleware.\n */\nfunction createDevSwMiddleware(deps: {\n swUrl: string;\n manifestUrl: string;\n matches: (url: string, target: string) => boolean;\n buildWorker: () => Promise<string>;\n}): (\n req: { url?: string },\n res: {\n statusCode?: number;\n setHeader: (name: string, value: string) => void;\n end: (body: string) => void;\n },\n next: () => void,\n) => Promise<void> {\n return async (req, res, next) => {\n const url = (req.url ?? \"\").split(\"?\")[0];\n\n if (deps.matches(url, deps.swUrl)) {\n try {\n const source = await deps.buildWorker();\n res.setHeader(\"Content-Type\", \"application/javascript\");\n res.setHeader(\"Service-Worker-Allowed\", \"/\");\n res.setHeader(\"Cache-Control\", \"no-cache\");\n res.end(source);\n } catch (error) {\n res.statusCode = 500;\n res.end(`// SW dev build failed:\\n// ${String(error)}`);\n }\n return;\n }\n\n if (deps.matches(url, deps.manifestUrl)) {\n res.setHeader(\"Content-Type\", \"application/json\");\n res.setHeader(\"Cache-Control\", \"no-cache\");\n res.end(JSON.stringify({ version: \"dev\", urls: [] }));\n return;\n }\n\n next();\n };\n}\n\n/**\n * Dev-server plugin that makes the service worker available under `npm run dev`.\n *\n * The production worker is bundled at build time (`vite.sw.config.ts`), so in\n * dev there is no `/sw.js` to register. This plugin compiles `swSrc` on the fly\n * with esbuild — through one incremental context, not a cold build per request —\n * and serves it as a classic worker, plus an empty `precache-manifest.json` (there are no hashed build assets to precache in\n * dev — push and runtime caching still work). It closes the \"SW in dev\" gap\n * that otherwise only `vite-plugin-pwa`'s `devOptions` covered.\n *\n * @example\n * // vite.config.ts\n * import { createViteConfig, tempestPwaDevSw } from \"tempest-react-sdk/vite\";\n *\n * export default createViteConfig({ plugins: [tempestPwaDevSw()] });\n */\nexport function tempestPwaDevSw(options: TempestPwaDevSwOptions = {}): TempestVitePlugin {\n const {\n swSrc = \"src/sw.ts\",\n swUrl = \"/sw.js\",\n manifestUrl = \"/precache-manifest.json\",\n enabled = true,\n } = options;\n\n let root = process.cwd();\n let base = \"/\";\n\n /**\n * Whether a request path addresses `target`.\n *\n * Both the bare path and the base-prefixed one are accepted. Which of the\n * two arrives depends on where this middleware lands relative to Vite's own\n * base handling, and a project served from a subpath would otherwise never\n * match — the browser asks for `/app/sw.js` while the option says `/sw.js`.\n */\n function matches(url: string, target: string): boolean {\n if (url === target) return true;\n const prefix = basePrefix(base);\n if (prefix === \"/\") return false;\n return url === `${prefix}${target.replace(/^\\//, \"\")}`;\n }\n\n /**\n * The slice of esbuild's incremental API this plugin uses.\n *\n * Typed structurally rather than imported: `esbuild` is not a dependency of\n * this package. It is resolved at runtime from the app's own tree, where Vite\n * already brings it, so importing its types would add a build-time dependency\n * the runtime does not have.\n */\n interface BuildContext {\n rebuild(): Promise<{ outputFiles?: readonly { text: string }[] }>;\n dispose(): Promise<void>;\n }\n\n let context: BuildContext | null = null;\n let pending: Promise<string> | null = null;\n\n /**\n * The incremental build context, created on the first request that needs it.\n *\n * Lazy because a project that never registers a worker should not pay for an\n * esbuild child process, and because `apply: \"serve\"` still loads this module\n * in a build.\n *\n * @returns The context, reused across requests.\n */\n async function ensureContext(): Promise<BuildContext> {\n if (context) return context;\n const esbuild = (await import(\"esbuild\")) as unknown as {\n context(options: Record<string, unknown>): Promise<BuildContext>;\n };\n context = await esbuild.context({\n entryPoints: [resolve(root, swSrc)],\n bundle: true,\n format: \"iife\",\n platform: \"browser\",\n target: \"es2020\",\n write: false,\n absWorkingDir: root,\n logLevel: \"silent\",\n });\n return context;\n }\n\n /**\n * Bundle the worker, reusing the previous graph.\n *\n * `esbuild.build()` per request was a cold bundle every time, and the request\n * count is not one per session: `Cache-Control: no-cache` guarantees one per\n * page load, and Chrome re-fetches the worker script on every navigation and\n * on its own update checks. `rebuild()` reuses the graph and only redoes what\n * changed on disk.\n *\n * Single-flight, because those requests arrive in bursts and esbuild does not\n * promise anything about concurrent `rebuild()` calls on one context. Callers\n * that land while a bundle is in flight share its result, which is the right\n * answer anyway — they asked for the same file at the same moment.\n *\n * @returns The bundled worker source.\n */\n function buildWorker(): Promise<string> {\n if (pending) return pending;\n\n const run = (async (): Promise<string> => {\n const ctx = await ensureContext();\n const result = await ctx.rebuild();\n const text = result.outputFiles?.[0]?.text;\n if (text === undefined) throw new Error(\"esbuild produced no service-worker output\");\n return text;\n })();\n\n pending = run;\n const clear = (): void => {\n pending = null;\n };\n run.then(clear, clear);\n return run;\n }\n\n /**\n * Tear the context down, releasing esbuild's child process.\n *\n * Idempotent, because it is wired to both the dev server closing and the\n * plugin's `buildEnd`. Without it, restarting the dev server leaks one esbuild\n * process per restart.\n */\n async function disposeContext(): Promise<void> {\n const current = context;\n context = null;\n pending = null;\n await current?.dispose();\n }\n\n const plugin: Plugin = {\n name: \"tempest-pwa-dev-sw\",\n apply: \"serve\",\n async buildEnd() {\n await disposeContext();\n },\n configResolved(config) {\n root = config.root ?? process.cwd();\n base = config.base ?? \"/\";\n },\n configureServer(server) {\n if (!enabled) return;\n\n server.httpServer?.once(\"close\", () => void disposeContext());\n\n server.middlewares.use(\n createDevSwMiddleware({ swUrl, manifestUrl, matches, buildWorker }),\n );\n },\n };\n\n return plugin as TempestVitePlugin;\n}\n"],"mappings":";;;AAsCA,SAAS,EAAsB,GAaZ;CACf,OAAO,OAAO,GAAK,GAAK,MAAS;EAC7B,IAAM,KAAO,EAAI,OAAO,GAAA,CAAI,MAAM,GAAG,CAAC,CAAC;EAEvC,IAAI,EAAK,QAAQ,GAAK,EAAK,KAAK,GAAG;GAC/B,IAAI;IACA,IAAM,IAAS,MAAM,EAAK,YAAY;IAItC,AAHA,EAAI,UAAU,gBAAgB,wBAAwB,GACtD,EAAI,UAAU,0BAA0B,GAAG,GAC3C,EAAI,UAAU,iBAAiB,UAAU,GACzC,EAAI,IAAI,CAAM;GAClB,SAAS,GAAO;IAEZ,AADA,EAAI,aAAa,KACjB,EAAI,IAAI,+BAA+B,OAAO,CAAK,GAAG;GAC1D;GACA;EACJ;EAEA,IAAI,EAAK,QAAQ,GAAK,EAAK,WAAW,GAAG;GAGrC,AAFA,EAAI,UAAU,gBAAgB,kBAAkB,GAChD,EAAI,UAAU,iBAAiB,UAAU,GACzC,EAAI,IAAI,KAAK,UAAU;IAAE,SAAS;IAAO,MAAM,CAAC;GAAE,CAAC,CAAC;GACpD;EACJ;EAEA,EAAK;CACT;AACJ;AAkBA,SAAgB,EAAgB,IAAkC,CAAC,GAAsB;CACrF,IAAM,EACF,WAAQ,aACR,WAAQ,UACR,iBAAc,2BACd,aAAU,OACV,GAEA,IAAO,QAAQ,IAAI,GACnB,IAAO;CAUX,SAAS,EAAQ,GAAa,GAAyB;EACnD,IAAI,MAAQ,GAAQ,OAAO;EAC3B,IAAM,IAAS,EAAW,CAAI;EAE9B,OADI,MAAW,OACR,MAAQ,GAAG,IAAS,EAAO,QAAQ,OAAO,EAAE;CACvD;CAeA,IAAI,IAA+B,MAC/B,IAAkC;CAWtC,eAAe,IAAuC;EAelD,OAdI,MAIJ,IAAU,OAAM,MAHO,OAAO,WAAA,CAGN,QAAQ;GAC5B,aAAa,CAAC,EAAQ,GAAM,CAAK,CAAC;GAClC,QAAQ;GACR,QAAQ;GACR,UAAU;GACV,QAAQ;GACR,OAAO;GACP,eAAe;GACf,UAAU;EACd,CAAC,GACM;CACX;CAkBA,SAAS,IAA+B;EACpC,IAAI,GAAS,OAAO;EAEpB,IAAM,KAAO,YAA6B;GAGtC,IAAM,KAAO,OADQ,MADH,EAAc,EAAA,CACP,QAAQ,EAAA,CACb,cAAc,EAAE,EAAE;GACtC,IAAI,MAAS,KAAA,GAAW,MAAU,MAAM,2CAA2C;GACnF,OAAO;EACX,EAAA,CAAG;EAEH,IAAU;EACV,IAAM,UAAoB;GACtB,IAAU;EACd;EAEA,OADA,EAAI,KAAK,GAAO,CAAK,GACd;CACX;CASA,eAAe,IAAgC;EAC3C,IAAM,IAAU;EAGhB,AAFA,IAAU,MACV,IAAU,MACV,MAAM,GAAS,QAAQ;CAC3B;CAuBA,OAAO;EApBH,MAAM;EACN,OAAO;EACP,MAAM,WAAW;GACb,MAAM,EAAe;EACzB;EACA,eAAe,GAAQ;GAEnB,AADA,IAAO,EAAO,QAAQ,QAAQ,IAAI,GAClC,IAAO,EAAO,QAAQ;EAC1B;EACA,gBAAgB,GAAQ;GACf,MAEL,EAAO,YAAY,KAAK,eAAe,KAAK,EAAe,CAAC,GAE5D,EAAO,YAAY,IACf,EAAsB;IAAE;IAAO;IAAa;IAAS;GAAY,CAAC,CACtE;EACJ;CAGG;AACX"}
|