@vizejs/composable 0.426.2 → 0.427.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/abort-signal.d.mts +1 -1
- package/dist/active-element.d.mts +37 -0
- package/dist/active-element.mjs +56 -0
- package/dist/animate.d.mts +114 -0
- package/dist/animate.mjs +157 -0
- package/dist/battery.d.mts +61 -0
- package/dist/battery.mjs +90 -0
- package/dist/breakpoints.d.mts +82 -0
- package/dist/breakpoints.mjs +91 -0
- package/dist/catalog-BeTXFKxF.d.mts +4188 -0
- package/dist/catalog-r4Z5nptX.mjs +5144 -0
- package/dist/catalog.d.mts +1 -1
- package/dist/catalog.mjs +1 -1
- package/dist/color-mode.d.mts +131 -0
- package/dist/color-mode.mjs +133 -0
- package/dist/computed-async.d.mts +63 -0
- package/dist/computed-async.mjs +43 -0
- package/dist/computed-with-control.d.mts +44 -0
- package/dist/computed-with-control.mjs +54 -0
- package/dist/create-event-hook.d.mts +77 -0
- package/dist/create-event-hook.mjs +66 -0
- package/dist/create-global-state.d.mts +24 -0
- package/dist/create-global-state.mjs +34 -0
- package/dist/create-injection-state.d.mts +51 -0
- package/dist/create-injection-state.mjs +19 -0
- package/dist/create-shared-composable.d.mts +38 -0
- package/dist/create-shared-composable.mjs +54 -0
- package/dist/device-motion.d.mts +71 -0
- package/dist/device-motion.mjs +75 -0
- package/dist/device-orientation.d.mts +82 -0
- package/dist/device-orientation.mjs +86 -0
- package/dist/device-pixel-ratio.d.mts +45 -0
- package/dist/device-pixel-ratio.mjs +50 -0
- package/dist/document-visibility.mjs +6 -2
- package/dist/draggable.d.mts +121 -0
- package/dist/draggable.mjs +153 -0
- package/dist/element-bounding.d.mts +87 -0
- package/dist/element-bounding.mjs +95 -0
- package/dist/element-by-point.d.mts +70 -0
- package/dist/element-by-point.mjs +69 -0
- package/dist/element-hover.d.mts +42 -0
- package/dist/element-hover.mjs +70 -0
- package/dist/element-ref.d.mts +34 -0
- package/dist/element-ref.mjs +23 -0
- package/dist/element-target-Crpxl4D9.d.mts +60 -0
- package/dist/element-target.d.mts +2 -0
- package/dist/element-target.mjs +58 -0
- package/dist/event-listener.mjs +7 -3
- package/dist/focus.d.mts +69 -0
- package/dist/focus.mjs +124 -0
- package/dist/fps.d.mts +59 -0
- package/dist/fps.mjs +64 -0
- package/dist/geolocation.d.mts +98 -0
- package/dist/geolocation.mjs +86 -0
- package/dist/i18n.d.mts +237 -0
- package/dist/i18n.mjs +215 -0
- package/dist/icu-message.d.mts +235 -0
- package/dist/icu-message.mjs +343 -0
- package/dist/idle.d.mts +79 -0
- package/dist/idle.mjs +90 -0
- package/dist/index.d.mts +172 -6
- package/dist/index.mjs +173 -7
- package/dist/infinite-scroll.d.mts +56 -0
- package/dist/infinite-scroll.mjs +72 -0
- package/dist/intersection-observer.d.mts +122 -0
- package/dist/intersection-observer.mjs +117 -0
- package/dist/magic-keys.d.mts +121 -0
- package/dist/magic-keys.mjs +163 -0
- package/dist/math.d.mts +96 -0
- package/dist/math.mjs +84 -0
- package/dist/media-query.d.mts +3 -1
- package/dist/media-query.mjs +5 -3
- package/dist/mouse-in-element.d.mts +49 -0
- package/dist/mouse-in-element.mjs +66 -0
- package/dist/mouse.d.mts +77 -0
- package/dist/mouse.mjs +100 -0
- package/dist/mutation-observer.d.mts +98 -0
- package/dist/mutation-observer.mjs +69 -0
- package/dist/network.d.mts +96 -0
- package/dist/network.mjs +117 -0
- package/dist/on-click-outside.d.mts +69 -0
- package/dist/on-click-outside.mjs +115 -0
- package/dist/on-key-stroke.d.mts +71 -0
- package/dist/on-key-stroke.mjs +79 -0
- package/dist/on-long-press.d.mts +82 -0
- package/dist/on-long-press.mjs +105 -0
- package/dist/on-start-typing.d.mts +44 -0
- package/dist/on-start-typing.mjs +61 -0
- package/dist/page-leave.d.mts +26 -0
- package/dist/page-leave.mjs +40 -0
- package/dist/parent-element.d.mts +16 -0
- package/dist/parent-element.mjs +27 -0
- package/dist/pinch.d.mts +73 -0
- package/dist/pinch.mjs +138 -0
- package/dist/pointer-lock.d.mts +79 -0
- package/dist/pointer-lock.mjs +126 -0
- package/dist/pointer.d.mts +78 -0
- package/dist/pointer.mjs +95 -0
- package/dist/preferences.d.mts +55 -0
- package/dist/preferences.mjs +63 -0
- package/dist/preferred-languages.d.mts +43 -0
- package/dist/preferred-languages.mjs +44 -0
- package/dist/reactify.d.mts +28 -0
- package/dist/reactify.mjs +7 -0
- package/dist/ref-auto-reset.d.mts +47 -0
- package/dist/ref-auto-reset.mjs +70 -0
- package/dist/ref-debounced.d.mts +29 -0
- package/dist/ref-debounced.mjs +29 -0
- package/dist/ref-default.d.mts +27 -0
- package/dist/ref-default.mjs +33 -0
- package/dist/resize-observer.d.mts +96 -0
- package/dist/resize-observer.mjs +111 -0
- package/dist/resolver.d.mts +124 -0
- package/dist/resolver.mjs +138 -0
- package/dist/retry-async.d.mts +2 -2
- package/dist/retry-delay-DYsdq4SE.d.mts +73 -0
- package/dist/retry-delay.d.mts +1 -72
- package/dist/screen-orientation.d.mts +77 -0
- package/dist/screen-orientation.mjs +74 -0
- package/dist/scroll-lock.d.mts +37 -0
- package/dist/scroll-lock.mjs +50 -0
- package/dist/scroll.d.mts +158 -0
- package/dist/scroll.mjs +172 -0
- package/dist/standard-schema-DmNgFVK5.d.mts +107 -0
- package/dist/standard-schema.d.mts +2 -0
- package/dist/standard-schema.mjs +60 -0
- package/dist/swipe.d.mts +125 -0
- package/dist/swipe.mjs +225 -0
- package/dist/sync-ref.d.mts +80 -0
- package/dist/sync-ref.mjs +43 -0
- package/dist/text-selection.d.mts +46 -0
- package/dist/text-selection.mjs +62 -0
- package/dist/textarea-autosize.d.mts +58 -0
- package/dist/textarea-autosize.mjs +54 -0
- package/dist/timeout-scheduler-CI8RVLZs.d.mts +18 -0
- package/dist/timeout-scheduler.d.mts +1 -17
- package/dist/to-reactive.d.mts +26 -0
- package/dist/to-reactive.mjs +46 -0
- package/dist/transition.d.mts +125 -0
- package/dist/transition.mjs +266 -0
- package/dist/until.d.mts +177 -0
- package/dist/until.mjs +111 -0
- package/dist/use-array.d.mts +112 -0
- package/dist/use-array.mjs +90 -0
- package/dist/use-async-queue.d.mts +109 -0
- package/dist/use-async-queue.mjs +96 -0
- package/dist/use-async-state.d.mts +86 -0
- package/dist/use-async-state.mjs +41 -0
- package/dist/use-barcode-detector.d.mts +159 -0
- package/dist/use-barcode-detector.mjs +143 -0
- package/dist/use-bluetooth.d.mts +164 -0
- package/dist/use-bluetooth.mjs +183 -0
- package/dist/use-broadcast-channel.d.mts +115 -0
- package/dist/use-broadcast-channel.mjs +121 -0
- package/dist/use-builtin-ai.d.mts +342 -0
- package/dist/use-builtin-ai.mjs +261 -0
- package/dist/use-clipboard.d.mts +169 -0
- package/dist/use-clipboard.mjs +226 -0
- package/dist/use-compression-stream.d.mts +173 -0
- package/dist/use-compression-stream.mjs +166 -0
- package/dist/use-confirm-dialog.d.mts +70 -0
- package/dist/use-confirm-dialog.mjs +57 -0
- package/dist/use-contact-picker.d.mts +117 -0
- package/dist/use-contact-picker.mjs +103 -0
- package/dist/use-cookie.d.mts +196 -0
- package/dist/use-cookie.mjs +259 -0
- package/dist/use-countdown.d.mts +85 -0
- package/dist/use-countdown.mjs +74 -0
- package/dist/use-css-var.d.mts +96 -0
- package/dist/use-css-var.mjs +104 -0
- package/dist/use-cycle-list.d.mts +76 -0
- package/dist/use-cycle-list.mjs +39 -0
- package/dist/use-date-format.d.mts +103 -0
- package/dist/use-date-format.mjs +147 -0
- package/dist/use-date-time-format.d.mts +75 -0
- package/dist/use-date-time-format.mjs +53 -0
- package/dist/use-debounced.d.mts +1 -1
- package/dist/use-display-media.d.mts +62 -0
- package/dist/use-display-media.mjs +46 -0
- package/dist/use-display-names.d.mts +75 -0
- package/dist/use-display-names.mjs +52 -0
- package/dist/use-document-title.d.mts +88 -0
- package/dist/use-document-title.mjs +100 -0
- package/dist/use-drop-zone.d.mts +128 -0
- package/dist/use-drop-zone.mjs +154 -0
- package/dist/use-event-source.d.mts +184 -0
- package/dist/use-event-source.mjs +226 -0
- package/dist/use-eye-dropper.d.mts +79 -0
- package/dist/use-eye-dropper.mjs +68 -0
- package/dist/use-favicon.d.mts +78 -0
- package/dist/use-favicon.mjs +68 -0
- package/dist/use-fetch.d.mts +230 -0
- package/dist/use-fetch.mjs +263 -0
- package/dist/use-field.d.mts +107 -0
- package/dist/use-field.mjs +117 -0
- package/dist/use-file-dialog.d.mts +139 -0
- package/dist/use-file-dialog.mjs +101 -0
- package/dist/use-file-system-access.d.mts +184 -0
- package/dist/use-file-system-access.mjs +151 -0
- package/dist/use-form-field-props.d.mts +235 -0
- package/dist/use-form-field-props.mjs +164 -0
- package/dist/use-form.d.mts +324 -0
- package/dist/use-form.mjs +482 -0
- package/dist/use-fullscreen.d.mts +104 -0
- package/dist/use-fullscreen.mjs +95 -0
- package/dist/use-gamepad.d.mts +242 -0
- package/dist/use-gamepad.mjs +218 -0
- package/dist/use-hid.d.mts +160 -0
- package/dist/use-hid.mjs +156 -0
- package/dist/use-id-generator.d.mts +44 -0
- package/dist/use-id-generator.mjs +38 -0
- package/dist/use-image.d.mts +125 -0
- package/dist/use-image.mjs +109 -0
- package/dist/use-indexed-db.d.mts +207 -0
- package/dist/use-indexed-db.mjs +251 -0
- package/dist/use-interval-BA55gTHT.d.mts +140 -0
- package/dist/use-interval.d.mts +2 -0
- package/dist/use-interval.mjs +100 -0
- package/dist/use-list-format.d.mts +60 -0
- package/dist/use-list-format.mjs +40 -0
- package/dist/use-machine.d.mts +90 -0
- package/dist/use-machine.mjs +78 -0
- package/dist/use-map.d.mts +90 -0
- package/dist/use-map.mjs +70 -0
- package/dist/use-media-controls.d.mts +198 -0
- package/dist/use-media-controls.mjs +264 -0
- package/dist/use-memoize.d.mts +73 -0
- package/dist/use-memoize.mjs +28 -0
- package/dist/use-mounted.d.mts +24 -0
- package/dist/use-mounted.mjs +29 -0
- package/dist/use-navigation-api.d.mts +360 -0
- package/dist/use-navigation-api.mjs +231 -0
- package/dist/use-notification.d.mts +139 -0
- package/dist/use-notification.mjs +131 -0
- package/dist/use-now.d.mts +137 -0
- package/dist/use-now.mjs +44 -0
- package/dist/use-number-format.d.mts +73 -0
- package/dist/use-number-format.mjs +58 -0
- package/dist/use-object-url.d.mts +47 -0
- package/dist/use-object-url.mjs +54 -0
- package/dist/use-offset-pagination.d.mts +91 -0
- package/dist/use-offset-pagination.mjs +80 -0
- package/dist/use-payment-request.d.mts +239 -0
- package/dist/use-payment-request.mjs +206 -0
- package/dist/use-performance-observer.d.mts +285 -0
- package/dist/use-performance-observer.mjs +132 -0
- package/dist/use-permission.d.mts +99 -0
- package/dist/use-permission.mjs +92 -0
- package/dist/use-plural-rules.d.mts +79 -0
- package/dist/use-plural-rules.mjs +59 -0
- package/dist/use-push-subscription.d.mts +154 -0
- package/dist/use-push-subscription.mjs +146 -0
- package/dist/use-queue.d.mts +89 -0
- package/dist/use-queue.mjs +116 -0
- package/dist/use-raf-fn.d.mts +87 -0
- package/dist/use-raf-fn.mjs +99 -0
- package/dist/use-relative-time-format.d.mts +111 -0
- package/dist/use-relative-time-format.mjs +119 -0
- package/dist/use-request-idle-callback.d.mts +122 -0
- package/dist/use-request-idle-callback.mjs +147 -0
- package/dist/use-route-query.d.mts +149 -0
- package/dist/use-route-query.mjs +86 -0
- package/dist/use-scheduler-post-task.d.mts +156 -0
- package/dist/use-scheduler-post-task.mjs +182 -0
- package/dist/use-screen-details.d.mts +205 -0
- package/dist/use-screen-details.mjs +188 -0
- package/dist/use-script-tag.d.mts +162 -0
- package/dist/use-script-tag.mjs +171 -0
- package/dist/use-segmenter.d.mts +78 -0
- package/dist/use-segmenter.mjs +66 -0
- package/dist/use-selection.d.mts +125 -0
- package/dist/use-selection.mjs +77 -0
- package/dist/use-serial.d.mts +165 -0
- package/dist/use-serial.mjs +205 -0
- package/dist/use-service-worker.d.mts +177 -0
- package/dist/use-service-worker.mjs +178 -0
- package/dist/use-set.d.mts +96 -0
- package/dist/use-set.mjs +74 -0
- package/dist/use-share.d.mts +92 -0
- package/dist/use-share.mjs +99 -0
- package/dist/use-sorted-locale.d.mts +62 -0
- package/dist/use-sorted-locale.mjs +52 -0
- package/dist/use-sorted.d.mts +29 -0
- package/dist/use-sorted.mjs +14 -0
- package/dist/use-speech-recognition.d.mts +150 -0
- package/dist/use-speech-recognition.mjs +168 -0
- package/dist/use-speech-synthesis.d.mts +148 -0
- package/dist/use-speech-synthesis.mjs +152 -0
- package/dist/use-stack.d.mts +83 -0
- package/dist/use-stack.mjs +93 -0
- package/dist/use-stepper.d.mts +79 -0
- package/dist/use-stepper.mjs +52 -0
- package/dist/use-storage-estimate.d.mts +151 -0
- package/dist/use-storage-estimate.mjs +170 -0
- package/dist/use-storage.d.mts +214 -0
- package/dist/use-storage.mjs +415 -0
- package/dist/use-style-tag.d.mts +102 -0
- package/dist/use-style-tag.mjs +91 -0
- package/dist/use-supported.d.mts +23 -0
- package/dist/use-supported.mjs +26 -0
- package/dist/use-throttled.d.mts +1 -1
- package/dist/use-time-ago.d.mts +163 -0
- package/dist/use-time-ago.mjs +95 -0
- package/dist/use-timeout.d.mts +122 -0
- package/dist/use-timeout.mjs +65 -0
- package/dist/use-url-hash.d.mts +88 -0
- package/dist/use-url-hash.mjs +145 -0
- package/dist/use-url-search-params.d.mts +223 -0
- package/dist/use-url-search-params.mjs +295 -0
- package/dist/use-usb.d.mts +212 -0
- package/dist/use-usb.mjs +137 -0
- package/dist/use-user-activation.d.mts +83 -0
- package/dist/use-user-activation.mjs +113 -0
- package/dist/use-user-media.d.mts +194 -0
- package/dist/use-user-media.mjs +214 -0
- package/dist/use-v-model.d.mts +113 -0
- package/dist/use-v-model.mjs +40 -0
- package/dist/use-vibrate.d.mts +81 -0
- package/dist/use-vibrate.mjs +90 -0
- package/dist/use-view-transition.d.mts +106 -0
- package/dist/use-view-transition.mjs +101 -0
- package/dist/use-visual-viewport.d.mts +105 -0
- package/dist/use-visual-viewport.mjs +104 -0
- package/dist/use-wake-lock.d.mts +96 -0
- package/dist/use-wake-lock.mjs +106 -0
- package/dist/use-web-authn.d.mts +404 -0
- package/dist/use-web-authn.mjs +323 -0
- package/dist/use-web-locks.d.mts +180 -0
- package/dist/use-web-locks.mjs +136 -0
- package/dist/use-web-midi.d.mts +148 -0
- package/dist/use-web-midi.mjs +140 -0
- package/dist/use-web-vitals.d.mts +142 -0
- package/dist/use-web-vitals.mjs +248 -0
- package/dist/use-websocket.d.mts +218 -0
- package/dist/use-websocket.mjs +257 -0
- package/dist/use-worker-fn.d.mts +119 -0
- package/dist/use-worker-fn.mjs +162 -0
- package/dist/virtual-list.d.mts +105 -0
- package/dist/virtual-list.mjs +128 -0
- package/dist/watch-debounced.d.mts +84 -0
- package/dist/watch-debounced.mjs +84 -0
- package/dist/watch-ignorable.d.mts +47 -0
- package/dist/watch-ignorable.mjs +61 -0
- package/dist/watch-once.d.mts +29 -0
- package/dist/watch-once.mjs +10 -0
- package/dist/watch-pausable.d.mts +51 -0
- package/dist/watch-pausable.mjs +23 -0
- package/dist/watch-source.d.mts +29 -0
- package/dist/watch-source.mjs +1 -0
- package/dist/watch-throttled.d.mts +70 -0
- package/dist/watch-throttled.mjs +94 -0
- package/dist/whenever.d.mts +39 -0
- package/dist/whenever.mjs +41 -0
- package/dist/window-size.d.mts +87 -0
- package/dist/window-size.mjs +65 -0
- package/package.json +846 -5
- package/registry/files/abort-signal.ts +292 -0
- package/registry/files/active-element.ts +93 -0
- package/registry/files/animate.ts +322 -0
- package/registry/files/async-resource.ts +190 -0
- package/registry/files/battery.ts +166 -0
- package/registry/files/breakpoints.ts +166 -0
- package/registry/files/capability.ts +171 -0
- package/registry/files/color-mode.ts +274 -0
- package/registry/files/computed-async.ts +123 -0
- package/registry/files/computed-with-control.ts +80 -0
- package/registry/files/create-event-hook.ts +139 -0
- package/registry/files/create-global-state.ts +34 -0
- package/registry/files/create-injection-state.ts +75 -0
- package/registry/files/create-shared-composable.ts +74 -0
- package/registry/files/device-motion.ts +152 -0
- package/registry/files/device-orientation.ts +168 -0
- package/registry/files/device-pixel-ratio.ts +100 -0
- package/registry/files/disposal-scope.ts +201 -0
- package/registry/files/document-visibility.ts +152 -0
- package/registry/files/draggable.ts +273 -0
- package/registry/files/element-bounding.ts +175 -0
- package/registry/files/element-by-point.ts +149 -0
- package/registry/files/element-hover.ts +104 -0
- package/registry/files/element-ref.ts +58 -0
- package/registry/files/element-target.ts +99 -0
- package/registry/files/event-listener.ts +188 -0
- package/registry/files/focus.ts +181 -0
- package/registry/files/fps.ts +133 -0
- package/registry/files/geolocation.ts +214 -0
- package/registry/files/i18n.ts +524 -0
- package/registry/files/icu-message.ts +701 -0
- package/registry/files/idle.ts +176 -0
- package/registry/files/infinite-scroll.ts +135 -0
- package/registry/files/intersection-observer.ts +261 -0
- package/registry/files/locale.ts +148 -0
- package/registry/files/magic-keys.ts +298 -0
- package/registry/files/math.ts +175 -0
- package/registry/files/media-query.ts +102 -0
- package/registry/files/mouse-in-element.ts +107 -0
- package/registry/files/mouse.ts +167 -0
- package/registry/files/mutation-observer.ts +183 -0
- package/registry/files/network.ts +229 -0
- package/registry/files/on-click-outside.ts +178 -0
- package/registry/files/on-key-stroke.ts +135 -0
- package/registry/files/on-long-press.ts +188 -0
- package/registry/files/on-start-typing.ts +88 -0
- package/registry/files/page-leave.ts +57 -0
- package/registry/files/parent-element.ts +29 -0
- package/registry/files/pinch.ts +187 -0
- package/registry/files/pointer-lock.ts +232 -0
- package/registry/files/pointer.ts +153 -0
- package/registry/files/preferences.ts +98 -0
- package/registry/files/preferred-languages.ts +88 -0
- package/registry/files/reactify.ts +39 -0
- package/registry/files/ref-auto-reset.ts +102 -0
- package/registry/files/ref-debounced.ts +34 -0
- package/registry/files/ref-default.ts +35 -0
- package/registry/files/resize-observer.ts +220 -0
- package/registry/files/retry-async.ts +261 -0
- package/registry/files/retry-delay.ts +132 -0
- package/registry/files/scope.ts +21 -0
- package/registry/files/screen-orientation.ts +165 -0
- package/registry/files/scroll-lock.ts +80 -0
- package/registry/files/scroll.ts +338 -0
- package/registry/files/standard-schema.ts +163 -0
- package/registry/files/swipe.ts +321 -0
- package/registry/files/sync-ref.ts +166 -0
- package/registry/files/temporal.ts +182 -0
- package/registry/files/text-selection.ts +110 -0
- package/registry/files/textarea-autosize.ts +114 -0
- package/registry/files/timeout-scheduler.ts +16 -0
- package/registry/files/to-reactive.ts +44 -0
- package/registry/files/transition.ts +271 -0
- package/registry/files/until.ts +352 -0
- package/registry/files/use-array.ts +201 -0
- package/registry/files/use-async-queue.ts +278 -0
- package/registry/files/use-async-state.ts +139 -0
- package/registry/files/use-barcode-detector.ts +321 -0
- package/registry/files/use-bluetooth.ts +409 -0
- package/registry/files/use-broadcast-channel.ts +250 -0
- package/registry/files/use-builtin-ai.ts +674 -0
- package/registry/files/use-clipboard.ts +425 -0
- package/registry/files/use-compression-stream.ts +364 -0
- package/registry/files/use-confirm-dialog.ts +137 -0
- package/registry/files/use-contact-picker.ts +246 -0
- package/registry/files/use-cookie.ts +500 -0
- package/registry/files/use-countdown.ts +161 -0
- package/registry/files/use-counter.ts +147 -0
- package/registry/files/use-css-var.ts +191 -0
- package/registry/files/use-cycle-list.ts +136 -0
- package/registry/files/use-date-format.ts +281 -0
- package/registry/files/use-date-time-format.ts +113 -0
- package/registry/files/use-debounced.ts +163 -0
- package/registry/files/use-display-media.ts +89 -0
- package/registry/files/use-display-names.ts +122 -0
- package/registry/files/use-document-title.ts +196 -0
- package/registry/files/use-drop-zone.ts +287 -0
- package/registry/files/use-event-source.ts +470 -0
- package/registry/files/use-eye-dropper.ts +134 -0
- package/registry/files/use-favicon.ts +140 -0
- package/registry/files/use-fetch.ts +578 -0
- package/registry/files/use-field.ts +247 -0
- package/registry/files/use-file-dialog.ts +257 -0
- package/registry/files/use-file-system-access.ts +383 -0
- package/registry/files/use-form-field-props.ts +381 -0
- package/registry/files/use-form.ts +980 -0
- package/registry/files/use-fullscreen.ts +214 -0
- package/registry/files/use-gamepad.ts +463 -0
- package/registry/files/use-hid.ts +359 -0
- package/registry/files/use-history.ts +231 -0
- package/registry/files/use-id-generator.ts +61 -0
- package/registry/files/use-image.ts +253 -0
- package/registry/files/use-indexed-db.ts +508 -0
- package/registry/files/use-interval.ts +253 -0
- package/registry/files/use-list-format.ts +85 -0
- package/registry/files/use-machine.ts +171 -0
- package/registry/files/use-map.ts +145 -0
- package/registry/files/use-media-controls.ts +545 -0
- package/registry/files/use-memoize.ts +118 -0
- package/registry/files/use-mounted.ts +30 -0
- package/registry/files/use-navigation-api.ts +641 -0
- package/registry/files/use-notification.ts +285 -0
- package/registry/files/use-now.ts +194 -0
- package/registry/files/use-number-format.ts +120 -0
- package/registry/files/use-object-url.ts +87 -0
- package/registry/files/use-offset-pagination.ts +168 -0
- package/registry/files/use-payment-request.ts +465 -0
- package/registry/files/use-performance-observer.ts +469 -0
- package/registry/files/use-permission.ts +220 -0
- package/registry/files/use-plural-rules.ts +134 -0
- package/registry/files/use-previous.ts +62 -0
- package/registry/files/use-push-subscription.ts +352 -0
- package/registry/files/use-queue.ts +197 -0
- package/registry/files/use-raf-fn.ts +176 -0
- package/registry/files/use-relative-time-format.ts +201 -0
- package/registry/files/use-request-idle-callback.ts +278 -0
- package/registry/files/use-route-query.ts +300 -0
- package/registry/files/use-scheduler-post-task.ts +397 -0
- package/registry/files/use-screen-details.ts +393 -0
- package/registry/files/use-script-tag.ts +341 -0
- package/registry/files/use-segmenter.ts +147 -0
- package/registry/files/use-selection.ts +233 -0
- package/registry/files/use-serial.ts +404 -0
- package/registry/files/use-service-worker.ts +413 -0
- package/registry/files/use-set.ts +154 -0
- package/registry/files/use-share.ts +168 -0
- package/registry/files/use-sorted-locale.ts +130 -0
- package/registry/files/use-sorted.ts +49 -0
- package/registry/files/use-speech-recognition.ts +357 -0
- package/registry/files/use-speech-synthesis.ts +333 -0
- package/registry/files/use-stack.ts +164 -0
- package/registry/files/use-stepper.ts +166 -0
- package/registry/files/use-storage-estimate.ts +352 -0
- package/registry/files/use-storage.ts +635 -0
- package/registry/files/use-style-tag.ts +200 -0
- package/registry/files/use-supported.ts +26 -0
- package/registry/files/use-throttled.ts +215 -0
- package/registry/files/use-time-ago.ts +276 -0
- package/registry/files/use-timeout.ts +218 -0
- package/registry/files/use-toggle.ts +54 -0
- package/registry/files/use-url-hash.ts +280 -0
- package/registry/files/use-url-search-params.ts +497 -0
- package/registry/files/use-usb.ts +395 -0
- package/registry/files/use-user-activation.ts +215 -0
- package/registry/files/use-user-media.ts +422 -0
- package/registry/files/use-v-model.ts +210 -0
- package/registry/files/use-vibrate.ts +174 -0
- package/registry/files/use-view-transition.ts +229 -0
- package/registry/files/use-visual-viewport.ts +228 -0
- package/registry/files/use-wake-lock.ts +206 -0
- package/registry/files/use-web-authn.ts +793 -0
- package/registry/files/use-web-locks.ts +363 -0
- package/registry/files/use-web-midi.ts +306 -0
- package/registry/files/use-web-vitals.ts +409 -0
- package/registry/files/use-websocket.ts +553 -0
- package/registry/files/use-worker-fn.ts +306 -0
- package/registry/files/virtual-list.ts +230 -0
- package/registry/files/watch-debounced.ts +206 -0
- package/registry/files/watch-ignorable.ts +128 -0
- package/registry/files/watch-once.ts +46 -0
- package/registry/files/watch-pausable.ts +93 -0
- package/registry/files/watch-source.ts +51 -0
- package/registry/files/watch-throttled.ts +201 -0
- package/registry/files/whenever.ts +70 -0
- package/registry/files/window-size.ts +151 -0
- package/registry/registry.json +5792 -0
- package/dist/catalog-D2AL3xYl.d.mts +0 -523
- package/dist/catalog-D5SYCsgW.mjs +0 -549
|
@@ -0,0 +1,701 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ICU MessageFormat support: a type-level parameter extractor and a small
|
|
3
|
+
* runtime parser/formatter built on `Intl`.
|
|
4
|
+
*
|
|
5
|
+
* The type-level parser reads message string literals and produces the
|
|
6
|
+
* parameter object a message needs, so a typo in a parameter name or a
|
|
7
|
+
* wrong value type fails to compile. The runtime implements the same
|
|
8
|
+
* grammar subset: simple arguments, `number`, `date`, `time`, `plural`
|
|
9
|
+
* (with `offset:` and `=N` cases), `selectordinal`, `select`, nesting, and
|
|
10
|
+
* ICU apostrophe quoting.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
/* -------------------------------------------------------------------------- */
|
|
14
|
+
/* Type-level parser */
|
|
15
|
+
/* -------------------------------------------------------------------------- */
|
|
16
|
+
|
|
17
|
+
/** Whitespace characters skipped around ICU tokens. */
|
|
18
|
+
type Whitespace = " " | "\n" | "\t" | "\r";
|
|
19
|
+
|
|
20
|
+
/** Remove leading and trailing whitespace from a string literal. */
|
|
21
|
+
type Trim<Text extends string> = Text extends `${Whitespace}${infer Rest}`
|
|
22
|
+
? Trim<Rest>
|
|
23
|
+
: Text extends `${infer Rest}${Whitespace}`
|
|
24
|
+
? Trim<Rest>
|
|
25
|
+
: Text;
|
|
26
|
+
|
|
27
|
+
/** Characters that start a quoted literal after an apostrophe. */
|
|
28
|
+
type QuoteStarter = "{" | "}" | "#" | "|";
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* One parameter occurrence found while scanning: `[name, value type,
|
|
32
|
+
* whether the occurrence carries a format type]`.
|
|
33
|
+
*/
|
|
34
|
+
type Occurrence = readonly [name: string, value: unknown, typed: boolean];
|
|
35
|
+
|
|
36
|
+
/** Value type required by an argument's format type. */
|
|
37
|
+
type ArgumentValue<Kind extends string> = Kind extends
|
|
38
|
+
| "number"
|
|
39
|
+
| "spellout"
|
|
40
|
+
| "ordinal"
|
|
41
|
+
| "duration"
|
|
42
|
+
? number
|
|
43
|
+
: Kind extends "date" | "time"
|
|
44
|
+
? Date | number
|
|
45
|
+
: string;
|
|
46
|
+
|
|
47
|
+
/** Skip a quoted literal (the text after `'`) up to its closing apostrophe. */
|
|
48
|
+
type SkipQuoted<Text extends string> = Text extends `${string}'${infer Rest}` ? Rest : "";
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Scan message text, collecting argument occurrences. Plain text is skipped
|
|
52
|
+
* in one step up to the next `{` unless it contains an apostrophe, in which
|
|
53
|
+
* case quoting is handled character by character.
|
|
54
|
+
*/
|
|
55
|
+
type Scan<
|
|
56
|
+
Text extends string,
|
|
57
|
+
Found extends Occurrence,
|
|
58
|
+
> = Text extends `${infer Plain}{${infer Rest}`
|
|
59
|
+
? Plain extends `${string}'${string}`
|
|
60
|
+
? ScanChars<Text, Found>
|
|
61
|
+
: ScanArgument<Rest, Found>
|
|
62
|
+
: Found;
|
|
63
|
+
|
|
64
|
+
/** Character-level scan used around apostrophes. */
|
|
65
|
+
type ScanChars<Text extends string, Found extends Occurrence> = Text extends `''${infer Rest}`
|
|
66
|
+
? ScanChars<Rest, Found>
|
|
67
|
+
: Text extends `'${infer Next}${infer Rest}`
|
|
68
|
+
? Next extends QuoteStarter
|
|
69
|
+
? Scan<SkipQuoted<Rest>, Found>
|
|
70
|
+
: ScanChars<`${Next}${Rest}`, Found>
|
|
71
|
+
: Text extends `{${infer Rest}`
|
|
72
|
+
? ScanArgument<Rest, Found>
|
|
73
|
+
: Text extends `${string}${infer Rest}`
|
|
74
|
+
? Text extends ""
|
|
75
|
+
? Found
|
|
76
|
+
: ScanChars<Rest, Found>
|
|
77
|
+
: Found;
|
|
78
|
+
|
|
79
|
+
/** Parse the argument that starts after a `{`. */
|
|
80
|
+
type ScanArgument<
|
|
81
|
+
Text extends string,
|
|
82
|
+
Found extends Occurrence,
|
|
83
|
+
> = Text extends `${infer Head}}${infer AfterSimple}`
|
|
84
|
+
? Head extends `${string},${string}`
|
|
85
|
+
? Text extends `${infer Name},${infer AfterName}`
|
|
86
|
+
? ScanFormat<Trim<Name>, AfterName, Found>
|
|
87
|
+
: Found
|
|
88
|
+
: Scan<AfterSimple, Found | readonly [Trim<Head>, string, false]>
|
|
89
|
+
: Found;
|
|
90
|
+
|
|
91
|
+
/** Parse `type[, style]}` or `type, cases}` after an argument name. */
|
|
92
|
+
type ScanFormat<
|
|
93
|
+
Name extends string,
|
|
94
|
+
Text extends string,
|
|
95
|
+
Found extends Occurrence,
|
|
96
|
+
> = Text extends `${infer Kind},${infer AfterKind}`
|
|
97
|
+
? Kind extends `${string}}${string}`
|
|
98
|
+
? ScanSimpleFormat<Name, Text, Found>
|
|
99
|
+
: Trim<Kind> extends "plural" | "selectordinal"
|
|
100
|
+
? ScanCases<SkipOffset<AfterKind>, Name, number, never, Found>
|
|
101
|
+
: Trim<Kind> extends "select"
|
|
102
|
+
? ScanCases<AfterKind, Name, never, never, Found>
|
|
103
|
+
: AfterKind extends `${string}}${infer Rest}`
|
|
104
|
+
? Scan<Rest, Found | readonly [Name, ArgumentValue<Trim<Kind>>, true]>
|
|
105
|
+
: Found
|
|
106
|
+
: ScanSimpleFormat<Name, Text, Found>;
|
|
107
|
+
|
|
108
|
+
/** Parse `type}` (a format type without style). */
|
|
109
|
+
type ScanSimpleFormat<
|
|
110
|
+
Name extends string,
|
|
111
|
+
Text extends string,
|
|
112
|
+
Found extends Occurrence,
|
|
113
|
+
> = Text extends `${infer Kind}}${infer Rest}`
|
|
114
|
+
? Scan<Rest, Found | readonly [Name, ArgumentValue<Trim<Kind>>, true]>
|
|
115
|
+
: Found;
|
|
116
|
+
|
|
117
|
+
/** Drop a plural `offset:N` prefix. */
|
|
118
|
+
type SkipOffset<Text extends string> =
|
|
119
|
+
Trim<Text> extends `offset:${infer Rest}`
|
|
120
|
+
? Rest extends `${infer _Digits}${Whitespace}${infer Cases}`
|
|
121
|
+
? Cases
|
|
122
|
+
: Rest
|
|
123
|
+
: Text;
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Parse `key {content} key {content} ... }`. `Value` is the fixed argument
|
|
127
|
+
* type (`number` for plural kinds); for `select` it stays `never` and the
|
|
128
|
+
* case keys are collected in `Keys` instead.
|
|
129
|
+
*/
|
|
130
|
+
type ScanCases<
|
|
131
|
+
Text extends string,
|
|
132
|
+
Name extends string,
|
|
133
|
+
Value,
|
|
134
|
+
Keys extends string,
|
|
135
|
+
Found extends Occurrence,
|
|
136
|
+
> =
|
|
137
|
+
Trim<Text> extends `}${infer Rest}`
|
|
138
|
+
? Scan<Rest, Found | readonly [Name, [Value] extends [never] ? Keys : Value, true]>
|
|
139
|
+
: Trim<Text> extends `${infer Key}{${infer Body}`
|
|
140
|
+
? TakeBalanced<Body, "", []> extends [infer Content extends string, infer Rest extends string]
|
|
141
|
+
? ScanCases<Rest, Name, Value, Keys | Trim<Key>, Found | Scan<Content, never>>
|
|
142
|
+
: Found
|
|
143
|
+
: Found;
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Split `content}rest` at the `}` that closes the current case, honoring
|
|
147
|
+
* nested braces and apostrophe quoting. Returns `[content, rest]`.
|
|
148
|
+
*/
|
|
149
|
+
type TakeBalanced<
|
|
150
|
+
Text extends string,
|
|
151
|
+
Taken extends string,
|
|
152
|
+
Depth extends readonly unknown[],
|
|
153
|
+
> = Text extends `''${infer Rest}`
|
|
154
|
+
? TakeBalanced<Rest, `${Taken}''`, Depth>
|
|
155
|
+
: Text extends `'${infer Next}${infer Rest}`
|
|
156
|
+
? Next extends QuoteStarter
|
|
157
|
+
? Rest extends `${infer Quoted}'${infer After}`
|
|
158
|
+
? TakeBalanced<After, `${Taken}'${Next}${Quoted}'`, Depth>
|
|
159
|
+
: [`${Taken}'${Next}${Rest}`, ""]
|
|
160
|
+
: TakeBalanced<`${Next}${Rest}`, `${Taken}'`, Depth>
|
|
161
|
+
: Text extends `{${infer Rest}`
|
|
162
|
+
? TakeBalanced<Rest, `${Taken}{`, [...Depth, unknown]>
|
|
163
|
+
: Text extends `}${infer Rest}`
|
|
164
|
+
? Depth extends readonly [unknown, ...infer Outer]
|
|
165
|
+
? TakeBalanced<Rest, `${Taken}}`, Outer>
|
|
166
|
+
: [Taken, Rest]
|
|
167
|
+
: Text extends `${infer Char}${infer Rest}`
|
|
168
|
+
? TakeBalanced<Rest, `${Taken}${Char}`, Depth>
|
|
169
|
+
: [Taken, ""];
|
|
170
|
+
|
|
171
|
+
/** Flatten an intersection into a single object type. */
|
|
172
|
+
type Simplify<Value> = { [Key in keyof Value]: Value[Key] } & {};
|
|
173
|
+
|
|
174
|
+
/** Intersect the members of a union. */
|
|
175
|
+
type UnionToIntersection<Union> = (Union extends unknown ? (value: Union) => void : never) extends (
|
|
176
|
+
value: infer Intersection,
|
|
177
|
+
) => void
|
|
178
|
+
? Intersection
|
|
179
|
+
: never;
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Value type of one parameter: typed occurrences win over plain `{name}`
|
|
183
|
+
* occurrences and are intersected with each other.
|
|
184
|
+
*/
|
|
185
|
+
type ResolveOccurrences<Entries extends Occurrence> = [
|
|
186
|
+
Extract<Entries, readonly [string, unknown, true]>,
|
|
187
|
+
] extends [never]
|
|
188
|
+
? string
|
|
189
|
+
: UnionToIntersection<BoxTypedValue<Entries>> extends infer Merged
|
|
190
|
+
? Merged extends { readonly value: unknown }
|
|
191
|
+
? Merged["value"]
|
|
192
|
+
: never
|
|
193
|
+
: never;
|
|
194
|
+
|
|
195
|
+
/** Box the value of each typed occurrence (distributive over `Entry`). */
|
|
196
|
+
type BoxTypedValue<Entry extends Occurrence> = Entry extends readonly [string, infer Value, true]
|
|
197
|
+
? { readonly value: Value }
|
|
198
|
+
: never;
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Every argument occurrence of a message literal as a union of
|
|
202
|
+
* `[name, value type, typed]` tuples. Exposed so that several messages (for
|
|
203
|
+
* example the translations of one key) can be merged with the same
|
|
204
|
+
* "typed occurrence wins" rule through {@link ResolveMessageArguments}.
|
|
205
|
+
*/
|
|
206
|
+
export type MessageArguments<Message extends string> = Scan<Message, never>;
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Turn a union of {@link MessageArguments} into a parameter object. Typed
|
|
210
|
+
* occurrences win over plain `{name}` ones; conflicting typed occurrences
|
|
211
|
+
* intersect (and become `never` when incompatible).
|
|
212
|
+
*/
|
|
213
|
+
export type ResolveMessageArguments<Entries extends Occurrence> = Simplify<CollectValues<Entries>>;
|
|
214
|
+
|
|
215
|
+
/** One argument occurrence: `[name, value type, whether it is typed]`. */
|
|
216
|
+
export type MessageArgument = Occurrence;
|
|
217
|
+
|
|
218
|
+
/** Distribute a union of occurrences into one value type per name. */
|
|
219
|
+
type CollectValues<Entries extends Occurrence> = {
|
|
220
|
+
[Name in Entries[0]]: ResolveOccurrences<Extract<Entries, readonly [Name, unknown, boolean]>>;
|
|
221
|
+
};
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Parameters a single ICU message needs, extracted from its string literal.
|
|
225
|
+
*
|
|
226
|
+
* - `{name}` → `string`
|
|
227
|
+
* - `{n, number}` (also `spellout`, `ordinal`, `duration`) → `number`
|
|
228
|
+
* - `{d, date}` / `{d, time}` → `Date | number`
|
|
229
|
+
* - `{n, plural, …}` / `{n, selectordinal, …}` → `number`
|
|
230
|
+
* - `{g, select, a {…} b {…} other {…}}` → `"a" | "b" | "other"`
|
|
231
|
+
*
|
|
232
|
+
* Nested arguments inside plural/select cases are collected as well. When
|
|
233
|
+
* a name occurs both plainly and with a type, the typed occurrence wins.
|
|
234
|
+
* A non-literal `string` yields `Record<string, unknown>`.
|
|
235
|
+
*
|
|
236
|
+
* @example
|
|
237
|
+
* ```ts
|
|
238
|
+
* type P = MessageParams<"Hi {name}, {count, plural, one {# item} other {# items}}">;
|
|
239
|
+
* // ^? { name: string; count: number }
|
|
240
|
+
* ```
|
|
241
|
+
*/
|
|
242
|
+
export type MessageParams<Message extends string> = string extends Message
|
|
243
|
+
? Record<string, unknown>
|
|
244
|
+
: Simplify<CollectValues<Scan<Message, never>>>;
|
|
245
|
+
|
|
246
|
+
/** Whether a message literal takes no parameters. */
|
|
247
|
+
export type HasNoParams<Message extends string> = keyof MessageParams<Message> extends never
|
|
248
|
+
? true
|
|
249
|
+
: false;
|
|
250
|
+
|
|
251
|
+
/* -------------------------------------------------------------------------- */
|
|
252
|
+
/* Runtime parser */
|
|
253
|
+
/* -------------------------------------------------------------------------- */
|
|
254
|
+
|
|
255
|
+
/** Node of a parsed ICU message. */
|
|
256
|
+
export type MessageNode =
|
|
257
|
+
| { readonly kind: "text"; readonly value: string }
|
|
258
|
+
| { readonly kind: "pound" }
|
|
259
|
+
| { readonly kind: "argument"; readonly name: string }
|
|
260
|
+
| {
|
|
261
|
+
readonly kind: "number" | "date" | "time";
|
|
262
|
+
readonly name: string;
|
|
263
|
+
readonly style: string | undefined;
|
|
264
|
+
}
|
|
265
|
+
| {
|
|
266
|
+
readonly kind: "plural" | "selectordinal";
|
|
267
|
+
readonly name: string;
|
|
268
|
+
readonly offset: number;
|
|
269
|
+
readonly cases: Readonly<Record<string, readonly MessageNode[]>>;
|
|
270
|
+
}
|
|
271
|
+
| {
|
|
272
|
+
readonly kind: "select";
|
|
273
|
+
readonly name: string;
|
|
274
|
+
readonly cases: Readonly<Record<string, readonly MessageNode[]>>;
|
|
275
|
+
};
|
|
276
|
+
|
|
277
|
+
/** Error code carried by {@link MessageSyntaxError}. */
|
|
278
|
+
export type MessageSyntaxErrorCode = "VIZE_COMPOSE_ICU_SYNTAX";
|
|
279
|
+
|
|
280
|
+
/** Thrown by {@link parseMessage} for malformed ICU messages. */
|
|
281
|
+
export interface MessageSyntaxError extends SyntaxError {
|
|
282
|
+
/** Stable machine-readable code. */
|
|
283
|
+
readonly code: MessageSyntaxErrorCode;
|
|
284
|
+
/** Character offset of the problem. */
|
|
285
|
+
readonly offset: number;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
function syntaxError(message: string, offset: number): MessageSyntaxError {
|
|
289
|
+
return Object.assign(
|
|
290
|
+
new SyntaxError(`[VIZE_COMPOSE_ICU_SYNTAX] ${message} at offset ${String(offset)}`),
|
|
291
|
+
{ code: "VIZE_COMPOSE_ICU_SYNTAX" as const, offset },
|
|
292
|
+
);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
const quoteStarters = new Set(["{", "}", "#", "|"]);
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* Parse an ICU MessageFormat string into an AST.
|
|
299
|
+
*
|
|
300
|
+
* Supports simple arguments, `number`/`date`/`time` with an optional style,
|
|
301
|
+
* `plural` and `selectordinal` (with `offset:` and `=N` cases), `select`,
|
|
302
|
+
* nesting, `#` inside plural cases, and apostrophe quoting (`''` is a
|
|
303
|
+
* literal apostrophe; `'{…}'` is literal text). Pure and deterministic.
|
|
304
|
+
*
|
|
305
|
+
* @example
|
|
306
|
+
* ```ts
|
|
307
|
+
* parseMessage("{n, plural, one {# item} other {# items}}");
|
|
308
|
+
* ```
|
|
309
|
+
*
|
|
310
|
+
* @param message ICU message source.
|
|
311
|
+
* @throws {@link MessageSyntaxError} tagged `VIZE_COMPOSE_ICU_SYNTAX` for
|
|
312
|
+
* malformed messages, including a plural or select without an `other` case.
|
|
313
|
+
* @returns The parsed nodes.
|
|
314
|
+
*/
|
|
315
|
+
export function parseMessage(message: string): MessageNode[] {
|
|
316
|
+
let position = 0;
|
|
317
|
+
|
|
318
|
+
const skipSpace = (): void => {
|
|
319
|
+
while (position < message.length && /\s/.test(message.charAt(position))) position += 1;
|
|
320
|
+
};
|
|
321
|
+
|
|
322
|
+
const readIdentifier = (stops: string): string => {
|
|
323
|
+
const start = position;
|
|
324
|
+
while (position < message.length && !stops.includes(message.charAt(position))) position += 1;
|
|
325
|
+
return message.slice(start, position).trim();
|
|
326
|
+
};
|
|
327
|
+
|
|
328
|
+
const expect = (char: string): void => {
|
|
329
|
+
if (message.charAt(position) !== char) {
|
|
330
|
+
throw syntaxError(`expected "${char}"`, position);
|
|
331
|
+
}
|
|
332
|
+
position += 1;
|
|
333
|
+
};
|
|
334
|
+
|
|
335
|
+
const parseNodes = (inPlural: boolean, nested: boolean): MessageNode[] => {
|
|
336
|
+
const nodes: MessageNode[] = [];
|
|
337
|
+
let text = "";
|
|
338
|
+
const flush = (): void => {
|
|
339
|
+
if (text !== "") nodes.push({ kind: "text", value: text });
|
|
340
|
+
text = "";
|
|
341
|
+
};
|
|
342
|
+
while (position < message.length) {
|
|
343
|
+
const char = message.charAt(position);
|
|
344
|
+
if (char === "'") {
|
|
345
|
+
const next = message.charAt(position + 1);
|
|
346
|
+
if (next === "'") {
|
|
347
|
+
text += "'";
|
|
348
|
+
position += 2;
|
|
349
|
+
} else if (quoteStarters.has(next)) {
|
|
350
|
+
const end = message.indexOf("'", position + 1);
|
|
351
|
+
text += message.slice(position + 1, end === -1 ? message.length : end);
|
|
352
|
+
position = end === -1 ? message.length : end + 1;
|
|
353
|
+
} else {
|
|
354
|
+
text += "'";
|
|
355
|
+
position += 1;
|
|
356
|
+
}
|
|
357
|
+
} else if (char === "{") {
|
|
358
|
+
flush();
|
|
359
|
+
position += 1;
|
|
360
|
+
nodes.push(parseArgument());
|
|
361
|
+
} else if (char === "}") {
|
|
362
|
+
if (!nested) throw syntaxError('unexpected "}"', position);
|
|
363
|
+
break;
|
|
364
|
+
} else if (char === "#" && inPlural) {
|
|
365
|
+
flush();
|
|
366
|
+
nodes.push({ kind: "pound" });
|
|
367
|
+
position += 1;
|
|
368
|
+
} else {
|
|
369
|
+
text += char;
|
|
370
|
+
position += 1;
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
flush();
|
|
374
|
+
return nodes;
|
|
375
|
+
};
|
|
376
|
+
|
|
377
|
+
const parseCases = (inPlural: boolean): Record<string, MessageNode[]> => {
|
|
378
|
+
const cases: Record<string, MessageNode[]> = {};
|
|
379
|
+
for (;;) {
|
|
380
|
+
skipSpace();
|
|
381
|
+
if (message.charAt(position) === "}") break;
|
|
382
|
+
const key = readIdentifier("{}");
|
|
383
|
+
if (key === "" || /\s/.test(key)) throw syntaxError("invalid case key", position);
|
|
384
|
+
expect("{");
|
|
385
|
+
cases[key] = parseNodes(inPlural, true);
|
|
386
|
+
expect("}");
|
|
387
|
+
}
|
|
388
|
+
if (cases["other"] === undefined) throw syntaxError('missing "other" case', position);
|
|
389
|
+
return cases;
|
|
390
|
+
};
|
|
391
|
+
|
|
392
|
+
const parseArgument = (): MessageNode => {
|
|
393
|
+
skipSpace();
|
|
394
|
+
const name = readIdentifier(",}");
|
|
395
|
+
if (name === "") throw syntaxError("empty argument name", position);
|
|
396
|
+
if (message.charAt(position) === "}") {
|
|
397
|
+
position += 1;
|
|
398
|
+
return { kind: "argument", name };
|
|
399
|
+
}
|
|
400
|
+
expect(",");
|
|
401
|
+
const kind = readIdentifier(",}");
|
|
402
|
+
let node: MessageNode;
|
|
403
|
+
if (kind === "plural" || kind === "selectordinal") {
|
|
404
|
+
expect(",");
|
|
405
|
+
skipSpace();
|
|
406
|
+
let offset = 0;
|
|
407
|
+
const match = /^offset:\s*(\d+)/.exec(message.slice(position));
|
|
408
|
+
if (match?.[1] !== undefined) {
|
|
409
|
+
offset = Number(match[1]);
|
|
410
|
+
position += match[0].length;
|
|
411
|
+
}
|
|
412
|
+
node = { kind, name, offset, cases: parseCases(true) };
|
|
413
|
+
} else if (kind === "select") {
|
|
414
|
+
expect(",");
|
|
415
|
+
node = { kind, name, cases: parseCases(false) };
|
|
416
|
+
} else if (kind === "number" || kind === "date" || kind === "time") {
|
|
417
|
+
let style: string | undefined;
|
|
418
|
+
if (message.charAt(position) === ",") {
|
|
419
|
+
position += 1;
|
|
420
|
+
style = readIdentifier("}");
|
|
421
|
+
}
|
|
422
|
+
node = { kind, name, style };
|
|
423
|
+
} else {
|
|
424
|
+
throw syntaxError(`unsupported argument type "${kind}"`, position);
|
|
425
|
+
}
|
|
426
|
+
skipSpace();
|
|
427
|
+
expect("}");
|
|
428
|
+
return node;
|
|
429
|
+
};
|
|
430
|
+
|
|
431
|
+
const nodes = parseNodes(false, false);
|
|
432
|
+
return nodes;
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
/* -------------------------------------------------------------------------- */
|
|
436
|
+
/* Runtime formatter */
|
|
437
|
+
/* -------------------------------------------------------------------------- */
|
|
438
|
+
|
|
439
|
+
/** Options for {@link formatMessage} and {@link createMessageFormatter}. */
|
|
440
|
+
export interface FormatMessageOptions {
|
|
441
|
+
/**
|
|
442
|
+
* IANA time zone for `date` and `time` arguments. Fixed by default so
|
|
443
|
+
* server and client render identically.
|
|
444
|
+
*
|
|
445
|
+
* @default "UTC"
|
|
446
|
+
*/
|
|
447
|
+
readonly timeZone?: string;
|
|
448
|
+
|
|
449
|
+
/**
|
|
450
|
+
* Named `number` styles usable as `{n, number, name}`.
|
|
451
|
+
*
|
|
452
|
+
* @default {}
|
|
453
|
+
*/
|
|
454
|
+
readonly numberFormats?: Readonly<Record<string, Intl.NumberFormatOptions>>;
|
|
455
|
+
|
|
456
|
+
/**
|
|
457
|
+
* Named `date`/`time` styles usable as `{d, date, name}`.
|
|
458
|
+
*
|
|
459
|
+
* @default {}
|
|
460
|
+
*/
|
|
461
|
+
readonly dateTimeFormats?: Readonly<Record<string, Intl.DateTimeFormatOptions>>;
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
/** Formats parsed messages for one locale, caching `Intl` objects. */
|
|
465
|
+
export interface MessageFormatter {
|
|
466
|
+
/** Locale the formatter renders in. */
|
|
467
|
+
readonly locale: string;
|
|
468
|
+
|
|
469
|
+
/**
|
|
470
|
+
* Render a message source or pre-parsed nodes.
|
|
471
|
+
*
|
|
472
|
+
* @returns The formatted string.
|
|
473
|
+
*/
|
|
474
|
+
readonly format: (
|
|
475
|
+
message: string | readonly MessageNode[],
|
|
476
|
+
params?: Readonly<Record<string, unknown>>,
|
|
477
|
+
) => string;
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
const dateStyles = new Set(["short", "medium", "long", "full"]);
|
|
481
|
+
|
|
482
|
+
function numberOptions(
|
|
483
|
+
style: string | undefined,
|
|
484
|
+
named: Readonly<Record<string, Intl.NumberFormatOptions>>,
|
|
485
|
+
): Intl.NumberFormatOptions {
|
|
486
|
+
if (style === undefined) return {};
|
|
487
|
+
const custom = named[style];
|
|
488
|
+
if (custom !== undefined) return custom;
|
|
489
|
+
if (style === "integer") return { maximumFractionDigits: 0 };
|
|
490
|
+
if (style === "percent") return { style: "percent" };
|
|
491
|
+
if (!style.startsWith("::")) return {};
|
|
492
|
+
const options: Intl.NumberFormatOptions = {};
|
|
493
|
+
for (const token of style.slice(2).trim().split(/\s+/)) {
|
|
494
|
+
if (token.startsWith("currency/")) {
|
|
495
|
+
Object.assign(options, { style: "currency", currency: token.slice(9) });
|
|
496
|
+
} else if (token === "percent") {
|
|
497
|
+
Object.assign(options, { style: "percent" });
|
|
498
|
+
} else if (token === "compact-short" || token === "K") {
|
|
499
|
+
Object.assign(options, { notation: "compact", compactDisplay: "short" });
|
|
500
|
+
} else if (token === "compact-long" || token === "KK") {
|
|
501
|
+
Object.assign(options, { notation: "compact", compactDisplay: "long" });
|
|
502
|
+
} else if (token === "group-off" || token === ",_") {
|
|
503
|
+
Object.assign(options, { useGrouping: false });
|
|
504
|
+
} else if (/^\.0*#*$/.test(token)) {
|
|
505
|
+
const minimum = token.slice(1).replaceAll("#", "").length;
|
|
506
|
+
Object.assign(options, {
|
|
507
|
+
minimumFractionDigits: minimum,
|
|
508
|
+
maximumFractionDigits: token.length - 1,
|
|
509
|
+
});
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
return options;
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
function dateTimeOptions(
|
|
516
|
+
kind: "date" | "time",
|
|
517
|
+
style: string | undefined,
|
|
518
|
+
named: Readonly<Record<string, Intl.DateTimeFormatOptions>>,
|
|
519
|
+
): Intl.DateTimeFormatOptions {
|
|
520
|
+
const custom = style === undefined ? undefined : named[style];
|
|
521
|
+
if (custom !== undefined) return custom;
|
|
522
|
+
const resolved = style !== undefined && dateStyles.has(style) ? style : "medium";
|
|
523
|
+
const value =
|
|
524
|
+
resolved === "short" || resolved === "long" || resolved === "full" ? resolved : "medium";
|
|
525
|
+
return kind === "date" ? { dateStyle: value } : { timeStyle: value };
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
/** Stringify an argument value; plain objects use JSON rather than `[object Object]`. */
|
|
529
|
+
function stringify(value: unknown): string {
|
|
530
|
+
if (typeof value === "string") return value;
|
|
531
|
+
if (typeof value === "bigint" || typeof value === "boolean" || typeof value === "symbol") {
|
|
532
|
+
return value.toString();
|
|
533
|
+
}
|
|
534
|
+
return typeof value === "function" ? "[function]" : JSON.stringify(value);
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
/**
|
|
538
|
+
* Create a caching formatter for one locale.
|
|
539
|
+
*
|
|
540
|
+
* Parsed messages and `Intl` formatters are cached inside the returned
|
|
541
|
+
* object only (no module state), so one formatter per request or per
|
|
542
|
+
* i18n instance keeps server rendering isolated.
|
|
543
|
+
*
|
|
544
|
+
* @example
|
|
545
|
+
* ```ts
|
|
546
|
+
* const formatter = createMessageFormatter("en");
|
|
547
|
+
* formatter.format("{n, plural, one {# file} other {# files}}", { n: 3 }); // "3 files"
|
|
548
|
+
* ```
|
|
549
|
+
*
|
|
550
|
+
* @param locale BCP 47 locale.
|
|
551
|
+
* @param options Time zone and named formats.
|
|
552
|
+
* @default options {}
|
|
553
|
+
* @returns The formatter.
|
|
554
|
+
*/
|
|
555
|
+
export function createMessageFormatter(
|
|
556
|
+
locale: string,
|
|
557
|
+
options: FormatMessageOptions = {},
|
|
558
|
+
): MessageFormatter {
|
|
559
|
+
const timeZone = options.timeZone ?? "UTC";
|
|
560
|
+
const numberFormats = options.numberFormats ?? {};
|
|
561
|
+
const dateTimeFormats = options.dateTimeFormats ?? {};
|
|
562
|
+
const parsed = new Map<string, readonly MessageNode[]>();
|
|
563
|
+
const numbers = new Map<string, Intl.NumberFormat>();
|
|
564
|
+
const dates = new Map<string, Intl.DateTimeFormat>();
|
|
565
|
+
const plurals = new Map<string, Intl.PluralRules>();
|
|
566
|
+
|
|
567
|
+
const number = (style: string | undefined): Intl.NumberFormat => {
|
|
568
|
+
const key = style ?? "";
|
|
569
|
+
let formatter = numbers.get(key);
|
|
570
|
+
if (formatter === undefined) {
|
|
571
|
+
formatter = new Intl.NumberFormat(locale, numberOptions(style, numberFormats));
|
|
572
|
+
numbers.set(key, formatter);
|
|
573
|
+
}
|
|
574
|
+
return formatter;
|
|
575
|
+
};
|
|
576
|
+
const dateTime = (kind: "date" | "time", style: string | undefined): Intl.DateTimeFormat => {
|
|
577
|
+
const key = `${kind}:${style ?? ""}`;
|
|
578
|
+
let formatter = dates.get(key);
|
|
579
|
+
if (formatter === undefined) {
|
|
580
|
+
formatter = new Intl.DateTimeFormat(locale, {
|
|
581
|
+
...dateTimeOptions(kind, style, dateTimeFormats),
|
|
582
|
+
timeZone,
|
|
583
|
+
});
|
|
584
|
+
dates.set(key, formatter);
|
|
585
|
+
}
|
|
586
|
+
return formatter;
|
|
587
|
+
};
|
|
588
|
+
const pluralRules = (type: Intl.PluralRuleType): Intl.PluralRules => {
|
|
589
|
+
let rules = plurals.get(type);
|
|
590
|
+
if (rules === undefined) {
|
|
591
|
+
rules = new Intl.PluralRules(locale, { type });
|
|
592
|
+
plurals.set(type, rules);
|
|
593
|
+
}
|
|
594
|
+
return rules;
|
|
595
|
+
};
|
|
596
|
+
|
|
597
|
+
const toNumber = (value: unknown): number => (typeof value === "number" ? value : Number(value));
|
|
598
|
+
const toDate = (value: unknown): Date =>
|
|
599
|
+
value instanceof Date ? value : new Date(typeof value === "number" ? value : String(value));
|
|
600
|
+
|
|
601
|
+
const render = (
|
|
602
|
+
nodes: readonly MessageNode[],
|
|
603
|
+
params: Readonly<Record<string, unknown>>,
|
|
604
|
+
pound: number | undefined,
|
|
605
|
+
): string => {
|
|
606
|
+
let output = "";
|
|
607
|
+
for (const node of nodes) {
|
|
608
|
+
switch (node.kind) {
|
|
609
|
+
case "text":
|
|
610
|
+
output += node.value;
|
|
611
|
+
break;
|
|
612
|
+
case "pound":
|
|
613
|
+
output += pound === undefined ? "#" : number(undefined).format(pound);
|
|
614
|
+
break;
|
|
615
|
+
case "argument": {
|
|
616
|
+
const value = params[node.name];
|
|
617
|
+
output +=
|
|
618
|
+
value instanceof Date
|
|
619
|
+
? dateTime("date", undefined).format(value)
|
|
620
|
+
: typeof value === "number"
|
|
621
|
+
? number(undefined).format(value)
|
|
622
|
+
: value === undefined || value === null
|
|
623
|
+
? `{${node.name}}`
|
|
624
|
+
: stringify(value);
|
|
625
|
+
break;
|
|
626
|
+
}
|
|
627
|
+
case "number":
|
|
628
|
+
output += number(node.style).format(toNumber(params[node.name]));
|
|
629
|
+
break;
|
|
630
|
+
case "date":
|
|
631
|
+
case "time":
|
|
632
|
+
output += dateTime(node.kind, node.style).format(toDate(params[node.name]));
|
|
633
|
+
break;
|
|
634
|
+
case "plural":
|
|
635
|
+
case "selectordinal": {
|
|
636
|
+
const value = toNumber(params[node.name]);
|
|
637
|
+
const exact = node.cases[`=${String(value)}`];
|
|
638
|
+
const shifted = value - node.offset;
|
|
639
|
+
const category = pluralRules(node.kind === "plural" ? "cardinal" : "ordinal").select(
|
|
640
|
+
shifted,
|
|
641
|
+
);
|
|
642
|
+
const branch = exact ?? node.cases[category] ?? node.cases["other"] ?? [];
|
|
643
|
+
output += render(branch, params, shifted);
|
|
644
|
+
break;
|
|
645
|
+
}
|
|
646
|
+
case "select": {
|
|
647
|
+
const value = String(params[node.name]);
|
|
648
|
+
const branch = node.cases[value] ?? node.cases["other"] ?? [];
|
|
649
|
+
output += render(branch, params, pound);
|
|
650
|
+
break;
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
return output;
|
|
655
|
+
};
|
|
656
|
+
|
|
657
|
+
return {
|
|
658
|
+
locale,
|
|
659
|
+
format: (message, params = {}) => {
|
|
660
|
+
let nodes: readonly MessageNode[];
|
|
661
|
+
if (typeof message === "string") {
|
|
662
|
+
const cached = parsed.get(message);
|
|
663
|
+
nodes = cached ?? parseMessage(message);
|
|
664
|
+
if (cached === undefined) parsed.set(message, nodes);
|
|
665
|
+
} else {
|
|
666
|
+
nodes = message;
|
|
667
|
+
}
|
|
668
|
+
return render(nodes, params, undefined);
|
|
669
|
+
},
|
|
670
|
+
};
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
/**
|
|
674
|
+
* Format one ICU message with typed parameters.
|
|
675
|
+
*
|
|
676
|
+
* Parameters are checked against the message literal at compile time via
|
|
677
|
+
* {@link MessageParams}. For repeated formatting prefer
|
|
678
|
+
* {@link createMessageFormatter}, which caches parsing and `Intl` objects.
|
|
679
|
+
* Deterministic: `date`/`time` arguments use `timeZone` (UTC by default).
|
|
680
|
+
*
|
|
681
|
+
* @example
|
|
682
|
+
* ```ts
|
|
683
|
+
* formatMessage("en", "Hello {name}!", { name: "Ada" }); // "Hello Ada!"
|
|
684
|
+
* ```
|
|
685
|
+
*
|
|
686
|
+
* @param locale BCP 47 locale.
|
|
687
|
+
* @param message ICU message.
|
|
688
|
+
* @param params Values for the message's arguments.
|
|
689
|
+
* @param options Time zone and named formats.
|
|
690
|
+
* @default options {}
|
|
691
|
+
* @throws {@link MessageSyntaxError} for malformed messages.
|
|
692
|
+
* @returns The formatted string.
|
|
693
|
+
*/
|
|
694
|
+
export function formatMessage<const Message extends string>(
|
|
695
|
+
locale: string,
|
|
696
|
+
message: Message,
|
|
697
|
+
params: MessageParams<Message>,
|
|
698
|
+
options: FormatMessageOptions = {},
|
|
699
|
+
): string {
|
|
700
|
+
return createMessageFormatter(locale, options).format(message, params);
|
|
701
|
+
}
|