@take-out/helpers 0.2.9 → 0.3.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/cjs/constants.cjs +1 -1
- package/dist/cjs/constants.native.js +1 -1
- package/dist/cjs/constants.native.js.map +1 -1
- package/dist/cjs/string/pluralize.cjs +4 -1
- package/dist/esm/async/abortable.mjs.map +1 -1
- package/dist/esm/constants.mjs +1 -1
- package/dist/esm/constants.mjs.map +1 -1
- package/dist/esm/constants.native.js +1 -1
- package/dist/esm/constants.native.js.map +1 -1
- package/dist/esm/index.js +72 -74
- package/dist/esm/index.js.map +1 -6
- package/dist/esm/string/pluralize.mjs +4 -1
- package/dist/esm/string/pluralize.mjs.map +1 -1
- package/package.json +3 -3
- package/src/async/abortable.ts +3 -3
- package/src/async/asyncContext.native.ts +5 -5
- package/src/async/asyncContext.ts +5 -5
- package/src/async/idle.ts +2 -2
- package/src/async/interval.ts +1 -1
- package/src/async/useAsync.ts +1 -1
- package/src/async/useAsyncEffect.ts +8 -8
- package/src/async/useLazyMount.ts +1 -1
- package/src/async/useLazyValue.ts +2 -2
- package/src/browser/clearIndexedDB.ts +1 -1
- package/src/browser/openPopup.ts +1 -1
- package/src/constants.ts +1 -1
- package/src/debug/debugUseState.ts +2 -2
- package/src/emitter.tsx +16 -16
- package/src/ensure/ensure.ts +2 -2
- package/src/function/throttle.ts +3 -3
- package/src/global/globalValue.ts +1 -1
- package/src/object/decorateObject.ts +1 -1
- package/src/object/mapObject.ts +1 -1
- package/src/object/object.ts +4 -4
- package/src/storage/storage.test.ts +1 -1
- package/src/string/pickLast.ts +1 -1
- package/src/string/pluralize.ts +5 -2
- package/src/string/truncateList.ts +1 -1
- package/src/time/formatDate.tsx +1 -1
- package/src/time/formatDateRelative.ts +4 -4
- package/src/time/useTimer.ts +2 -2
- package/src/url/urlValidate.ts +1 -1
- package/types/async/abortable.d.ts.map +1 -1
- package/types/async/asyncContext.d.ts.map +1 -1
- package/types/async/asyncContext.native.d.ts.map +1 -1
- package/types/async/idle.d.ts.map +1 -1
- package/types/async/interval.d.ts.map +1 -1
- package/types/async/useAsync.d.ts.map +1 -1
- package/types/async/useAsyncEffect.d.ts.map +1 -1
- package/types/async/useLazyMount.d.ts.map +1 -1
- package/types/async/useLazyValue.d.ts.map +1 -1
- package/types/browser/clearIndexedDB.d.ts.map +1 -1
- package/types/browser/openPopup.d.ts.map +1 -1
- package/types/constants.d.ts.map +1 -1
- package/types/debug/debugUseState.d.ts.map +1 -1
- package/types/emitter.d.ts.map +1 -1
- package/types/ensure/ensure.d.ts.map +1 -1
- package/types/function/throttle.d.ts.map +1 -1
- package/types/global/globalValue.d.ts.map +1 -1
- package/types/object/decorateObject.d.ts.map +1 -1
- package/types/object/mapObject.d.ts.map +1 -1
- package/types/object/object.d.ts.map +1 -1
- package/types/storage/storage.test.d.ts.map +1 -1
- package/types/string/pickLast.d.ts.map +1 -1
- package/types/string/pluralize.d.ts.map +2 -2
- package/types/string/truncateList.d.ts.map +1 -1
- package/types/time/formatDate.d.ts.map +1 -1
- package/types/time/formatDateRelative.d.ts.map +1 -1
- package/types/time/useTimer.d.ts.map +1 -1
- package/types/url/urlValidate.d.ts.map +1 -1
- package/dist/cjs/array/getRandomItem.js +0 -25
- package/dist/cjs/array/getRandomItem.js.map +0 -6
- package/dist/cjs/array/takeLast.js +0 -21
- package/dist/cjs/array/takeLast.js.map +0 -6
- package/dist/cjs/array/uniqBy.js +0 -28
- package/dist/cjs/array/uniqBy.js.map +0 -6
- package/dist/cjs/assert.js +0 -44
- package/dist/cjs/assert.js.map +0 -6
- package/dist/cjs/async/abortable.js +0 -47
- package/dist/cjs/async/abortable.js.map +0 -6
- package/dist/cjs/async/asyncContext.js +0 -101
- package/dist/cjs/async/asyncContext.js.map +0 -6
- package/dist/cjs/async/idle.js +0 -36
- package/dist/cjs/async/idle.js.map +0 -6
- package/dist/cjs/async/interval.js +0 -25
- package/dist/cjs/async/interval.js.map +0 -6
- package/dist/cjs/async/isAborted.js +0 -21
- package/dist/cjs/async/isAborted.js.map +0 -6
- package/dist/cjs/async/sleep.js +0 -25
- package/dist/cjs/async/sleep.js.map +0 -6
- package/dist/cjs/async/useAsync.js +0 -37
- package/dist/cjs/async/useAsync.js.map +0 -6
- package/dist/cjs/async/useAsyncEffect.js +0 -85
- package/dist/cjs/async/useAsyncEffect.js.map +0 -6
- package/dist/cjs/async/useLazyMount.js +0 -38
- package/dist/cjs/async/useLazyMount.js.map +0 -6
- package/dist/cjs/async/useLazyValue.js +0 -35
- package/dist/cjs/async/useLazyValue.js.map +0 -6
- package/dist/cjs/browser/clearIndexedDB.js +0 -31
- package/dist/cjs/browser/clearIndexedDB.js.map +0 -6
- package/dist/cjs/browser/isActiveElementFormField.js +0 -23
- package/dist/cjs/browser/isActiveElementFormField.js.map +0 -6
- package/dist/cjs/browser/openPopup.js +0 -31
- package/dist/cjs/browser/openPopup.js.map +0 -6
- package/dist/cjs/client-only.js +0 -2
- package/dist/cjs/client-only.js.map +0 -6
- package/dist/cjs/clipboard/clipboard.js +0 -27
- package/dist/cjs/clipboard/clipboard.js.map +0 -6
- package/dist/cjs/color/extractOpacityFromColor.js +0 -29
- package/dist/cjs/color/extractOpacityFromColor.js.map +0 -6
- package/dist/cjs/color/generateColors.js +0 -64
- package/dist/cjs/color/generateColors.js.map +0 -6
- package/dist/cjs/color/lum.js +0 -61
- package/dist/cjs/color/lum.js.map +0 -6
- package/dist/cjs/color/toHex.js +0 -27
- package/dist/cjs/color/toHex.js.map +0 -6
- package/dist/cjs/constants.js +0 -37
- package/dist/cjs/constants.js.map +0 -6
- package/dist/cjs/debug/debugLog.js +0 -24
- package/dist/cjs/debug/debugLog.js.map +0 -6
- package/dist/cjs/debug/debugUseState.js +0 -41
- package/dist/cjs/debug/debugUseState.js.map +0 -6
- package/dist/cjs/emitter.js +0 -183
- package/dist/cjs/emitter.js.map +0 -6
- package/dist/cjs/ensure/ensure.js +0 -40
- package/dist/cjs/ensure/ensure.js.map +0 -6
- package/dist/cjs/ensure/ensureOne.js +0 -27
- package/dist/cjs/ensure/ensureOne.js.map +0 -6
- package/dist/cjs/error/errors.js +0 -50
- package/dist/cjs/error/errors.js.map +0 -6
- package/dist/cjs/function/emptyFn.js +0 -22
- package/dist/cjs/function/emptyFn.js.map +0 -6
- package/dist/cjs/function/identityFn.js +0 -21
- package/dist/cjs/function/identityFn.js.map +0 -6
- package/dist/cjs/function/throttle.js +0 -40
- package/dist/cjs/function/throttle.js.map +0 -6
- package/dist/cjs/global/globalEffect.js +0 -26
- package/dist/cjs/global/globalEffect.js.map +0 -6
- package/dist/cjs/global/globalValue.js +0 -24
- package/dist/cjs/global/globalValue.js.map +0 -6
- package/dist/cjs/index.js +0 -91
- package/dist/cjs/index.js.map +0 -6
- package/dist/cjs/number/formatNumber.js +0 -56
- package/dist/cjs/number/formatNumber.js.map +0 -6
- package/dist/cjs/object/decorateObject.js +0 -26
- package/dist/cjs/object/decorateObject.js.map +0 -6
- package/dist/cjs/object/isEqualDeep.js +0 -23
- package/dist/cjs/object/isEqualDeep.js.map +0 -6
- package/dist/cjs/object/isEqualIdentity.js +0 -21
- package/dist/cjs/object/isEqualIdentity.js.map +0 -6
- package/dist/cjs/object/isEqualJSON.js +0 -21
- package/dist/cjs/object/isEqualJSON.js.map +0 -6
- package/dist/cjs/object/isEqualNever.js +0 -21
- package/dist/cjs/object/isEqualNever.js.map +0 -6
- package/dist/cjs/object/mapObject.js +0 -26
- package/dist/cjs/object/mapObject.js.map +0 -6
- package/dist/cjs/object/object.js +0 -37
- package/dist/cjs/object/object.js.map +0 -6
- package/dist/cjs/object/objectUniqueKey.js +0 -24
- package/dist/cjs/object/objectUniqueKey.js.map +0 -6
- package/dist/cjs/react/createGlobalContext.js +0 -24
- package/dist/cjs/react/createGlobalContext.js.map +0 -6
- package/dist/cjs/react/getCurrentComponentStack.js +0 -51
- package/dist/cjs/react/getCurrentComponentStack.js.map +0 -6
- package/dist/cjs/server/ensureEnv.js +0 -31
- package/dist/cjs/server/ensureEnv.js.map +0 -6
- package/dist/cjs/server/getHeaders.js +0 -26
- package/dist/cjs/server/getHeaders.js.map +0 -6
- package/dist/cjs/server/isServerRuntime.js +0 -23
- package/dist/cjs/server/isServerRuntime.js.map +0 -6
- package/dist/cjs/server/prettyPrintRequest.js +0 -23
- package/dist/cjs/server/prettyPrintRequest.js.map +0 -6
- package/dist/cjs/server/prettyPrintResponse.js +0 -30
- package/dist/cjs/server/prettyPrintResponse.js.map +0 -6
- package/dist/cjs/server/streamToString.js +0 -36
- package/dist/cjs/server/streamToString.js.map +0 -6
- package/dist/cjs/server-only.js +0 -3
- package/dist/cjs/server-only.js.map +0 -6
- package/dist/cjs/storage/createStorage.js +0 -85
- package/dist/cjs/storage/createStorage.js.map +0 -6
- package/dist/cjs/storage/driver.js +0 -33
- package/dist/cjs/storage/driver.js.map +0 -6
- package/dist/cjs/storage/storage.test.js +0 -71
- package/dist/cjs/storage/storage.test.js.map +0 -6
- package/dist/cjs/storage/types.js +0 -14
- package/dist/cjs/storage/types.js.map +0 -6
- package/dist/cjs/string/dedent.js +0 -33
- package/dist/cjs/string/dedent.js.map +0 -6
- package/dist/cjs/string/ellipsis.js +0 -24
- package/dist/cjs/string/ellipsis.js.map +0 -6
- package/dist/cjs/string/hash.js +0 -27
- package/dist/cjs/string/hash.js.map +0 -6
- package/dist/cjs/string/insertAtIndex.js +0 -25
- package/dist/cjs/string/insertAtIndex.js.map +0 -6
- package/dist/cjs/string/nbspLastWord.js +0 -25
- package/dist/cjs/string/nbspLastWord.js.map +0 -6
- package/dist/cjs/string/pickLast.js +0 -24
- package/dist/cjs/string/pickLast.js.map +0 -6
- package/dist/cjs/string/pluralize.js +0 -38
- package/dist/cjs/string/pluralize.js.map +0 -6
- package/dist/cjs/string/randomId.js +0 -21
- package/dist/cjs/string/randomId.js.map +0 -6
- package/dist/cjs/string/slugify.js +0 -21
- package/dist/cjs/string/slugify.js.map +0 -6
- package/dist/cjs/string/truncateList.js +0 -26
- package/dist/cjs/string/truncateList.js.map +0 -6
- package/dist/cjs/time/formatDate.js +0 -51
- package/dist/cjs/time/formatDate.js.map +0 -6
- package/dist/cjs/time/formatDateRelative.js +0 -48
- package/dist/cjs/time/formatDateRelative.js.map +0 -6
- package/dist/cjs/time/formatDistanceToNow.js +0 -24
- package/dist/cjs/time/formatDistanceToNow.js.map +0 -6
- package/dist/cjs/time/time.js +0 -40
- package/dist/cjs/time/time.js.map +0 -6
- package/dist/cjs/time/useTimer.js +0 -51
- package/dist/cjs/time/useTimer.js.map +0 -6
- package/dist/cjs/types/NullToOptional.js +0 -14
- package/dist/cjs/types/NullToOptional.js.map +0 -6
- package/dist/cjs/types/object.js +0 -14
- package/dist/cjs/types/object.js.map +0 -6
- package/dist/cjs/types/react.js +0 -14
- package/dist/cjs/types/react.js.map +0 -6
- package/dist/cjs/types/timer.js +0 -14
- package/dist/cjs/types/timer.js.map +0 -6
- package/dist/cjs/types/tuple.js +0 -14
- package/dist/cjs/types/tuple.js.map +0 -6
- package/dist/cjs/url/urlSanitize.js +0 -31
- package/dist/cjs/url/urlSanitize.js.map +0 -6
- package/dist/cjs/url/urlValidate.js +0 -26
- package/dist/cjs/url/urlValidate.js.map +0 -6
- package/dist/esm/array/getRandomItem.js +0 -9
- package/dist/esm/array/getRandomItem.js.map +0 -6
- package/dist/esm/array/takeLast.js +0 -5
- package/dist/esm/array/takeLast.js.map +0 -6
- package/dist/esm/array/uniqBy.js +0 -12
- package/dist/esm/array/uniqBy.js.map +0 -6
- package/dist/esm/assert.js +0 -28
- package/dist/esm/assert.js.map +0 -6
- package/dist/esm/async/abortable.js +0 -31
- package/dist/esm/async/abortable.js.map +0 -6
- package/dist/esm/async/asyncContext.js +0 -85
- package/dist/esm/async/asyncContext.js.map +0 -6
- package/dist/esm/async/idle.js +0 -21
- package/dist/esm/async/idle.js.map +0 -6
- package/dist/esm/async/interval.js +0 -9
- package/dist/esm/async/interval.js.map +0 -6
- package/dist/esm/async/isAborted.js +0 -5
- package/dist/esm/async/isAborted.js.map +0 -6
- package/dist/esm/async/sleep.js +0 -9
- package/dist/esm/async/sleep.js.map +0 -6
- package/dist/esm/async/useAsync.js +0 -21
- package/dist/esm/async/useAsync.js.map +0 -6
- package/dist/esm/async/useAsyncEffect.js +0 -72
- package/dist/esm/async/useAsyncEffect.js.map +0 -6
- package/dist/esm/async/useLazyMount.js +0 -24
- package/dist/esm/async/useLazyMount.js.map +0 -6
- package/dist/esm/async/useLazyValue.js +0 -21
- package/dist/esm/async/useLazyValue.js.map +0 -6
- package/dist/esm/browser/clearIndexedDB.js +0 -15
- package/dist/esm/browser/clearIndexedDB.js.map +0 -6
- package/dist/esm/browser/isActiveElementFormField.js +0 -7
- package/dist/esm/browser/isActiveElementFormField.js.map +0 -6
- package/dist/esm/browser/openPopup.js +0 -15
- package/dist/esm/browser/openPopup.js.map +0 -6
- package/dist/esm/client-only.js +0 -2
- package/dist/esm/client-only.js.map +0 -6
- package/dist/esm/clipboard/clipboard.js +0 -11
- package/dist/esm/clipboard/clipboard.js.map +0 -6
- package/dist/esm/color/extractOpacityFromColor.js +0 -13
- package/dist/esm/color/extractOpacityFromColor.js.map +0 -6
- package/dist/esm/color/generateColors.js +0 -48
- package/dist/esm/color/generateColors.js.map +0 -6
- package/dist/esm/color/lum.js +0 -45
- package/dist/esm/color/lum.js.map +0 -6
- package/dist/esm/color/toHex.js +0 -11
- package/dist/esm/color/toHex.js.map +0 -6
- package/dist/esm/constants.js +0 -22
- package/dist/esm/constants.js.map +0 -6
- package/dist/esm/debug/debugLog.js +0 -8
- package/dist/esm/debug/debugLog.js.map +0 -6
- package/dist/esm/debug/debugUseState.js +0 -25
- package/dist/esm/debug/debugUseState.js.map +0 -6
- package/dist/esm/emitter.js +0 -167
- package/dist/esm/emitter.js.map +0 -6
- package/dist/esm/ensure/ensure.js +0 -24
- package/dist/esm/ensure/ensure.js.map +0 -6
- package/dist/esm/ensure/ensureOne.js +0 -11
- package/dist/esm/ensure/ensureOne.js.map +0 -6
- package/dist/esm/error/errors.js +0 -34
- package/dist/esm/error/errors.js.map +0 -6
- package/dist/esm/function/emptyFn.js +0 -6
- package/dist/esm/function/emptyFn.js.map +0 -6
- package/dist/esm/function/identityFn.js +0 -5
- package/dist/esm/function/identityFn.js.map +0 -6
- package/dist/esm/function/throttle.js +0 -24
- package/dist/esm/function/throttle.js.map +0 -6
- package/dist/esm/global/globalEffect.js +0 -10
- package/dist/esm/global/globalEffect.js.map +0 -6
- package/dist/esm/global/globalValue.js +0 -8
- package/dist/esm/global/globalValue.js.map +0 -6
- package/dist/esm/number/formatNumber.js +0 -40
- package/dist/esm/number/formatNumber.js.map +0 -6
- package/dist/esm/object/decorateObject.js +0 -10
- package/dist/esm/object/decorateObject.js.map +0 -6
- package/dist/esm/object/isEqualDeep.js +0 -8
- package/dist/esm/object/isEqualDeep.js.map +0 -6
- package/dist/esm/object/isEqualIdentity.js +0 -5
- package/dist/esm/object/isEqualIdentity.js.map +0 -6
- package/dist/esm/object/isEqualJSON.js +0 -5
- package/dist/esm/object/isEqualJSON.js.map +0 -6
- package/dist/esm/object/isEqualNever.js +0 -5
- package/dist/esm/object/isEqualNever.js.map +0 -6
- package/dist/esm/object/mapObject.js +0 -10
- package/dist/esm/object/mapObject.js.map +0 -6
- package/dist/esm/object/object.js +0 -21
- package/dist/esm/object/object.js.map +0 -6
- package/dist/esm/object/objectUniqueKey.js +0 -8
- package/dist/esm/object/objectUniqueKey.js.map +0 -6
- package/dist/esm/react/createGlobalContext.js +0 -9
- package/dist/esm/react/createGlobalContext.js.map +0 -6
- package/dist/esm/react/getCurrentComponentStack.js +0 -27
- package/dist/esm/react/getCurrentComponentStack.js.map +0 -6
- package/dist/esm/server/ensureEnv.js +0 -15
- package/dist/esm/server/ensureEnv.js.map +0 -6
- package/dist/esm/server/getHeaders.js +0 -10
- package/dist/esm/server/getHeaders.js.map +0 -6
- package/dist/esm/server/isServerRuntime.js +0 -7
- package/dist/esm/server/isServerRuntime.js.map +0 -6
- package/dist/esm/server/prettyPrintRequest.js +0 -7
- package/dist/esm/server/prettyPrintRequest.js.map +0 -6
- package/dist/esm/server/prettyPrintResponse.js +0 -15
- package/dist/esm/server/prettyPrintResponse.js.map +0 -6
- package/dist/esm/server/streamToString.js +0 -20
- package/dist/esm/server/streamToString.js.map +0 -6
- package/dist/esm/server-only.js +0 -3
- package/dist/esm/server-only.js.map +0 -6
- package/dist/esm/storage/createStorage.js +0 -69
- package/dist/esm/storage/createStorage.js.map +0 -6
- package/dist/esm/storage/driver.js +0 -17
- package/dist/esm/storage/driver.js.map +0 -6
- package/dist/esm/storage/storage.test.js +0 -73
- package/dist/esm/storage/storage.test.js.map +0 -6
- package/dist/esm/storage/types.js +0 -1
- package/dist/esm/storage/types.js.map +0 -6
- package/dist/esm/string/dedent.js +0 -17
- package/dist/esm/string/dedent.js.map +0 -6
- package/dist/esm/string/ellipsis.js +0 -8
- package/dist/esm/string/ellipsis.js.map +0 -6
- package/dist/esm/string/hash.js +0 -11
- package/dist/esm/string/hash.js.map +0 -6
- package/dist/esm/string/insertAtIndex.js +0 -9
- package/dist/esm/string/insertAtIndex.js.map +0 -6
- package/dist/esm/string/nbspLastWord.js +0 -9
- package/dist/esm/string/nbspLastWord.js.map +0 -6
- package/dist/esm/string/pickLast.js +0 -8
- package/dist/esm/string/pickLast.js.map +0 -6
- package/dist/esm/string/pluralize.js +0 -22
- package/dist/esm/string/pluralize.js.map +0 -6
- package/dist/esm/string/randomId.js +0 -5
- package/dist/esm/string/randomId.js.map +0 -6
- package/dist/esm/string/slugify.js +0 -5
- package/dist/esm/string/slugify.js.map +0 -6
- package/dist/esm/string/truncateList.js +0 -10
- package/dist/esm/string/truncateList.js.map +0 -6
- package/dist/esm/time/formatDate.js +0 -35
- package/dist/esm/time/formatDate.js.map +0 -6
- package/dist/esm/time/formatDateRelative.js +0 -32
- package/dist/esm/time/formatDateRelative.js.map +0 -6
- package/dist/esm/time/formatDistanceToNow.js +0 -8
- package/dist/esm/time/formatDistanceToNow.js.map +0 -6
- package/dist/esm/time/time.js +0 -24
- package/dist/esm/time/time.js.map +0 -6
- package/dist/esm/time/useTimer.js +0 -35
- package/dist/esm/time/useTimer.js.map +0 -6
- package/dist/esm/types/NullToOptional.js +0 -1
- package/dist/esm/types/NullToOptional.js.map +0 -6
- package/dist/esm/types/object.js +0 -1
- package/dist/esm/types/object.js.map +0 -6
- package/dist/esm/types/react.js +0 -1
- package/dist/esm/types/react.js.map +0 -6
- package/dist/esm/types/timer.js +0 -1
- package/dist/esm/types/timer.js.map +0 -6
- package/dist/esm/types/tuple.js +0 -1
- package/dist/esm/types/tuple.js.map +0 -6
- package/dist/esm/url/urlSanitize.js +0 -15
- package/dist/esm/url/urlSanitize.js.map +0 -6
- package/dist/esm/url/urlValidate.js +0 -10
- package/dist/esm/url/urlValidate.js.map +0 -6
package/src/emitter.tsx
CHANGED
|
@@ -16,7 +16,7 @@ import type { JSX, PropsWithChildren } from 'react'
|
|
|
16
16
|
function useGet<A>(
|
|
17
17
|
currentValue: A,
|
|
18
18
|
initialValue?: any,
|
|
19
|
-
forwardToFunction?: boolean
|
|
19
|
+
forwardToFunction?: boolean,
|
|
20
20
|
): () => A {
|
|
21
21
|
const curRef = React.useRef<any>(initialValue ?? currentValue)
|
|
22
22
|
|
|
@@ -29,7 +29,7 @@ function useGet<A>(
|
|
|
29
29
|
forwardToFunction
|
|
30
30
|
? (...args) => curRef.current?.apply(null, args)
|
|
31
31
|
: () => curRef.current,
|
|
32
|
-
[curRef, forwardToFunction]
|
|
32
|
+
[curRef, forwardToFunction],
|
|
33
33
|
)
|
|
34
34
|
}
|
|
35
35
|
|
|
@@ -78,7 +78,7 @@ this will emit the same value again, which can be desirable, but we warn to ensu
|
|
|
78
78
|
|
|
79
79
|
- if you want this behavior, add { comparator: isEqualNever }
|
|
80
80
|
- if you want only non-equal values: { comparator: isEqualIdentity }
|
|
81
|
-
- if you want only deeply non-equal values: { comparator: isEqualDeep }
|
|
81
|
+
- if you want only deeply non-equal values: { comparator: isEqualDeep }`,
|
|
82
82
|
)
|
|
83
83
|
}
|
|
84
84
|
}
|
|
@@ -110,7 +110,7 @@ this will emit the same value again, which can be desirable, but we warn to ensu
|
|
|
110
110
|
export function createGlobalEmitter<T>(
|
|
111
111
|
name: string,
|
|
112
112
|
defaultValue: T,
|
|
113
|
-
options?: CreateEmitterOpts<T
|
|
113
|
+
options?: CreateEmitterOpts<T>,
|
|
114
114
|
): Emitter<T> {
|
|
115
115
|
return globalValue(name, () => createEmitter(name, defaultValue, options))
|
|
116
116
|
}
|
|
@@ -118,7 +118,7 @@ export function createGlobalEmitter<T>(
|
|
|
118
118
|
export function createEmitter<T>(
|
|
119
119
|
name: string,
|
|
120
120
|
defaultValue: T,
|
|
121
|
-
options?: CreateEmitterOpts<T
|
|
121
|
+
options?: CreateEmitterOpts<T>,
|
|
122
122
|
): Emitter<T> {
|
|
123
123
|
const existing = createOrUpdateCache(name, defaultValue) as T
|
|
124
124
|
return new Emitter<T>(existing || defaultValue, { name, ...options })
|
|
@@ -130,7 +130,7 @@ export type EmitterType<E extends Emitter<any>> =
|
|
|
130
130
|
export const useEmitter = <E extends Emitter<any>>(
|
|
131
131
|
emitter: E,
|
|
132
132
|
cb: (cb: EmitterType<E>) => void,
|
|
133
|
-
args?: any[]
|
|
133
|
+
args?: any[],
|
|
134
134
|
): void => {
|
|
135
135
|
const getCallback = useGet(cb)
|
|
136
136
|
|
|
@@ -150,7 +150,7 @@ export const useEmitter = <E extends Emitter<any>>(
|
|
|
150
150
|
|
|
151
151
|
export const useEmitterValue = <E extends Emitter<any>>(
|
|
152
152
|
emitter: E,
|
|
153
|
-
options?: { disable?: boolean }
|
|
153
|
+
options?: { disable?: boolean },
|
|
154
154
|
): EmitterType<E> => {
|
|
155
155
|
const disabled = options?.disable
|
|
156
156
|
|
|
@@ -204,7 +204,7 @@ export const useEmitterSelector = <E extends Emitter<any>, T extends EmitterType
|
|
|
204
204
|
disable?: boolean
|
|
205
205
|
lazy?: boolean
|
|
206
206
|
},
|
|
207
|
-
args: any[] = EMPTY_ARRAY
|
|
207
|
+
args: any[] = EMPTY_ARRAY,
|
|
208
208
|
): R => {
|
|
209
209
|
const [state, setState] = useState<R>(() => selector(emitter.value))
|
|
210
210
|
const disabled = options?.disable
|
|
@@ -240,7 +240,7 @@ export const useEmitterSelector = <E extends Emitter<any>, T extends EmitterType
|
|
|
240
240
|
export const useEmittersSelector = <const E extends readonly Emitter<any>[], R>(
|
|
241
241
|
emitters: E,
|
|
242
242
|
selector: (values: { [K in keyof E]: EmitterType<E[K]> }) => R,
|
|
243
|
-
options?: { disable?: boolean; isEqual?: (a: R, b: R) => boolean }
|
|
243
|
+
options?: { disable?: boolean; isEqual?: (a: R, b: R) => boolean },
|
|
244
244
|
): R => {
|
|
245
245
|
const getSelector = useGet(selector)
|
|
246
246
|
const disabled = options?.disable
|
|
@@ -290,23 +290,23 @@ export const useEmittersSelector = <const E extends readonly Emitter<any>[], R>(
|
|
|
290
290
|
}
|
|
291
291
|
|
|
292
292
|
export const createUseEmitter = <E extends Emitter<any>>(
|
|
293
|
-
emitter: E
|
|
293
|
+
emitter: E,
|
|
294
294
|
): ((cb: (val: EmitterType<E>) => void, args?: any[]) => void) => {
|
|
295
295
|
return (cb: (val: EmitterType<E>) => void, args?: any[]) =>
|
|
296
296
|
useEmitter(emitter, cb, args)
|
|
297
297
|
}
|
|
298
298
|
|
|
299
299
|
export const createUseSelector = <E extends Emitter<any>>(
|
|
300
|
-
emitter: E
|
|
300
|
+
emitter: E,
|
|
301
301
|
): (<R>(
|
|
302
302
|
selector: (value: EmitterType<E>) => R,
|
|
303
303
|
options?: { disable?: boolean; lazy?: boolean },
|
|
304
|
-
args?: any[]
|
|
304
|
+
args?: any[],
|
|
305
305
|
) => R) => {
|
|
306
306
|
return <R,>(
|
|
307
307
|
selector: (value: EmitterType<E>) => R,
|
|
308
308
|
options?: { disable?: boolean; lazy?: boolean },
|
|
309
|
-
args?: any[]
|
|
309
|
+
args?: any[],
|
|
310
310
|
): R => {
|
|
311
311
|
return useEmitterSelector(emitter, selector, options, args)
|
|
312
312
|
}
|
|
@@ -315,7 +315,7 @@ export const createUseSelector = <E extends Emitter<any>>(
|
|
|
315
315
|
export function createContextualEmitter<T>(
|
|
316
316
|
name: string,
|
|
317
317
|
defaultValue: T,
|
|
318
|
-
defaultOptions?: Omit<EmitterOptions<T>, 'name'
|
|
318
|
+
defaultOptions?: Omit<EmitterOptions<T>, 'name'>,
|
|
319
319
|
): readonly [
|
|
320
320
|
() => Emitter<T>,
|
|
321
321
|
(props: PropsWithChildren<{ value?: T; silent?: boolean }>) => JSX.Element,
|
|
@@ -323,7 +323,7 @@ export function createContextualEmitter<T>(
|
|
|
323
323
|
const id = Math.random().toString(36)
|
|
324
324
|
const EmitterContext = createGlobalContext<Emitter<T> | null>(
|
|
325
325
|
`contextual-emitter/${id}`,
|
|
326
|
-
null
|
|
326
|
+
null,
|
|
327
327
|
)
|
|
328
328
|
|
|
329
329
|
const useContextEmitter = () => {
|
|
@@ -342,7 +342,7 @@ export function createContextualEmitter<T>(
|
|
|
342
342
|
const ProvideEmitter = (props: ProvideEmitterProps) => {
|
|
343
343
|
const { children, value, silent } = props
|
|
344
344
|
const [emitter] = useState(
|
|
345
|
-
() => new Emitter<T>(value ?? defaultValue, { name, silent, ...defaultOptions })
|
|
345
|
+
() => new Emitter<T>(value ?? defaultValue, { name, silent, ...defaultOptions }),
|
|
346
346
|
)
|
|
347
347
|
|
|
348
348
|
useLayoutEffect(() => {
|
package/src/ensure/ensure.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { EnsureError } from '../error/errors'
|
|
|
2
2
|
|
|
3
3
|
export function ensureExists<T>(
|
|
4
4
|
value: T | undefined | null,
|
|
5
|
-
msg = ''
|
|
5
|
+
msg = '',
|
|
6
6
|
): asserts value is T {
|
|
7
7
|
if (value === undefined || value === null) {
|
|
8
8
|
throw new EnsureError(`Invalid nullish value (${value}): ${msg}`)
|
|
@@ -11,7 +11,7 @@ export function ensureExists<T>(
|
|
|
11
11
|
|
|
12
12
|
export function ensure<T>(
|
|
13
13
|
value: T,
|
|
14
|
-
msg = ''
|
|
14
|
+
msg = '',
|
|
15
15
|
): asserts value is Exclude<T, null | undefined | false> {
|
|
16
16
|
if (!value) {
|
|
17
17
|
throw new EnsureError(`ensure() invalid: (${value}): ${msg} ${new Error().stack}`)
|
package/src/function/throttle.ts
CHANGED
|
@@ -25,7 +25,7 @@ interface Throttler<T extends unknown[]> {
|
|
|
25
25
|
export function throttle<T extends unknown[]>(
|
|
26
26
|
callback: (...args: T) => unknown,
|
|
27
27
|
wait = 0,
|
|
28
|
-
{ start = true, middle = true, once = false }: ThrottleOptions = {}
|
|
28
|
+
{ start = true, middle = true, once = false }: ThrottleOptions = {},
|
|
29
29
|
): Throttler<T> {
|
|
30
30
|
let innerStart = start
|
|
31
31
|
let last = 0
|
|
@@ -52,7 +52,7 @@ export function throttle<T extends unknown[]>(
|
|
|
52
52
|
callback.apply(this, args)
|
|
53
53
|
if (once) fn.cancel()
|
|
54
54
|
},
|
|
55
|
-
!middle ? wait : wait - delta
|
|
55
|
+
!middle ? wait : wait - delta,
|
|
56
56
|
)
|
|
57
57
|
}
|
|
58
58
|
}
|
|
@@ -66,7 +66,7 @@ export function throttle<T extends unknown[]>(
|
|
|
66
66
|
export function debounce<T extends unknown[]>(
|
|
67
67
|
callback: (...args: T) => unknown,
|
|
68
68
|
wait = 0,
|
|
69
|
-
{ start = false, middle = false, once = false }: ThrottleOptions = {}
|
|
69
|
+
{ start = false, middle = false, once = false }: ThrottleOptions = {},
|
|
70
70
|
): Throttler<T> {
|
|
71
71
|
return throttle(callback, wait, { start, middle, once })
|
|
72
72
|
}
|
package/src/object/mapObject.ts
CHANGED
package/src/object/object.ts
CHANGED
|
@@ -7,17 +7,17 @@ import type {
|
|
|
7
7
|
|
|
8
8
|
export function postfixObjKeys<A extends { [key: string]: string }, B extends string>(
|
|
9
9
|
obj: A,
|
|
10
|
-
postfix: B
|
|
10
|
+
postfix: B,
|
|
11
11
|
): {
|
|
12
12
|
[Key in `${keyof A extends string ? keyof A : never}${B}`]: string
|
|
13
13
|
} {
|
|
14
14
|
return Object.fromEntries(
|
|
15
|
-
Object.entries(obj).map(([k, v]) => [`${k}${postfix}`, v])
|
|
15
|
+
Object.entries(obj).map(([k, v]) => [`${k}${postfix}`, v]),
|
|
16
16
|
) as any
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
export function objectFromEntries<ARR_T extends EntriesType>(
|
|
20
|
-
arr: ARR_T
|
|
20
|
+
arr: ARR_T,
|
|
21
21
|
): EntriesToObject<ARR_T> {
|
|
22
22
|
return Object.fromEntries(arr) as EntriesToObject<ARR_T>
|
|
23
23
|
}
|
|
@@ -27,7 +27,7 @@ export function objectKeys<O extends object>(obj: O) {
|
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
export function objectEntries<OBJ_T extends ObjectType>(
|
|
30
|
-
obj: OBJ_T
|
|
30
|
+
obj: OBJ_T,
|
|
31
31
|
): ObjectEntries<OBJ_T> {
|
|
32
32
|
return Object.entries(obj) as ObjectEntries<OBJ_T>
|
|
33
33
|
}
|
|
@@ -65,7 +65,7 @@ describe('storage driver system', () => {
|
|
|
65
65
|
|
|
66
66
|
it('should handle JSON serialization', () => {
|
|
67
67
|
const storage = createStorage<'obj', { name: string; count: number }>(
|
|
68
|
-
`test-${Date.now()}-3
|
|
68
|
+
`test-${Date.now()}-3`,
|
|
69
69
|
)
|
|
70
70
|
const obj = { name: 'test', count: 42 }
|
|
71
71
|
storage.set('obj', obj)
|
package/src/string/pickLast.ts
CHANGED
package/src/string/pluralize.ts
CHANGED
|
@@ -7,9 +7,12 @@ export function pluralize(count: number, singular: string, plural: string): stri
|
|
|
7
7
|
return `${count} ${singular}`
|
|
8
8
|
case 'other':
|
|
9
9
|
return `${count} ${plural}`
|
|
10
|
-
|
|
10
|
+
case 'zero':
|
|
11
|
+
case 'two':
|
|
12
|
+
case 'few':
|
|
13
|
+
case 'many':
|
|
11
14
|
throw new Error(
|
|
12
|
-
`Can't pluralize: ${grammaticalNumber} for ${count} / ${singular} / ${plural}
|
|
15
|
+
`Can't pluralize: ${grammaticalNumber} for ${count} / ${singular} / ${plural}`,
|
|
13
16
|
)
|
|
14
17
|
}
|
|
15
18
|
}
|
package/src/time/formatDate.tsx
CHANGED
|
@@ -33,7 +33,7 @@ export function formatDateRelative(date: Date | string | number): string {
|
|
|
33
33
|
new Intl.DateTimeFormat('en', {
|
|
34
34
|
hour: 'numeric',
|
|
35
35
|
minute: 'numeric',
|
|
36
|
-
}).format(messageDate)
|
|
36
|
+
}).format(messageDate),
|
|
37
37
|
)
|
|
38
38
|
}
|
|
39
39
|
|
|
@@ -43,7 +43,7 @@ export function formatDateRelative(date: Date | string | number): string {
|
|
|
43
43
|
new Intl.DateTimeFormat('en', {
|
|
44
44
|
hour: 'numeric',
|
|
45
45
|
minute: 'numeric',
|
|
46
|
-
}).format(messageDate)
|
|
46
|
+
}).format(messageDate),
|
|
47
47
|
)
|
|
48
48
|
}
|
|
49
49
|
|
|
@@ -55,7 +55,7 @@ export function formatDateRelative(date: Date | string | number): string {
|
|
|
55
55
|
day: 'numeric',
|
|
56
56
|
hour: 'numeric',
|
|
57
57
|
minute: 'numeric',
|
|
58
|
-
}).format(messageDate)
|
|
58
|
+
}).format(messageDate),
|
|
59
59
|
)
|
|
60
60
|
}
|
|
61
61
|
|
|
@@ -67,6 +67,6 @@ export function formatDateRelative(date: Date | string | number): string {
|
|
|
67
67
|
year: 'numeric',
|
|
68
68
|
hour: 'numeric',
|
|
69
69
|
minute: 'numeric',
|
|
70
|
-
}).format(messageDate)
|
|
70
|
+
}).format(messageDate),
|
|
71
71
|
)
|
|
72
72
|
}
|
package/src/time/useTimer.ts
CHANGED
|
@@ -36,7 +36,7 @@ export const useTimer = (): UseTimerReturn => {
|
|
|
36
36
|
}, 1000)
|
|
37
37
|
}
|
|
38
38
|
},
|
|
39
|
-
[clearTimer]
|
|
39
|
+
[clearTimer],
|
|
40
40
|
)
|
|
41
41
|
|
|
42
42
|
const pauseTimer = useCallback(() => {
|
|
@@ -75,6 +75,6 @@ export const useTimer = (): UseTimerReturn => {
|
|
|
75
75
|
resume: resumeTimer,
|
|
76
76
|
clear: clearTimer,
|
|
77
77
|
}),
|
|
78
|
-
[timerCount, clearTimer, pauseTimer, resetTimer, resumeTimer]
|
|
78
|
+
[timerCount, clearTimer, pauseTimer, resetTimer, resumeTimer],
|
|
79
79
|
)
|
|
80
80
|
}
|
package/src/url/urlValidate.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// Source: https://stackoverflow.com/a/8234912/2013580
|
|
2
2
|
|
|
3
3
|
const urlRegExp = new RegExp(
|
|
4
|
-
/((([A-Za-z]{3,9}:(?:\/\/)?)(?:[-;:&=+$,\w]+@)?[A-Za-z0-9.-]+|(?:www.|[-;:&=+$,\w]+@)[A-Za-z0-9.-]+)((?:\/[+~%/.\w-_]*)?\??(?:[-+=&;%@.\w_]*)#?(?:[\w]*))?)
|
|
4
|
+
/((([A-Za-z]{3,9}:(?:\/\/)?)(?:[-;:&=+$,\w]+@)?[A-Za-z0-9.-]+|(?:www.|[-;:&=+$,\w]+@)[A-Za-z0-9.-]+)((?:\/[+~%/.\w-_]*)?\??(?:[-+=&;%@.\w_]*)#?(?:[\w]*))?)/,
|
|
5
5
|
)
|
|
6
6
|
|
|
7
7
|
export function urlValidate(url: string): boolean {
|
|
@@ -6,6 +6,6 @@
|
|
|
6
6
|
],
|
|
7
7
|
"version": 3,
|
|
8
8
|
"sourcesContent": [
|
|
9
|
-
"import { AbortError, EnsureError } from '../error/errors'\n\ntype AbortableOptions = { message?: string }\n\nexport function abortable<T>(cb: () => T, options?: AbortableOptions): T | undefined\nexport function abortable<T>(\n cb: () => Promise<T>,\n options?: AbortableOptions
|
|
9
|
+
"import { AbortError, EnsureError } from '../error/errors'\n\ntype AbortableOptions = { message?: string }\n\nexport function abortable<T>(cb: () => T, options?: AbortableOptions): T | undefined\nexport function abortable<T>(\n cb: () => Promise<T>,\n options?: AbortableOptions,\n): Promise<T | undefined>\nexport function abortable<T>(\n cb: () => T | Promise<T>,\n options?: AbortableOptions,\n): T | undefined | Promise<T | undefined> {\n try {\n const value = cb()\n\n if (value instanceof Promise) {\n return value.catch((err: unknown) => {\n if (didAbort(err, options)) {\n return undefined\n }\n throw err\n })\n }\n\n return value\n } catch (err) {\n if (didAbort(err, options)) {\n return undefined\n }\n throw err\n }\n}\n\nexport function didAbort(err: unknown, options?: AbortableOptions): boolean {\n if (err instanceof AbortError || err instanceof EnsureError) {\n if (options?.message) {\n console.warn(`Aborted: ${options.message}`)\n }\n return true\n }\n return false\n}\n\nexport function handleAbortError(error: any, debug?: boolean): void {\n if (error instanceof AbortError || error instanceof EnsureError) {\n if (debug || process.env.DEBUG) {\n console.info(`🐛 useAsyncEffect aborted: ${error.message}`)\n }\n return\n }\n\n // js handles aborting a promise as an error. ignore them since they're\n // a normal part of the expected async workflow\n if (typeof error === 'object' && error.name === 'AbortError') {\n return\n }\n\n throw error\n}\n"
|
|
10
10
|
]
|
|
11
11
|
}
|
|
@@ -6,6 +6,6 @@
|
|
|
6
6
|
],
|
|
7
7
|
"version": 3,
|
|
8
8
|
"sourcesContent": [
|
|
9
|
-
"import { isServerRuntime } from '../server/isServerRuntime'\n\ninterface AsyncContext<T> {\n get(): T | undefined\n run<R>(value: T, fn: () => R | Promise<R>): Promise<R>\n}\n\ninterface NodeAsyncLocalStorage<T> {\n getStore(): T | undefined\n run<R>(store: T, callback: () => R): R\n}\n\ninterface AsyncLocalStorageConstructor {\n new <T>(): NodeAsyncLocalStorage<T>\n}\n\nlet nodeAsyncLocalStorageCache: AsyncLocalStorageConstructor | null = null\n\n// hide from vite/esbuild static analysis to avoid browser compat warning\nconst nodeModuleId = ['node', 'async_hooks'].join(':')\n\nasync function getNodeAsyncLocalStorage(): Promise<AsyncLocalStorageConstructor | null> {\n if (!nodeAsyncLocalStorageCache) {\n try {\n const module = await import(/* @vite-ignore */ nodeModuleId)\n nodeAsyncLocalStorageCache =\n module.AsyncLocalStorage as AsyncLocalStorageConstructor\n } catch {\n return null\n }\n }\n return nodeAsyncLocalStorageCache\n}\n\nexport function createAsyncContext<T>(): AsyncContext<T> {\n if (isServerRuntime()) {\n let storage: NodeAsyncLocalStorage<T> | null = null\n\n getNodeAsyncLocalStorage().then((AsyncLocalStorage) => {\n if (AsyncLocalStorage && !storage) {\n storage = new AsyncLocalStorage<T>()\n }\n })\n\n return {\n get(): T | undefined {\n if (!storage) {\n console.warn(`⚠️ called AsyncContext before load!`)\n return\n }\n\n return storage.getStore()\n },\n\n async run<R>(value: T, fn: () => R | Promise<R>): Promise<R> {\n if (!storage) {\n throw new Error(`⚠️ called AsyncContext before load!`)\n }\n return storage.run(value, fn)\n },\n }\n } else {\n // browser implementation using promise patching\n return createBrowserAsyncContext<T>()\n }\n}\n\nfunction createBrowserAsyncContext<T>(): AsyncContext<T> {\n let currentContext: T | undefined\n const contextStack: (T | undefined)[] = []\n\n return {\n get(): T | undefined {\n return currentContext\n },\n async run<R>(value: T, fn: () => R | Promise<R>): Promise<R> {\n const prevContext = currentContext\n currentContext = value\n contextStack.push(prevContext)\n\n // store original Promise methods\n const OriginalPromise = Promise\n const OriginalThen = OriginalPromise.prototype.then\n const OriginalCatch = OriginalPromise.prototype.catch\n const OriginalFinally = OriginalPromise.prototype.finally\n\n function wrapCallback(\n callback: Function | undefined | null,\n context: T | undefined
|
|
9
|
+
"import { isServerRuntime } from '../server/isServerRuntime'\n\ninterface AsyncContext<T> {\n get(): T | undefined\n run<R>(value: T, fn: () => R | Promise<R>): Promise<R>\n}\n\ninterface NodeAsyncLocalStorage<T> {\n getStore(): T | undefined\n run<R>(store: T, callback: () => R): R\n}\n\ninterface AsyncLocalStorageConstructor {\n new <T>(): NodeAsyncLocalStorage<T>\n}\n\nlet nodeAsyncLocalStorageCache: AsyncLocalStorageConstructor | null = null\n\n// hide from vite/esbuild static analysis to avoid browser compat warning\nconst nodeModuleId = ['node', 'async_hooks'].join(':')\n\nasync function getNodeAsyncLocalStorage(): Promise<AsyncLocalStorageConstructor | null> {\n if (!nodeAsyncLocalStorageCache) {\n try {\n const module = await import(/* @vite-ignore */ nodeModuleId)\n nodeAsyncLocalStorageCache =\n module.AsyncLocalStorage as AsyncLocalStorageConstructor\n } catch {\n return null\n }\n }\n return nodeAsyncLocalStorageCache\n}\n\nexport function createAsyncContext<T>(): AsyncContext<T> {\n if (isServerRuntime()) {\n let storage: NodeAsyncLocalStorage<T> | null = null\n\n getNodeAsyncLocalStorage().then((AsyncLocalStorage) => {\n if (AsyncLocalStorage && !storage) {\n storage = new AsyncLocalStorage<T>()\n }\n })\n\n return {\n get(): T | undefined {\n if (!storage) {\n console.warn(`⚠️ called AsyncContext before load!`)\n return\n }\n\n return storage.getStore()\n },\n\n async run<R>(value: T, fn: () => R | Promise<R>): Promise<R> {\n if (!storage) {\n throw new Error(`⚠️ called AsyncContext before load!`)\n }\n return storage.run(value, fn)\n },\n }\n } else {\n // browser implementation using promise patching\n return createBrowserAsyncContext<T>()\n }\n}\n\nfunction createBrowserAsyncContext<T>(): AsyncContext<T> {\n let currentContext: T | undefined\n const contextStack: (T | undefined)[] = []\n\n return {\n get(): T | undefined {\n return currentContext\n },\n async run<R>(value: T, fn: () => R | Promise<R>): Promise<R> {\n const prevContext = currentContext\n currentContext = value\n contextStack.push(prevContext)\n\n // store original Promise methods\n const OriginalPromise = Promise\n const OriginalThen = OriginalPromise.prototype.then\n const OriginalCatch = OriginalPromise.prototype.catch\n const OriginalFinally = OriginalPromise.prototype.finally\n\n function wrapCallback(\n callback: Function | undefined | null,\n context: T | undefined,\n ): Function | undefined | null {\n if (!callback) return callback\n return (...args: any[]) => {\n const prevContext = currentContext\n currentContext = context\n try {\n return callback(...args)\n } finally {\n currentContext = prevContext\n }\n }\n }\n\n // patch Promise methods to capture and restore context\n // eslint-disable-next-line no-then-property -- intentional patching for context propagation\n OriginalPromise.prototype.then = function (\n this: Promise<any>,\n onFulfilled?: any,\n onRejected?: any,\n ): Promise<any> {\n const context = currentContext\n return OriginalThen.call(\n this,\n wrapCallback(onFulfilled, context) as any,\n wrapCallback(onRejected, context) as any,\n )\n }\n\n OriginalPromise.prototype.catch = function (\n this: Promise<any>,\n onRejected?: any,\n ): Promise<any> {\n const context = currentContext\n return OriginalCatch.call(this, wrapCallback(onRejected, context) as any)\n }\n\n OriginalPromise.prototype.finally = function (\n this: Promise<any>,\n onFinally?: any,\n ): Promise<any> {\n const context = currentContext\n return OriginalFinally.call(this, wrapCallback(onFinally, context) as any)\n }\n\n try {\n const result = await fn()\n return result\n } finally {\n // restore original Promise methods\n // eslint-disable-next-line no-then-property -- restoring original methods\n OriginalPromise.prototype.then = OriginalThen\n OriginalPromise.prototype.catch = OriginalCatch\n OriginalPromise.prototype.finally = OriginalFinally\n\n contextStack.pop()\n currentContext = prevContext\n }\n },\n }\n}\n"
|
|
10
10
|
]
|
|
11
11
|
}
|
|
@@ -6,6 +6,6 @@
|
|
|
6
6
|
],
|
|
7
7
|
"version": 3,
|
|
8
8
|
"sourcesContent": [
|
|
9
|
-
"interface AsyncContext<T> {\n get(): T | undefined\n run<R>(value: T, fn: () => R | Promise<R>): Promise<R>\n}\n\n// react native implementation - no node:async_hooks available\nexport function createAsyncContext<T>(): AsyncContext<T> {\n let currentContext: T | undefined\n const contextStack: (T | undefined)[] = []\n\n return {\n get(): T | undefined {\n return currentContext\n },\n async run<R>(value: T, fn: () => R | Promise<R>): Promise<R> {\n const prevContext = currentContext\n currentContext = value\n contextStack.push(prevContext)\n\n // store original Promise methods\n const OriginalPromise = Promise\n const OriginalThen = OriginalPromise.prototype.then\n const OriginalCatch = OriginalPromise.prototype.catch\n const OriginalFinally = OriginalPromise.prototype.finally\n\n function wrapCallback(\n callback: Function | undefined | null,\n context: T | undefined
|
|
9
|
+
"interface AsyncContext<T> {\n get(): T | undefined\n run<R>(value: T, fn: () => R | Promise<R>): Promise<R>\n}\n\n// react native implementation - no node:async_hooks available\nexport function createAsyncContext<T>(): AsyncContext<T> {\n let currentContext: T | undefined\n const contextStack: (T | undefined)[] = []\n\n return {\n get(): T | undefined {\n return currentContext\n },\n async run<R>(value: T, fn: () => R | Promise<R>): Promise<R> {\n const prevContext = currentContext\n currentContext = value\n contextStack.push(prevContext)\n\n // store original Promise methods\n const OriginalPromise = Promise\n const OriginalThen = OriginalPromise.prototype.then\n const OriginalCatch = OriginalPromise.prototype.catch\n const OriginalFinally = OriginalPromise.prototype.finally\n\n function wrapCallback(\n callback: Function | undefined | null,\n context: T | undefined,\n ): Function | undefined | null {\n if (!callback) return callback\n return (...args: any[]) => {\n const prevContext = currentContext\n currentContext = context\n try {\n return callback(...args)\n } finally {\n currentContext = prevContext\n }\n }\n }\n\n // patch Promise methods to capture and restore context\n // eslint-disable-next-line no-then-property -- intentional patching for context propagation\n OriginalPromise.prototype.then = function (\n this: Promise<any>,\n onFulfilled?: any,\n onRejected?: any,\n ): Promise<any> {\n const context = currentContext\n return OriginalThen.call(\n this,\n wrapCallback(onFulfilled, context) as any,\n wrapCallback(onRejected, context) as any,\n )\n }\n\n OriginalPromise.prototype.catch = function (\n this: Promise<any>,\n onRejected?: any,\n ): Promise<any> {\n const context = currentContext\n return OriginalCatch.call(this, wrapCallback(onRejected, context) as any)\n }\n\n OriginalPromise.prototype.finally = function (\n this: Promise<any>,\n onFinally?: any,\n ): Promise<any> {\n const context = currentContext\n return OriginalFinally.call(this, wrapCallback(onFinally, context) as any)\n }\n\n try {\n const result = await fn()\n return result\n } finally {\n // restore original Promise methods\n // eslint-disable-next-line no-then-property -- restoring original methods\n OriginalPromise.prototype.then = OriginalThen\n OriginalPromise.prototype.catch = OriginalCatch\n OriginalPromise.prototype.finally = OriginalFinally\n\n contextStack.pop()\n currentContext = prevContext\n }\n },\n }\n}\n"
|
|
10
10
|
]
|
|
11
11
|
}
|
|
@@ -6,6 +6,6 @@
|
|
|
6
6
|
],
|
|
7
7
|
"version": 3,
|
|
8
8
|
"sourcesContent": [
|
|
9
|
-
"import { AbortError } from '../error/errors'\nimport { sleep } from './sleep'\n\n/**\n * Two helpers wrapping requestIdleCallback in async, abortable.\n */\n\ntype CallbackFn = (...args: any[]) => any
|
|
9
|
+
"import { AbortError } from '../error/errors'\nimport { sleep } from './sleep'\n\n/**\n * Two helpers wrapping requestIdleCallback in async, abortable.\n */\n\ntype CallbackFn = (...args: any[]) => any\ntype CallbackerFn = (cb: CallbackFn) => void\n\nconst idleCb: CallbackerFn =\n typeof requestIdleCallback === 'undefined'\n ? (cb: CallbackFn) => setTimeout(cb, 1)\n : requestIdleCallback\n\nconst idleAsync = () => {\n return new Promise((res) => {\n idleCb(res)\n })\n}\n\nexport type IdleOptions = {\n max?: number\n min?: number\n fully?: boolean\n}\n\nexport const idle = async (\n options?: IdleOptions,\n signal?: AbortSignal,\n): Promise<void> => {\n const { max, min, fully } = options || {}\n\n const idleFn = fully ? fullyIdle : idleAsync\n\n if (max && min && min < max) {\n await Promise.race([Promise.all([idleFn(), sleep(min)]), sleep(max)])\n } else if (max) {\n await Promise.race([idleFn(), sleep(max)])\n } else if (min) {\n await Promise.all([idleFn(), sleep(min)])\n } else {\n await idleFn()\n }\n\n if (signal?.aborted) {\n throw new AbortError()\n }\n}\n\nconst fullyIdle = async (signal?: AbortSignal): Promise<void> => {\n while (true) {\n const startTime = Date.now()\n await idle({}, signal)\n const endTime = Date.now()\n const duration = endTime - startTime\n\n // If idle callback took less than 15ms, consider it truly idle\n if (duration < 15) {\n break\n }\n\n // Check for abort signal after each iteration\n if (signal?.aborted) {\n throw new AbortError()\n }\n }\n}\n"
|
|
10
10
|
]
|
|
11
11
|
}
|
|
@@ -6,6 +6,6 @@
|
|
|
6
6
|
],
|
|
7
7
|
"version": 3,
|
|
8
8
|
"sourcesContent": [
|
|
9
|
-
"import { sleep } from './sleep'\n\nexport const interval = async (\n callback: () => void,\n ms: number,\n signal?: AbortSignal
|
|
9
|
+
"import { sleep } from './sleep'\n\nexport const interval = async (\n callback: () => void,\n ms: number,\n signal?: AbortSignal,\n): Promise<never> => {\n while (true) {\n callback()\n await sleep(ms, signal)\n }\n}\n"
|
|
10
10
|
]
|
|
11
11
|
}
|
|
@@ -6,6 +6,6 @@
|
|
|
6
6
|
],
|
|
7
7
|
"version": 3,
|
|
8
8
|
"sourcesContent": [
|
|
9
|
-
"import { useState, useEffect } from 'react'\n\nexport function useAsync<T>(\n promiseFn: () => Promise<T>,\n args: any[]
|
|
9
|
+
"import { useState, useEffect } from 'react'\n\nexport function useAsync<T>(\n promiseFn: () => Promise<T>,\n args: any[],\n): [T, 'loading' | 'idle' | 'error', Error | null] {\n const [data, setData] = useState<T | null>(null)\n const [error, setError] = useState<Error | null>(null)\n const [loading, setLoading] = useState<boolean>(true)\n\n useEffect(() => {\n let isMounted = true\n\n const fetchData = async () => {\n try {\n const result = await promiseFn()\n if (isMounted) {\n setData(result)\n setLoading(false)\n }\n } catch (err) {\n if (isMounted) {\n setError(err as Error)\n setLoading(false)\n }\n }\n }\n\n fetchData()\n\n return () => {\n isMounted = false\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, args)\n\n return [data as T, error ? 'error' : loading ? 'loading' : 'idle', error]\n}\n"
|
|
10
10
|
]
|
|
11
11
|
}
|
|
@@ -6,6 +6,6 @@
|
|
|
6
6
|
],
|
|
7
7
|
"version": 3,
|
|
8
8
|
"sourcesContent": [
|
|
9
|
-
"// adopted from https://github.com/franciscop/use-async/blob/master/src/index.js\n\nimport { useEffect, useId, useLayoutEffect } from 'react'\n\nimport { EMPTY_OBJECT } from '../constants'\nimport { getCurrentComponentStack } from '../react/getCurrentComponentStack'\nimport { handleAbortError } from './abortable'\n\ntype Cleanup = () => void\n\ntype AsyncEffectCallback = (\n signal: AbortSignal,\n ...deps: any[]\n) => Promise<Cleanup | void> | void\n\ntype AsyncEffectOptions = {\n circuitBreakAfter?: number\n circuitBreakPeriod?: number\n debug?: boolean\n}\n\nexport function useAsyncEffect(\n cb: AsyncEffectCallback,\n deps: any[] = [],\n options?: AsyncEffectOptions
|
|
9
|
+
"// adopted from https://github.com/franciscop/use-async/blob/master/src/index.js\n\nimport { useEffect, useId, useLayoutEffect } from 'react'\n\nimport { EMPTY_OBJECT } from '../constants'\nimport { getCurrentComponentStack } from '../react/getCurrentComponentStack'\nimport { handleAbortError } from './abortable'\n\ntype Cleanup = () => void\n\ntype AsyncEffectCallback = (\n signal: AbortSignal,\n ...deps: any[]\n) => Promise<Cleanup | void> | void\n\ntype AsyncEffectOptions = {\n circuitBreakAfter?: number\n circuitBreakPeriod?: number\n debug?: boolean\n}\n\nexport function useAsyncEffect(\n cb: AsyncEffectCallback,\n deps: any[] = [],\n options?: AsyncEffectOptions,\n): void {\n useAsyncEffectImpl(false, cb, deps, options)\n}\n\nexport function useAsyncLayoutEffect(\n cb: AsyncEffectCallback,\n deps: any[] = [],\n options?: AsyncEffectOptions,\n): void {\n useAsyncEffectImpl(true, cb, deps, options)\n}\n\nfunction useAsyncEffectImpl(\n isLayoutEffect: boolean,\n cb: AsyncEffectCallback,\n deps: any[] = [],\n options: AsyncEffectOptions = EMPTY_OBJECT,\n): void {\n const effectHook = isLayoutEffect ? useLayoutEffect : useEffect\n // eslint-disable-next-line react-hooks/rules-of-hooks\n const effectId = process.env.NODE_ENV === 'development' ? useId() : ''\n\n effectHook(() => {\n // Generate a unique ID for this effect instance for loop detection\n checkEffectLoop(\n effectId,\n cb,\n deps,\n options.circuitBreakAfter,\n options.circuitBreakPeriod,\n )\n const controller = new AbortController()\n const signal = controller.signal\n\n // wrap in try in case its not async (for simple use cases)\n try {\n const value = cb(signal, ...deps)\n\n Promise.resolve(value)\n .then(async (res) => {\n if (res && typeof res === 'function') {\n if (signal.aborted) return res()\n signal.addEventListener('abort', res)\n }\n })\n .catch(handleAbortError)\n } catch (error) {\n handleAbortError(error, options.debug)\n }\n\n return () => {\n if (signal.aborted) return\n controller.abort()\n }\n }, deps)\n}\n\n// loop detection in dev mode\nlet effectRunCounts: Map<string, number[]>\nlet checkEffectLoop: (\n effectId: string,\n cb: AsyncEffectCallback,\n deps: any[],\n circuitBreakAfter?: number,\n circuitBreakPeriod?: number,\n) => void\n\nfunction formatDeps(deps: any[]): string {\n try {\n return JSON.stringify(\n deps,\n (_, v) => {\n if (typeof v === 'function') return `[Function: ${v.name || 'anonymous'}]`\n if (typeof v === 'symbol') return v.toString()\n if (v instanceof Error) return `[Error: ${v.message}]`\n return v\n },\n 2,\n )\n } catch {\n return `[${deps.length} deps - not serializable]`\n }\n}\n\nif (process.env.NODE_ENV === 'development') {\n effectRunCounts = new Map<string, number[]>()\n\n checkEffectLoop = (\n effectId: string,\n cb: AsyncEffectCallback,\n deps: any[],\n circuitBreakAfter: number = 20,\n circuitBreakPeriod: number = 1000,\n ) => {\n const now = Date.now()\n const runs = effectRunCounts.get(effectId) || []\n\n runs.push(now)\n\n // keep only runs from the specified period\n const recentRuns = runs.filter((time) => now - time < circuitBreakPeriod)\n effectRunCounts.set(effectId, recentRuns)\n\n const runCount = recentRuns.length\n\n if (runCount > circuitBreakAfter) {\n const message = `🚨 useAsyncEffect infinite loop detected! Effect ran ${runCount} times in <${circuitBreakPeriod}ms`\n if (process.env.NODE_ENV === 'development') {\n console.error(message)\n console.error('Effect function:', cb.toString().slice(0, 500))\n console.error('Dependencies:', formatDeps(deps))\n console.error('Stack:', getCurrentComponentStack())\n // eslint-disable-next-line no-debugger\n debugger\n } else {\n alert(message)\n throw new Error(message)\n }\n } else if (runCount > circuitBreakAfter / 2) {\n console.warn(\n `⚠️ useAsyncEffect potential loop: Effect ran ${runCount} times in <${circuitBreakPeriod}ms`,\n )\n console.warn('Effect function:', cb.toString().slice(0, 500))\n console.warn('Dependencies:', formatDeps(deps))\n console.warn('Stack:', getCurrentComponentStack())\n }\n }\n} else {\n checkEffectLoop = (_id, _cb, _deps, _after, _period) => {}\n}\n"
|
|
10
10
|
]
|
|
11
11
|
}
|
|
@@ -6,6 +6,6 @@
|
|
|
6
6
|
],
|
|
7
7
|
"version": 3,
|
|
8
8
|
"sourcesContent": [
|
|
9
|
-
"import { startTransition, useState } from 'react'\n\nimport { idle, type IdleOptions } from './idle'\nimport { useAsyncEffect } from './useAsyncEffect'\n\nimport type React from 'react'\n\nexport type LazyMountProps = IdleOptions\n\nexport const useLazyMount = (props: LazyMountProps = { max: 100 }): boolean => {\n const [mounted, setMounted] = useState(false)\n\n useAsyncEffect(\n async (signal) => {\n await idle(props, signal)\n startTransition(() => {\n setMounted(true)\n })\n },\n [\n // no need for deps it only ever mounts once\n ]
|
|
9
|
+
"import { startTransition, useState } from 'react'\n\nimport { idle, type IdleOptions } from './idle'\nimport { useAsyncEffect } from './useAsyncEffect'\n\nimport type React from 'react'\n\nexport type LazyMountProps = IdleOptions\n\nexport const useLazyMount = (props: LazyMountProps = { max: 100 }): boolean => {\n const [mounted, setMounted] = useState(false)\n\n useAsyncEffect(\n async (signal) => {\n await idle(props, signal)\n startTransition(() => {\n setMounted(true)\n })\n },\n [\n // no need for deps it only ever mounts once\n ],\n )\n\n return mounted\n}\n\nexport const LazyMount = ({\n children,\n ...idleProps\n}: LazyMountProps & { children: any }): React.ReactNode => {\n const mounted = useLazyMount(idleProps)\n return mounted ? children : null\n}\n"
|
|
10
10
|
]
|
|
11
11
|
}
|
|
@@ -6,6 +6,6 @@
|
|
|
6
6
|
],
|
|
7
7
|
"version": 3,
|
|
8
8
|
"sourcesContent": [
|
|
9
|
-
"import { startTransition, useState } from 'react'\n\nimport { idle, type IdleOptions } from './idle'\nimport { useAsyncEffect } from './useAsyncEffect'\n\nexport const useLazyValue = <T>(\n value: T,\n {\n immediateFirstUpdate,\n ...idleOptions\n }: IdleOptions & { immediateFirstUpdate?: boolean } = {}
|
|
9
|
+
"import { startTransition, useState } from 'react'\n\nimport { idle, type IdleOptions } from './idle'\nimport { useAsyncEffect } from './useAsyncEffect'\n\nexport const useLazyValue = <T>(\n value: T,\n {\n immediateFirstUpdate,\n ...idleOptions\n }: IdleOptions & { immediateFirstUpdate?: boolean } = {},\n): T => {\n const [lazyValue, setLazyValue] = useState(value)\n\n // first update to a real value immediate\n if (value && lazyValue === undefined && lazyValue !== value && immediateFirstUpdate) {\n setLazyValue(value)\n }\n\n useAsyncEffect(\n async (signal) => {\n await idle(idleOptions, signal)\n startTransition(() => {\n setLazyValue(value)\n })\n },\n [value],\n )\n\n return lazyValue\n}\n"
|
|
10
10
|
]
|
|
11
11
|
}
|
|
@@ -6,6 +6,6 @@
|
|
|
6
6
|
],
|
|
7
7
|
"version": 3,
|
|
8
8
|
"sourcesContent": [
|
|
9
|
-
"export async function clearIndexedDB(): Promise<void> {\n if ('indexedDB' in window) {\n const databases = await indexedDB.databases()\n await Promise.all(\n databases.map((db) => {\n if (db.name) {\n return new Promise<void>((resolve, reject) => {\n const deleteReq = indexedDB.deleteDatabase(db.name!)\n deleteReq.onsuccess = () => resolve()\n deleteReq.onerror = () => reject(deleteReq.error)\n deleteReq.onblocked = () => reject(new Error('database deletion blocked'))\n })\n }\n return Promise.resolve()\n })
|
|
9
|
+
"export async function clearIndexedDB(): Promise<void> {\n if ('indexedDB' in window) {\n const databases = await indexedDB.databases()\n await Promise.all(\n databases.map((db) => {\n if (db.name) {\n return new Promise<void>((resolve, reject) => {\n const deleteReq = indexedDB.deleteDatabase(db.name!)\n deleteReq.onsuccess = () => resolve()\n deleteReq.onerror = () => reject(deleteReq.error)\n deleteReq.onblocked = () => reject(new Error('database deletion blocked'))\n })\n }\n return Promise.resolve()\n }),\n )\n }\n}\n"
|
|
10
10
|
]
|
|
11
11
|
}
|
|
@@ -6,6 +6,6 @@
|
|
|
6
6
|
],
|
|
7
7
|
"version": 3,
|
|
8
8
|
"sourcesContent": [
|
|
9
|
-
"// note safari enhanced privacy will break this as it reports screen height as smaller\n\nexport function openCenteredPopup(\n url: string,\n title: string,\n width: number,\n height: number
|
|
9
|
+
"// note safari enhanced privacy will break this as it reports screen height as smaller\n\nexport function openCenteredPopup(\n url: string,\n title: string,\n width: number,\n height: number,\n): Window | null {\n // availWidth/availHeight accounts for taskbars, docks, and other UI elements\n const screenWidth = screen.availWidth || screen.width\n const screenHeight = screen.availHeight || screen.height\n\n const shouldGuess = screenHeight < height\n\n // if we are guessing, at least make it a bit more likely to be centered\n // and not just touching the top which looks broken\n const left = Math.max(0, (screenWidth - width) / 2) + (shouldGuess ? 100 : 0)\n const top = Math.max(150, shouldGuess ? 150 : Math.max(0, (screenHeight - height) / 2))\n\n const windowFeatures = `\n width=${width},\n height=${height},\n left=${left},\n top=${top},\n scrollbars=yes,\n resizable=yes\n `\n\n return window.open(url, title, windowFeatures)\n}\n"
|
|
10
10
|
]
|
|
11
11
|
}
|
package/types/constants.d.ts.map
CHANGED
|
@@ -6,6 +6,6 @@
|
|
|
6
6
|
],
|
|
7
7
|
"version": 3,
|
|
8
8
|
"sourcesContent": [
|
|
9
|
-
"import { isWeb } from '@tamagui/constants'\n\nexport const isTauri: boolean = typeof window !== 'undefined' && '__TAURI__' in window\n\nexport const isNative: boolean = !isWeb && !isTauri\n\n// TODO move to probably ~/interface/constants\n\nexport const IS_MAC_DESKTOP: boolean =\n typeof navigator !== 'undefined' && /Macintosh|MacIntel/.test(navigator.platform)\n\nexport const IS_SAFARI: boolean =\n isTauri ||\n (typeof navigator !== 'undefined' &&\n /Version\\/[\\d.]+.*Safari/.test(navigator.userAgent) &&\n
|
|
9
|
+
"import { isWeb } from '@tamagui/constants'\n\nexport const isTauri: boolean = typeof window !== 'undefined' && '__TAURI__' in window\n\nexport const isNative: boolean = !isWeb && !isTauri\n\n// TODO move to probably ~/interface/constants\n\nexport const IS_MAC_DESKTOP: boolean =\n typeof navigator !== 'undefined' && /Macintosh|MacIntel/.test(navigator.platform)\n\nexport const IS_SAFARI: boolean =\n isTauri ||\n (typeof navigator !== 'undefined' &&\n /Version\\/[\\d.]+.*Safari/.test(navigator.userAgent) &&\n navigator.vendor.includes('Apple Computer'))\n\nexport { isAndroid, isBrowser, isIos, isServer, isWeb } from '@tamagui/constants'\n\nexport const EMPTY_ARRAY = [] as never\nexport const EMPTY_OBJECT = {} as never\n\nconst getDebugLevelFromUrl = (): number | null => {\n if (typeof window === 'undefined') return null\n const match = window.location?.search?.match(/debug=(\\d+)/)\n return match?.[1] ? parseInt(match[1], 10) : null\n}\n\nexport const DEBUG_LEVEL: number = process.env.DEBUG_LEVEL\n ? +process.env.DEBUG_LEVEL\n : (getDebugLevelFromUrl() ?? (process.env.NODE_ENV === 'development' ? 1 : 0))\n"
|
|
10
10
|
]
|
|
11
11
|
}
|
|
@@ -6,6 +6,6 @@
|
|
|
6
6
|
],
|
|
7
7
|
"version": 3,
|
|
8
8
|
"sourcesContent": [
|
|
9
|
-
"import { useCallback } from 'react'\n\nexport function useDebugState<T>(\n useState: [T, React.Dispatch<React.SetStateAction<T>>]
|
|
9
|
+
"import { useCallback } from 'react'\n\nexport function useDebugState<T>(\n useState: [T, React.Dispatch<React.SetStateAction<T>>],\n): [T, React.Dispatch<React.SetStateAction<T>>] {\n const [state, setState] = useState\n\n const wrappedSetState = useCallback(\n (newValue: T | ((prevState: T) => T)) => {\n if (typeof newValue === 'function') {\n // Handle callback style\n setState((prevState: T) => {\n const nextState = (newValue as (prevState: T) => T)(prevState)\n console.trace('State update (callback):', {\n prevState,\n nextState,\n stack: new Error().stack,\n })\n return nextState\n })\n } else {\n // Handle direct value style\n console.trace('State update (direct):', {\n prevState: state,\n nextState: newValue,\n stack: new Error().stack,\n })\n setState(newValue)\n }\n },\n [setState, state],\n )\n\n return [state, wrappedSetState]\n}\n"
|
|
10
10
|
]
|
|
11
11
|
}
|
package/types/emitter.d.ts.map
CHANGED
|
@@ -6,6 +6,6 @@
|
|
|
6
6
|
],
|
|
7
7
|
"version": 3,
|
|
8
8
|
"sourcesContent": [
|
|
9
|
-
"import { dequal } from 'dequal'\nimport * as React from 'react'\nimport { use, useLayoutEffect, useState } from 'react'\n\nimport { handleAbortError } from './async/abortable'\nimport { DEBUG_LEVEL, EMPTY_ARRAY } from './constants'\nimport { AbortError } from './error/errors'\nimport { globalValue } from './global/globalValue'\nimport { createGlobalContext } from './react/createGlobalContext'\n\nimport type { JSX, PropsWithChildren } from 'react'\n\n// keeps a reference to the current value easily\n\n// TODO can replace with useEffectEvent\nfunction useGet<A>(\n currentValue: A,\n initialValue?: any,\n forwardToFunction?: boolean\n): () => A {\n const curRef = React.useRef<any>(initialValue ?? currentValue)\n\n useLayoutEffect(() => {\n curRef.current = currentValue\n })\n\n // oxlint-disable-next-line exhaustive-deps\n return React.useCallback(\n forwardToFunction\n ? (...args) => curRef.current?.apply(null, args)\n : () => curRef.current,\n [curRef, forwardToFunction]\n )\n}\n\ntype EmitterOptions<T> = CreateEmitterOpts<T> & {\n name: string\n}\n\ntype CreateEmitterOpts<T> = {\n silent?: boolean\n comparator?: (a: T, b: T) => boolean\n}\n\nexport class Emitter<const T> {\n private disposables = new Set<(cb: any) => void>()\n value: T\n options?: EmitterOptions<T>\n\n constructor(value: T, options?: EmitterOptions<T>) {\n this.value = value\n this.options = options\n }\n\n listen = (disposable: (cb: T) => void): (() => void) => {\n this.disposables.add(disposable)\n return (): void => {\n this.disposables.delete(disposable)\n }\n }\n\n emit = (next: T): void => {\n if (process.env.NODE_ENV === 'development') {\n setCache(this, next)\n }\n const compare = this.options?.comparator\n if (compare) {\n if (this.value && compare(this.value, next)) {\n return\n }\n } else {\n if (this.value === next) {\n if (process.env.NODE_ENV === 'development') {\n console.warn(\n `[emitter] ${this.options?.name} no comparator option but received same value!\n \nthis will emit the same value again, which can be desirable, but we warn to ensure it's not unintended:\n\n- if you want this behavior, add { comparator: isEqualNever }\n- if you want only non-equal values: { comparator: isEqualIdentity }\n- if you want only deeply non-equal values: { comparator: isEqualDeep }`\n )\n }\n }\n }\n this.value = next\n if (DEBUG_LEVEL > 1) {\n if (!this.options?.silent) {\n const name = this.options?.name\n console.groupCollapsed(`📣 ${name}`)\n console.info(next)\n console.trace(`trace >`)\n console.groupEnd()\n }\n }\n this.disposables.forEach((cb) => cb(next))\n }\n\n nextValue = (): Promise<T> => {\n return new Promise<T>((res) => {\n const dispose = this.listen((val) => {\n dispose()\n res(val)\n })\n })\n }\n}\n\n// just createEmitter but ensures it doesn't mess up on HMR\nexport function createGlobalEmitter<T>(\n name: string,\n defaultValue: T,\n options?: CreateEmitterOpts<T>\n): Emitter<T> {\n return globalValue(name, () => createEmitter(name, defaultValue, options))\n}\n\nexport function createEmitter<T>(\n name: string,\n defaultValue: T,\n options?: CreateEmitterOpts<T>\n): Emitter<T> {\n const existing = createOrUpdateCache(name, defaultValue) as T\n return new Emitter<T>(existing || defaultValue, { name, ...options })\n}\n\nexport type EmitterType<E extends Emitter<any>> =\n E extends Emitter<infer Val> ? Val : never\n\nexport const useEmitter = <E extends Emitter<any>>(\n emitter: E,\n cb: (cb: EmitterType<E>) => void,\n args?: any[]\n): void => {\n const getCallback = useGet(cb)\n\n useLayoutEffect(() => {\n return emitter.listen((val) => {\n try {\n getCallback()(val)\n } catch (err) {\n handleAbortError(err)\n }\n })\n }, [emitter, getCallback])\n}\n\n// i think this was useSyncExternalStore but removed for concurrent rendering improvements\n// wondering if we could just always return a deferred value? or default to it?\n\nexport const useEmitterValue = <E extends Emitter<any>>(\n emitter: E,\n options?: { disable?: boolean }\n): EmitterType<E> => {\n const disabled = options?.disable\n\n // use a function initializer to get current emitter value\n const [state, setState] = useState<EmitterType<E>>(() => emitter.value)\n\n useLayoutEffect(() => {\n if (disabled) return\n\n // sync immediately in case emitter changed between render and effect\n if (emitter.value !== state) {\n setState(emitter.value)\n }\n\n return emitter.listen(setState)\n }, [state, disabled, emitter])\n\n return state\n}\n\n/**\n * By default selectors run every render, as well as when emitters update. This is a change\n * from the previous behavior where they only ran when emitters changed value.\n *\n * The reason for this is because emitters capture the variables in scope of the component\n * each render already by using \"useGet\" by default, which makes them easier to use - you\n * don't need to pass an args[] array except for edge cases.\n *\n * Before explaining why we switched to the default, understand the different uses:\n *\n * - Default behavior - selector is updated every render, and ran every render, as well as\n * when emitter value changes, so you basically are always up to date.\n *\n * - Set an args[] array as the fourth argument - this will stop the automatic capturing\n * and instead update selector only when you change args[] yourself. This is good for when you\n * want explicit control over re-selection and rendering.\n *\n * - With { lazy: true }, the selector only runs when the emitter value changes. If used with\n * args[], you capture the context of the selector based on args[], if not, it's based on the\n * current render.\n *\n * I made this change when we had 16 usages of useEmitterSelector and 100% of them are doing very\n * cheap calculations, so this feels like the right pattern. For the rare case of a heavy selector,\n * you have the option to control it.\n *\n */\nexport const useEmitterSelector = <E extends Emitter<any>, T extends EmitterType<E>, R>(\n emitter: E,\n selector: (value: T) => R,\n options?: {\n disable?: boolean\n lazy?: boolean\n },\n args: any[] = EMPTY_ARRAY\n): R => {\n const [state, setState] = useState<R>(() => selector(emitter.value))\n const disabled = options?.disable\n const getSelector = useGet(selector)\n\n if (options?.lazy !== true) {\n const next = selector(emitter.value)\n if (next !== state) {\n setState(next)\n }\n }\n\n useLayoutEffect(() => {\n if (disabled) return\n return emitter.listen((val) => {\n try {\n const selectorFn = args !== EMPTY_ARRAY ? selector : getSelector()\n const next = selectorFn(val)\n setState(next)\n } catch (error) {\n if (error instanceof AbortError) {\n return\n }\n throw error\n }\n })\n // oxlint-disable-next-line exhaustive-deps\n }, [disabled, emitter, getSelector, ...args])\n\n return state\n}\n\nexport const useEmittersSelector = <const E extends readonly Emitter<any>[], R>(\n emitters: E,\n selector: (values: { [K in keyof E]: EmitterType<E[K]> }) => R,\n options?: { disable?: boolean; isEqual?: (a: R, b: R) => boolean }\n): R => {\n const getSelector = useGet(selector)\n const disabled = options?.disable\n\n const [state, setState] = useState<R>(() => {\n const values = emitters.map((e) => e.value) as { [K in keyof E]: EmitterType<E[K]> }\n return getSelector()(values)\n })\n\n useLayoutEffect(() => {\n if (disabled) {\n return\n }\n\n const handler = () => {\n const values = emitters.map((e) => e.value) as {\n [K in keyof E]: EmitterType<E[K]>\n }\n try {\n const next = getSelector()(values)\n setState((prev) => {\n if (options?.isEqual?.(prev, next)) {\n return prev\n }\n if (dequal(prev, next)) {\n return prev\n }\n return next\n })\n } catch (error) {\n if (error instanceof AbortError) {\n return\n }\n throw error\n }\n }\n\n const disposals = emitters.map((emitter) => emitter.listen(handler))\n\n return () => {\n disposals.forEach((d) => d())\n }\n // oxlint-disable-next-line exhaustive-deps\n }, [disabled, getSelector, ...emitters])\n\n return state\n}\n\nexport const createUseEmitter = <E extends Emitter<any>>(\n emitter: E\n): ((cb: (val: EmitterType<E>) => void, args?: any[]) => void) => {\n return (cb: (val: EmitterType<E>) => void, args?: any[]) =>\n useEmitter(emitter, cb, args)\n}\n\nexport const createUseSelector = <E extends Emitter<any>>(\n emitter: E\n): (<R>(\n selector: (value: EmitterType<E>) => R,\n options?: { disable?: boolean; lazy?: boolean },\n args?: any[]\n) => R) => {\n return <R,>(\n selector: (value: EmitterType<E>) => R,\n options?: { disable?: boolean; lazy?: boolean },\n args?: any[]\n ): R => {\n return useEmitterSelector(emitter, selector, options, args)\n }\n}\n\nexport function createContextualEmitter<T>(\n name: string,\n defaultValue: T,\n defaultOptions?: Omit<EmitterOptions<T>, 'name'>\n): readonly [\n () => Emitter<T>,\n (props: PropsWithChildren<{ value?: T; silent?: boolean }>) => JSX.Element,\n] {\n const id = Math.random().toString(36)\n const EmitterContext = createGlobalContext<Emitter<T> | null>(\n `contextual-emitter/${id}`,\n null\n )\n\n const useContextEmitter = () => {\n const emitter = use(EmitterContext)\n if (!emitter) {\n throw new Error('useContextEmitter must be used within an EmitterProvider')\n }\n return emitter\n }\n\n type ProvideEmitterProps = PropsWithChildren<{\n value?: T\n silent?: boolean\n }>\n\n const ProvideEmitter = (props: ProvideEmitterProps) => {\n const { children, value, silent } = props\n const [emitter] = useState(\n () => new Emitter<T>(value ?? defaultValue, { name, silent, ...defaultOptions })\n )\n\n useLayoutEffect(() => {\n if (value !== undefined && value !== emitter.value) {\n emitter.emit(value)\n }\n }, [value, emitter])\n\n return <EmitterContext.Provider value={emitter}>{children}</EmitterContext.Provider>\n }\n\n return [useContextEmitter, ProvideEmitter] as const\n}\n\nconst HMRCache =\n process.env.NODE_ENV === 'development'\n ? new Map<string, { originalDefaultValue: unknown; currentValue: unknown }>()\n : null\n\nfunction setCache(emitter: Emitter<any>, value: unknown) {\n const name = emitter.options?.name\n if (!name) return\n const cache = HMRCache?.get(name)\n if (!cache) return\n cache.currentValue = value\n}\n\nfunction createOrUpdateCache(name: string, defaultValueProp: unknown) {\n const existing = HMRCache?.get(name)\n const defaultValue = dequal(existing?.originalDefaultValue, defaultValueProp)\n ? existing?.currentValue\n : defaultValueProp\n\n if (!existing) {\n HMRCache?.set(name, {\n originalDefaultValue: defaultValueProp,\n currentValue: defaultValue,\n })\n }\n\n return defaultValue\n}\n"
|
|
9
|
+
"import { dequal } from 'dequal'\nimport * as React from 'react'\nimport { use, useLayoutEffect, useState } from 'react'\n\nimport { handleAbortError } from './async/abortable'\nimport { DEBUG_LEVEL, EMPTY_ARRAY } from './constants'\nimport { AbortError } from './error/errors'\nimport { globalValue } from './global/globalValue'\nimport { createGlobalContext } from './react/createGlobalContext'\n\nimport type { JSX, PropsWithChildren } from 'react'\n\n// keeps a reference to the current value easily\n\n// TODO can replace with useEffectEvent\nfunction useGet<A>(\n currentValue: A,\n initialValue?: any,\n forwardToFunction?: boolean,\n): () => A {\n const curRef = React.useRef<any>(initialValue ?? currentValue)\n\n useLayoutEffect(() => {\n curRef.current = currentValue\n })\n\n // oxlint-disable-next-line exhaustive-deps\n return React.useCallback(\n forwardToFunction\n ? (...args) => curRef.current?.apply(null, args)\n : () => curRef.current,\n [curRef, forwardToFunction],\n )\n}\n\ntype EmitterOptions<T> = CreateEmitterOpts<T> & {\n name: string\n}\n\ntype CreateEmitterOpts<T> = {\n silent?: boolean\n comparator?: (a: T, b: T) => boolean\n}\n\nexport class Emitter<const T> {\n private disposables = new Set<(cb: any) => void>()\n value: T\n options?: EmitterOptions<T>\n\n constructor(value: T, options?: EmitterOptions<T>) {\n this.value = value\n this.options = options\n }\n\n listen = (disposable: (cb: T) => void): (() => void) => {\n this.disposables.add(disposable)\n return (): void => {\n this.disposables.delete(disposable)\n }\n }\n\n emit = (next: T): void => {\n if (process.env.NODE_ENV === 'development') {\n setCache(this, next)\n }\n const compare = this.options?.comparator\n if (compare) {\n if (this.value && compare(this.value, next)) {\n return\n }\n } else {\n if (this.value === next) {\n if (process.env.NODE_ENV === 'development') {\n console.warn(\n `[emitter] ${this.options?.name} no comparator option but received same value!\n \nthis will emit the same value again, which can be desirable, but we warn to ensure it's not unintended:\n\n- if you want this behavior, add { comparator: isEqualNever }\n- if you want only non-equal values: { comparator: isEqualIdentity }\n- if you want only deeply non-equal values: { comparator: isEqualDeep }`,\n )\n }\n }\n }\n this.value = next\n if (DEBUG_LEVEL > 1) {\n if (!this.options?.silent) {\n const name = this.options?.name\n console.groupCollapsed(`📣 ${name}`)\n console.info(next)\n console.trace(`trace >`)\n console.groupEnd()\n }\n }\n this.disposables.forEach((cb) => cb(next))\n }\n\n nextValue = (): Promise<T> => {\n return new Promise<T>((res) => {\n const dispose = this.listen((val) => {\n dispose()\n res(val)\n })\n })\n }\n}\n\n// just createEmitter but ensures it doesn't mess up on HMR\nexport function createGlobalEmitter<T>(\n name: string,\n defaultValue: T,\n options?: CreateEmitterOpts<T>,\n): Emitter<T> {\n return globalValue(name, () => createEmitter(name, defaultValue, options))\n}\n\nexport function createEmitter<T>(\n name: string,\n defaultValue: T,\n options?: CreateEmitterOpts<T>,\n): Emitter<T> {\n const existing = createOrUpdateCache(name, defaultValue) as T\n return new Emitter<T>(existing || defaultValue, { name, ...options })\n}\n\nexport type EmitterType<E extends Emitter<any>> =\n E extends Emitter<infer Val> ? Val : never\n\nexport const useEmitter = <E extends Emitter<any>>(\n emitter: E,\n cb: (cb: EmitterType<E>) => void,\n args?: any[],\n): void => {\n const getCallback = useGet(cb)\n\n useLayoutEffect(() => {\n return emitter.listen((val) => {\n try {\n getCallback()(val)\n } catch (err) {\n handleAbortError(err)\n }\n })\n }, [emitter, getCallback])\n}\n\n// i think this was useSyncExternalStore but removed for concurrent rendering improvements\n// wondering if we could just always return a deferred value? or default to it?\n\nexport const useEmitterValue = <E extends Emitter<any>>(\n emitter: E,\n options?: { disable?: boolean },\n): EmitterType<E> => {\n const disabled = options?.disable\n\n // use a function initializer to get current emitter value\n const [state, setState] = useState<EmitterType<E>>(() => emitter.value)\n\n useLayoutEffect(() => {\n if (disabled) return\n\n // sync immediately in case emitter changed between render and effect\n if (emitter.value !== state) {\n setState(emitter.value)\n }\n\n return emitter.listen(setState)\n }, [state, disabled, emitter])\n\n return state\n}\n\n/**\n * By default selectors run every render, as well as when emitters update. This is a change\n * from the previous behavior where they only ran when emitters changed value.\n *\n * The reason for this is because emitters capture the variables in scope of the component\n * each render already by using \"useGet\" by default, which makes them easier to use - you\n * don't need to pass an args[] array except for edge cases.\n *\n * Before explaining why we switched to the default, understand the different uses:\n *\n * - Default behavior - selector is updated every render, and ran every render, as well as\n * when emitter value changes, so you basically are always up to date.\n *\n * - Set an args[] array as the fourth argument - this will stop the automatic capturing\n * and instead update selector only when you change args[] yourself. This is good for when you\n * want explicit control over re-selection and rendering.\n *\n * - With { lazy: true }, the selector only runs when the emitter value changes. If used with\n * args[], you capture the context of the selector based on args[], if not, it's based on the\n * current render.\n *\n * I made this change when we had 16 usages of useEmitterSelector and 100% of them are doing very\n * cheap calculations, so this feels like the right pattern. For the rare case of a heavy selector,\n * you have the option to control it.\n *\n */\nexport const useEmitterSelector = <E extends Emitter<any>, T extends EmitterType<E>, R>(\n emitter: E,\n selector: (value: T) => R,\n options?: {\n disable?: boolean\n lazy?: boolean\n },\n args: any[] = EMPTY_ARRAY,\n): R => {\n const [state, setState] = useState<R>(() => selector(emitter.value))\n const disabled = options?.disable\n const getSelector = useGet(selector)\n\n if (options?.lazy !== true) {\n const next = selector(emitter.value)\n if (next !== state) {\n setState(next)\n }\n }\n\n useLayoutEffect(() => {\n if (disabled) return\n return emitter.listen((val) => {\n try {\n const selectorFn = args !== EMPTY_ARRAY ? selector : getSelector()\n const next = selectorFn(val)\n setState(next)\n } catch (error) {\n if (error instanceof AbortError) {\n return\n }\n throw error\n }\n })\n // oxlint-disable-next-line exhaustive-deps\n }, [disabled, emitter, getSelector, ...args])\n\n return state\n}\n\nexport const useEmittersSelector = <const E extends readonly Emitter<any>[], R>(\n emitters: E,\n selector: (values: { [K in keyof E]: EmitterType<E[K]> }) => R,\n options?: { disable?: boolean; isEqual?: (a: R, b: R) => boolean },\n): R => {\n const getSelector = useGet(selector)\n const disabled = options?.disable\n\n const [state, setState] = useState<R>(() => {\n const values = emitters.map((e) => e.value) as { [K in keyof E]: EmitterType<E[K]> }\n return getSelector()(values)\n })\n\n useLayoutEffect(() => {\n if (disabled) {\n return\n }\n\n const handler = () => {\n const values = emitters.map((e) => e.value) as {\n [K in keyof E]: EmitterType<E[K]>\n }\n try {\n const next = getSelector()(values)\n setState((prev) => {\n if (options?.isEqual?.(prev, next)) {\n return prev\n }\n if (dequal(prev, next)) {\n return prev\n }\n return next\n })\n } catch (error) {\n if (error instanceof AbortError) {\n return\n }\n throw error\n }\n }\n\n const disposals = emitters.map((emitter) => emitter.listen(handler))\n\n return () => {\n disposals.forEach((d) => d())\n }\n // oxlint-disable-next-line exhaustive-deps\n }, [disabled, getSelector, ...emitters])\n\n return state\n}\n\nexport const createUseEmitter = <E extends Emitter<any>>(\n emitter: E,\n): ((cb: (val: EmitterType<E>) => void, args?: any[]) => void) => {\n return (cb: (val: EmitterType<E>) => void, args?: any[]) =>\n useEmitter(emitter, cb, args)\n}\n\nexport const createUseSelector = <E extends Emitter<any>>(\n emitter: E,\n): (<R>(\n selector: (value: EmitterType<E>) => R,\n options?: { disable?: boolean; lazy?: boolean },\n args?: any[],\n) => R) => {\n return <R,>(\n selector: (value: EmitterType<E>) => R,\n options?: { disable?: boolean; lazy?: boolean },\n args?: any[],\n ): R => {\n return useEmitterSelector(emitter, selector, options, args)\n }\n}\n\nexport function createContextualEmitter<T>(\n name: string,\n defaultValue: T,\n defaultOptions?: Omit<EmitterOptions<T>, 'name'>,\n): readonly [\n () => Emitter<T>,\n (props: PropsWithChildren<{ value?: T; silent?: boolean }>) => JSX.Element,\n] {\n const id = Math.random().toString(36)\n const EmitterContext = createGlobalContext<Emitter<T> | null>(\n `contextual-emitter/${id}`,\n null,\n )\n\n const useContextEmitter = () => {\n const emitter = use(EmitterContext)\n if (!emitter) {\n throw new Error('useContextEmitter must be used within an EmitterProvider')\n }\n return emitter\n }\n\n type ProvideEmitterProps = PropsWithChildren<{\n value?: T\n silent?: boolean\n }>\n\n const ProvideEmitter = (props: ProvideEmitterProps) => {\n const { children, value, silent } = props\n const [emitter] = useState(\n () => new Emitter<T>(value ?? defaultValue, { name, silent, ...defaultOptions }),\n )\n\n useLayoutEffect(() => {\n if (value !== undefined && value !== emitter.value) {\n emitter.emit(value)\n }\n }, [value, emitter])\n\n return <EmitterContext.Provider value={emitter}>{children}</EmitterContext.Provider>\n }\n\n return [useContextEmitter, ProvideEmitter] as const\n}\n\nconst HMRCache =\n process.env.NODE_ENV === 'development'\n ? new Map<string, { originalDefaultValue: unknown; currentValue: unknown }>()\n : null\n\nfunction setCache(emitter: Emitter<any>, value: unknown) {\n const name = emitter.options?.name\n if (!name) return\n const cache = HMRCache?.get(name)\n if (!cache) return\n cache.currentValue = value\n}\n\nfunction createOrUpdateCache(name: string, defaultValueProp: unknown) {\n const existing = HMRCache?.get(name)\n const defaultValue = dequal(existing?.originalDefaultValue, defaultValueProp)\n ? existing?.currentValue\n : defaultValueProp\n\n if (!existing) {\n HMRCache?.set(name, {\n originalDefaultValue: defaultValueProp,\n currentValue: defaultValue,\n })\n }\n\n return defaultValue\n}\n"
|
|
10
10
|
]
|
|
11
11
|
}
|