@wwog/react 1.2.9 → 1.2.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -318,7 +318,7 @@ Categorically write styles and basic string styles, with built-in functionality
318
318
 
319
319
  ```tsx
320
320
  import { Styles } from "@wwog/react";
321
- import clazz from './index.module.css'
321
+ import clazz from "./index.module.css";
322
322
 
323
323
  function Example() {
324
324
  return (
@@ -375,22 +375,22 @@ A lightweight external state management utility that allows you to create and ma
375
375
  import { createExternalState } from "@wwog/react";
376
376
 
377
377
  // Create a global theme state
378
- const themeState = createExternalState('light', (newTheme, oldTheme) => {
378
+ const themeState = createExternalState("light", (newTheme, oldTheme) => {
379
379
  console.log(`Theme changed from ${oldTheme} to ${newTheme}`);
380
380
  });
381
381
 
382
382
  // Get or modify state from anywhere
383
383
  console.log(themeState.get()); // 'light'
384
- themeState.set('dark');
384
+ themeState.set("dark");
385
385
 
386
386
  // Use the state in components
387
387
  function ThemeConsumer() {
388
388
  const [theme, setTheme] = themeState.use();
389
-
389
+
390
390
  return (
391
391
  <div className={theme}>
392
392
  Current theme: {theme}
393
- <button onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}>
393
+ <button onClick={() => setTheme(theme === "light" ? "dark" : "light")}>
394
394
  Toggle theme
395
395
  </button>
396
396
  </div>
@@ -407,6 +407,7 @@ function ThemeConsumer() {
407
407
  - `use()`: React Hook, returns `[state, setState]` for using this state in components
408
408
 
409
409
  Use cases:
410
+
410
411
  - Global state management (themes, user settings, etc.)
411
412
  - Cross-component communication
412
413
  - Reactive state in services or utility classes
@@ -424,6 +425,10 @@ Interruptible child node traversal, enabling some branch processes to have ultim
424
425
 
425
426
  Incrementally class
426
427
 
428
+ #### `safePromiseTry` (v1.2.10+)
429
+
430
+ Support `Promise.try` Use `Promise.try`, otherwise use internal implementation
431
+
427
432
  #### `cx` (v1.2.5+)
428
433
 
429
434
  An efficient CSS class name merging utility function, similar to `clsx` or `classnames`, but automatically removes duplicate class names.
package/dist/index.d.mts CHANGED
@@ -560,5 +560,7 @@ declare class Counter {
560
560
  next(): number;
561
561
  }
562
562
 
563
- export { ArrayRender, Counter, DateRender, False, If, Pipe, Scope, SizeBox, Styles, Switch, Toggle, True, When, childrenLoop, createExternalState, cx, formatDate, useControlled };
563
+ declare const safePromiseTry: <T, U extends unknown[]>(callbackFn: (...args: U) => T | PromiseLike<T>, ...args: U) => Promise<Awaited<T>>;
564
+
565
+ export { ArrayRender, Counter, DateRender, False, If, Pipe, Scope, SizeBox, Styles, Switch, Toggle, True, When, childrenLoop, createExternalState, cx, formatDate, safePromiseTry, useControlled };
564
566
  export type { ArrayRenderProps, CreateStateListener, CxInput, DateRenderProps, ElseIfProps, ElseProps, ExternalSideEffect, ExternalState, FalseProps, IfProps, PipeProps, ScopeProps, StylesDescriptor, StylesProps, StylesType, SwitchCaseProps, SwitchDefaultProps, SwitchProps, ThenProps, ToggleProps, TrueProps, UseControlledOptions, WhenProps };
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- import r,{useMemo as h,Children as J,Fragment as S,isValidElement as P,cloneElement as R,useEffect as W,useState as I,useCallback as H}from"react";function T(e,n){if(e===void 0)return;let t=0;if(Array.isArray(e)){for(const l of e)if(n(l,t++)===!1)break}else n(e,t)}const _=(e,n)=>e===n,E=e=>r.createElement(r.Fragment,null,e.children);E.displayName="Switch_Case";const w=e=>r.createElement(r.Fragment,null,e.children);w.displayName="Switch_Default";const y=e=>{const{value:n,compare:t=_,children:l,strict:o=!1}=e,a=new Set;let s=null,c=null,i=!1;return T(l,(d,u)=>{if(!r.isValidElement(d))throw new Error(`Switch Children only accepts valid React elements at index ${u}`);const m=d.type;if(m.displayName===E.displayName){const f=d.props;if(a.has(f.value))throw new Error(`Switch found duplicate Case value at index ${u}: ${JSON.stringify(f.value)}${o?" (detected in strict mode)":""}`);if(a.add(f.value),!s&&t(n,f.value)&&(s=f.children,o===!1))return!1}else if(m.displayName===w.displayName){if(i)throw new Error(`Switch can only have one Default child at index ${u}`);if(i=!0,c=d.props.children,!o&&s)return!1}else throw new Error(`Switch Children only accepts 'Case' or 'Default' elements, found: ${String(m.displayName||m.name||m)} at index ${u}`)}),r.createElement(r.Fragment,null,s??c)};y.displayName="Switch",y.Case=E,y.Default=w,y.createTyped=function(){return{Switch:y,Case:E,Default:w}};const N=e=>r.createElement(r.Fragment,null,e.children),v=({children:e})=>r.createElement(r.Fragment,null,e),F=e=>r.createElement(r.Fragment,null,e.children);N.displayName="If_Then",v.displayName="If_Else",F.displayName="If_ElseIf";const p=({condition:e,children:n})=>{let t=null,l=null;const o=[];if(r.Children.forEach(n,a=>{if(!r.isValidElement(a))throw new Error("If component only accepts valid React elements");const s=a.type;if(s.displayName===N.displayName){if(t)throw new Error("If component can only have one Then child");t=a}else if(s.displayName===F.displayName)o.push(a);else if(s.displayName===v.displayName){if(l)throw new Error("If component can only have one Else child");l=a}else throw new Error(`If component only accepts 'Then', 'ElseIf', or 'Else' elements as children, found: ${String(s.displayName||s.name||s)}`)}),e)return t?r.createElement(r.Fragment,null,t.props.children):null;for(const a of o)if(a.props.condition)return r.createElement(r.Fragment,null,a.props.children);return l?r.createElement(r.Fragment,null,l.props.children):null};p.displayName="If",p.Then=N,p.ElseIf=F,p.Else=v,p.createTyped=function(){return{If:p,Then:N,ElseIf:F,Else:v}};const B=({condition:e,children:n})=>e?r.createElement(r.Fragment,null,n):null,V=({condition:e,children:n})=>e===!1?r.createElement(r.Fragment,null,n):null,Z=({all:e,any:n,none:t,children:l,fallback:o})=>h(()=>(e&&(n||t)&&console.warn('When: Multiple condition types (all, any, none) provided; "all" takes precedence.'),!!(e&&e.length>0&&e.every(Boolean)||n&&n.length>0&&n.some(Boolean)||t&&t.length>0&&t.every(a=>!a))),[e,n,t])?r.createElement(r.Fragment,null,l):r.createElement(r.Fragment,null,o||null),z=({data:e,transform:n,render:t,fallback:l})=>{const o=h(()=>n.reduce((a,s)=>s(a),e),[e,n]);return o==null?r.createElement(r.Fragment,null,l||null):r.createElement(r.Fragment,null,t(o))},L=e=>{const{children:n,h:t,w:l,size:o,height:a,width:s,className:c}=e;return r.createElement("div",{style:{width:o||l||s,height:o||t||a,flexShrink:0},className:c},n)},q=({let:e,props:n,children:t,fallback:l})=>{const o=h(()=>typeof e=="function"?e(n):e,[e,n]);return!t||!Object.keys(o).length?r.createElement(r.Fragment,null,l||null):r.createElement(r.Fragment,null,t(o))};function M(...e){const n=new Set;for(const t of e)if(t){if(typeof t=="string")n.add(t);else if(Array.isArray(t))t.forEach(l=>n.add(l));else if(typeof t=="object")for(const[l,o]of Object.entries(t))o&&n.add(l)}return Array.from(n).join(" ")}const G=e=>typeof e=="object"&&!!e,b=({className:e,children:n,asWrapper:t=!1})=>{if(!n)return null;if(J.count(n)>1)return console.error("<Styles>: children has more than one child. Please check your code."),r.createElement(S,null,n);if(!e)return r.createElement(S,null,n);const l=typeof e=="string"?e:M(...Object.values(e));if(t)return r.createElement(t===!0?"div":t,{className:l},n);if(P(n)){const o=n;let a=o?.props?.className;return o?.type?.displayName===b.displayName&&G(a)&&(a=M(...Object.values(a))),R(n,{className:M(l,a)})}return console.error("<Styles>: children is not a valid React element. Please check your code."),r.createElement(S,null,n)};b.displayName="W/Styles";const K=e=>{const{index:n=0,options:t,next:l,render:o}=e;W(()=>{if(t.length<n+1)throw new Error(`Index ${n} is out of bounds for options array of length ${t.length}. Defaulting to first option.`)},[n,t]);const[a,s]=I(n),c=()=>{s(i=>t.length?l?l(i,t):(i+1)%t.length:i)};return o(t[a],c)};function Q(e){const{items:n,renderItem:t,filter:l}=e;return n?r.createElement(S,null,n.map((o,a)=>l&&!l(o)?null:t(o,a))):(console.error("ArrayRender: items is null"),null)}function U({source:e,format:n,children:t}){const l=h(()=>{if(e instanceof Date)return e;if(typeof e=="string"||typeof e=="number"){const a=new Date(e);return isNaN(a.getTime())?null:a}return null},[e]),o=h(()=>l?n?n(l):l.toLocaleString():null,[l,n]);return!o||!t?null:r.createElement(r.Fragment,null,t(o))}const X="onChange",$="value";function ee(e){const{defaultValue:n,onBeforeChange:t,trigger:l=X,valuePropName:o=$,props:a}=e,s=Object.prototype.hasOwnProperty.call(a,o),[c,i]=I(n),d=s?a[o]:c,u=h(()=>a[l],[a,l]),m=H(f=>{const g=typeof f=="function"?f(d):f;t&&t(g,d)===!1||(s||i(g),u&&u(g))},[s,t,d,u]);return[d,m]}function te(e,n){let t=e;const l=[],o=()=>t,a=s=>{const c=t;t=s,n&&n(s,c),l.forEach(i=>i(t))};return{get:o,set:a,use:()=>{const[s,c]=r.useState(t);return r.useEffect(()=>(l.push(c),()=>{const i=l.indexOf(c);i>-1&&l.splice(i,1)}),[]),[s,a]}}}function ne(e,n){const t=n||new Date,l=t.getFullYear(),o=t.getMonth()+1,a=t.getDate(),s=t.getHours(),c=t.getMinutes(),i=t.getSeconds(),d=t.getMilliseconds(),u=t.getDay(),m=["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],f=["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],g=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],x=["January","February","March","April","May","June","July","August","September","October","November","December"],A=f[u],D=m[u],C=o-1,Y=x[C],k=g[C],O={YY:l.toString().slice(2),YYYY:l.toString(),M:o.toString(),MM:o.toString().padStart(2,"0"),MMM:k,MMMM:Y,D:a.toString(),DD:a.toString().padStart(2,"0"),d:u.toString(),dd:D,ddd:D,dddd:A,H:s.toString(),HH:s.toString().padStart(2,"0"),h:(s%12).toString(),hh:(s%12).toString().padStart(2,"0"),m:c.toString(),mm:c.toString().padStart(2,"0"),s:i.toString(),ss:i.toString().padStart(2,"0"),SSS:d.toString().padStart(3,"0"),Z:"+08:00",ZZ:"+0800",A:s<12?"AM":"PM",a:s<12?"am":"pm"};return e.replace(/YYYY|YY|M{1,4}|D{1,2}|d{1,4}|H{1,2}|h{1,2}|m{1,2}|s{1,2}|SSS|Z{1,2}|A|a/g,j=>O[j])}class re{count=0;next(){return this.count++}}export{Q as ArrayRender,re as Counter,U as DateRender,V as False,p as If,z as Pipe,q as Scope,L as SizeBox,b as Styles,y as Switch,K as Toggle,B as True,Z as When,T as childrenLoop,te as createExternalState,M as cx,ne as formatDate,ee as useControlled};
1
+ import r,{useMemo as p,Children as J,Fragment as S,isValidElement as R,cloneElement as W,useEffect as H,useState as x,useCallback as _}from"react";function C(t,n){if(t===void 0)return;let e=0;if(Array.isArray(t)){for(const l of t)if(n(l,e++)===!1)break}else n(t,e)}const B=(t,n)=>t===n,E=t=>r.createElement(r.Fragment,null,t.children);E.displayName="Switch_Case";const w=t=>r.createElement(r.Fragment,null,t.children);w.displayName="Switch_Default";const y=t=>{const{value:n,compare:e=B,children:l,strict:o=!1}=t,a=new Set;let s=null,c=null,i=!1;return C(l,(d,u)=>{if(!r.isValidElement(d))throw new Error(`Switch Children only accepts valid React elements at index ${u}`);const m=d.type;if(m.displayName===E.displayName){const f=d.props;if(a.has(f.value))throw new Error(`Switch found duplicate Case value at index ${u}: ${JSON.stringify(f.value)}${o?" (detected in strict mode)":""}`);if(a.add(f.value),!s&&e(n,f.value)&&(s=f.children,o===!1))return!1}else if(m.displayName===w.displayName){if(i)throw new Error(`Switch can only have one Default child at index ${u}`);if(i=!0,c=d.props.children,!o&&s)return!1}else throw new Error(`Switch Children only accepts 'Case' or 'Default' elements, found: ${String(m.displayName||m.name||m)} at index ${u}`)}),r.createElement(r.Fragment,null,s??c)};y.displayName="Switch",y.Case=E,y.Default=w,y.createTyped=function(){return{Switch:y,Case:E,Default:w}};const N=t=>r.createElement(r.Fragment,null,t.children),v=({children:t})=>r.createElement(r.Fragment,null,t),F=t=>r.createElement(r.Fragment,null,t.children);N.displayName="If_Then",v.displayName="If_Else",F.displayName="If_ElseIf";const h=({condition:t,children:n})=>{let e=null,l=null;const o=[];if(r.Children.forEach(n,a=>{if(!r.isValidElement(a))throw new Error("If component only accepts valid React elements");const s=a.type;if(s.displayName===N.displayName){if(e)throw new Error("If component can only have one Then child");e=a}else if(s.displayName===F.displayName)o.push(a);else if(s.displayName===v.displayName){if(l)throw new Error("If component can only have one Else child");l=a}else throw new Error(`If component only accepts 'Then', 'ElseIf', or 'Else' elements as children, found: ${String(s.displayName||s.name||s)}`)}),t)return e?r.createElement(r.Fragment,null,e.props.children):null;for(const a of o)if(a.props.condition)return r.createElement(r.Fragment,null,a.props.children);return l?r.createElement(r.Fragment,null,l.props.children):null};h.displayName="If",h.Then=N,h.ElseIf=F,h.Else=v,h.createTyped=function(){return{If:h,Then:N,ElseIf:F,Else:v}};const V=({condition:t,children:n})=>t?r.createElement(r.Fragment,null,n):null,Z=({condition:t,children:n})=>t===!1?r.createElement(r.Fragment,null,n):null,z=({all:t,any:n,none:e,children:l,fallback:o})=>p(()=>(t&&(n||e)&&console.warn('When: Multiple condition types (all, any, none) provided; "all" takes precedence.'),!!(t&&t.length>0&&t.every(Boolean)||n&&n.length>0&&n.some(Boolean)||e&&e.length>0&&e.every(a=>!a))),[t,n,e])?r.createElement(r.Fragment,null,l):r.createElement(r.Fragment,null,o||null),L=({data:t,transform:n,render:e,fallback:l})=>{const o=p(()=>n.reduce((a,s)=>s(a),t),[t,n]);return o==null?r.createElement(r.Fragment,null,l||null):r.createElement(r.Fragment,null,e(o))},q=t=>{const{children:n,h:e,w:l,size:o,height:a,width:s,className:c}=t;return r.createElement("div",{style:{width:o||l||s,height:o||e||a,flexShrink:0},className:c},n)},G=({let:t,props:n,children:e,fallback:l})=>{const o=p(()=>typeof t=="function"?t(n):t,[t,n]);return!e||!Object.keys(o).length?r.createElement(r.Fragment,null,l||null):r.createElement(r.Fragment,null,e(o))};function M(...t){const n=new Set;for(const e of t)if(e){if(typeof e=="string")n.add(e);else if(Array.isArray(e))e.forEach(l=>n.add(l));else if(typeof e=="object")for(const[l,o]of Object.entries(e))o&&n.add(l)}return Array.from(n).join(" ")}const K=t=>typeof t=="object"&&!!t,b=({className:t,children:n,asWrapper:e=!1})=>{if(!n)return null;if(J.count(n)>1)return console.error("<Styles>: children has more than one child. Please check your code."),r.createElement(S,null,n);if(!t)return r.createElement(S,null,n);const l=typeof t=="string"?t:M(...Object.values(t));if(e)return r.createElement(e===!0?"div":e,{className:l},n);if(R(n)){const o=n;let a=o?.props?.className;return o?.type?.displayName===b.displayName&&K(a)&&(a=M(...Object.values(a))),W(n,{className:M(l,a)})}return console.error("<Styles>: children is not a valid React element. Please check your code."),r.createElement(S,null,n)};b.displayName="W/Styles";const Q=t=>{const{index:n=0,options:e,next:l,render:o}=t;H(()=>{if(e.length<n+1)throw new Error(`Index ${n} is out of bounds for options array of length ${e.length}. Defaulting to first option.`)},[n,e]);const[a,s]=x(n),c=()=>{s(i=>e.length?l?l(i,e):(i+1)%e.length:i)};return o(e[a],c)};function U(t){const{items:n,renderItem:e,filter:l}=t;return n?r.createElement(S,null,n.map((o,a)=>l&&!l(o)?null:e(o,a))):(console.error("ArrayRender: items is null"),null)}function X({source:t,format:n,children:e}){const l=p(()=>{if(t instanceof Date)return t;if(typeof t=="string"||typeof t=="number"){const a=new Date(t);return isNaN(a.getTime())?null:a}return null},[t]),o=p(()=>l?n?n(l):l.toLocaleString():null,[l,n]);return!o||!e?null:r.createElement(r.Fragment,null,e(o))}const $="onChange",ee="value";function te(t){const{defaultValue:n,onBeforeChange:e,trigger:l=$,valuePropName:o=ee,props:a}=t,s=Object.prototype.hasOwnProperty.call(a,o),[c,i]=x(n),d=s?a[o]:c,u=p(()=>a[l],[a,l]),m=_(f=>{const g=typeof f=="function"?f(d):f;e&&e(g,d)===!1||(s||i(g),u&&u(g))},[s,e,d,u]);return[d,m]}function ne(t,...n){try{const e=t(...n);return e instanceof Promise?e:Promise.resolve(e)}catch(e){return Promise.reject(e)}}const I=typeof Promise.try=="function"?Promise.try:ne;function re(t,n){let e=t;const l=[],o=()=>e,a=s=>{const c=e;e=s,l.forEach(i=>i(e)),n&&I(n,e,c).catch(i=>{console.error("Error in external state side effect, Please do it within side effects:",i)})};return{get:o,set:a,use:()=>{const[s,c]=r.useState(e);return r.useEffect(()=>(l.push(c),()=>{const i=l.indexOf(c);i>-1&&l.splice(i,1)}),[]),[s,a]}}}function le(t,n){const e=n||new Date,l=e.getFullYear(),o=e.getMonth()+1,a=e.getDate(),s=e.getHours(),c=e.getMinutes(),i=e.getSeconds(),d=e.getMilliseconds(),u=e.getDay(),m=["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],f=["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],g=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],A=["January","February","March","April","May","June","July","August","September","October","November","December"],Y=f[u],D=m[u],T=o-1,P=A[T],k=g[T],O={YY:l.toString().slice(2),YYYY:l.toString(),M:o.toString(),MM:o.toString().padStart(2,"0"),MMM:k,MMMM:P,D:a.toString(),DD:a.toString().padStart(2,"0"),d:u.toString(),dd:D,ddd:D,dddd:Y,H:s.toString(),HH:s.toString().padStart(2,"0"),h:(s%12).toString(),hh:(s%12).toString().padStart(2,"0"),m:c.toString(),mm:c.toString().padStart(2,"0"),s:i.toString(),ss:i.toString().padStart(2,"0"),SSS:d.toString().padStart(3,"0"),Z:"+08:00",ZZ:"+0800",A:s<12?"AM":"PM",a:s<12?"am":"pm"};return t.replace(/YYYY|YY|M{1,4}|D{1,2}|d{1,4}|H{1,2}|h{1,2}|m{1,2}|s{1,2}|SSS|Z{1,2}|A|a/g,j=>O[j])}class ae{count=0;next(){return this.count++}}export{U as ArrayRender,ae as Counter,X as DateRender,Z as False,h as If,L as Pipe,G as Scope,q as SizeBox,b as Styles,y as Switch,Q as Toggle,V as True,z as When,C as childrenLoop,re as createExternalState,M as cx,le as formatDate,I as safePromiseTry,te as useControlled};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wwog/react",
3
- "version": "1.2.9",
3
+ "version": "1.2.10",
4
4
  "description": "A practical React component library providing declarative flow control and common UI utility components to make your React code more concise and readable.",
5
5
  "keywords": [
6
6
  "react",
@@ -1,4 +1,5 @@
1
1
  import React from "react";
2
+ import { safePromiseTry } from "./promise";
2
3
 
3
4
  /**
4
5
  * @en Callback function type for state change listeners
@@ -14,7 +15,10 @@ export type CreateStateListener<T> = (state: T) => void;
14
15
  * @param newState The new state value / 新的状态值
15
16
  * @param prevState The previous state value / 之前的状态值
16
17
  */
17
- export type ExternalSideEffect<T> = (newState: T, prevState: T) => void | Promise<void>;
18
+ export type ExternalSideEffect<T> = (
19
+ newState: T,
20
+ prevState: T
21
+ ) => void | Promise<void>;
18
22
 
19
23
  /**
20
24
  * @en External state management interface
@@ -28,14 +32,14 @@ export interface ExternalState<T> {
28
32
  * @returns The current state value / 当前状态值
29
33
  */
30
34
  get: () => T;
31
-
35
+
32
36
  /**
33
37
  * @en Set a new state value
34
38
  * @zh 设置新的状态值
35
39
  * @param newState The new state value / 新的状态值
36
40
  */
37
41
  set: (newState: T) => void;
38
-
42
+
39
43
  /**
40
44
  * @en React Hook for using external state in components
41
45
  * @zh 在组件中使用外部状态的 React Hook
@@ -45,20 +49,20 @@ export interface ExternalState<T> {
45
49
  }
46
50
 
47
51
  /**
48
- *
52
+ *
49
53
  * @example
50
54
  * ```tsx
51
55
  * // Create an app-level theme state
52
56
  * const themeState = createExternalState('light');
53
- *
54
- * // Get or modify state outside components
57
+ *
58
+ * // Get or modify state outside components
55
59
  * console.log(themeState.get()); // 'light'
56
60
  * themeState.set('dark');
57
- *
61
+ *
58
62
  * // Use state in components
59
63
  * function ThemeConsumer() {
60
64
  * const [theme, setTheme] = themeState.use();
61
- *
65
+ *
62
66
  * return (
63
67
  * <div className={theme}>
64
68
  * <button onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}>
@@ -81,10 +85,13 @@ export function createExternalState<T>(
81
85
  const set = (newState: T) => {
82
86
  const prevState = state;
83
87
  state = newState;
88
+
89
+ listeners.forEach((listener) => listener(state));
84
90
  if (sideEffect) {
85
- sideEffect(newState, prevState);
91
+ safePromiseTry(sideEffect, state, prevState).catch((error) => {
92
+ console.error("Error in external state side effect, Please do it within side effects:", error);
93
+ });
86
94
  }
87
- listeners.forEach((listener) => listener(state));
88
95
  };
89
96
 
90
97
  const use = () => {
@@ -2,3 +2,4 @@ export * from "./createExternalState";
2
2
  export * from "./cx";
3
3
  export * from "./reactUtils";
4
4
  export * from "./sundry";
5
+ export * from "./promise";
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Takes a callback of any kind (returns or throws, synchronously or asynchronously) and wraps its result
3
+ * in a Promise.
4
+ *
5
+ * @param callbackFn A function that is called synchronously. It can do anything: either return
6
+ * a value, throw an error, or return a promise.
7
+ * @param args Additional arguments, that will be passed to the callback.
8
+ *
9
+ * @returns A Promise that is:
10
+ * - Already fulfilled, if the callback synchronously returns a value.
11
+ * - Already rejected, if the callback synchronously throws an error.
12
+ * - Asynchronously fulfilled or rejected, if the callback returns a promise.
13
+ */
14
+ function promiseTry<T, U extends unknown[]>(
15
+ callbackFn: (...args: U) => T | PromiseLike<T>,
16
+ ...args: U
17
+ ): Promise<Awaited<T>> {
18
+ try {
19
+ const result = callbackFn(...args); // Call the callback function
20
+ // Check if the result is a PromiseLike (Promise)
21
+ if (result instanceof Promise) {
22
+ return result; // If it's a promise, return it directly
23
+ }
24
+ // If the result is not a promise, resolve it with Promise.resolve
25
+ return Promise.resolve(result as Awaited<T>);
26
+ } catch (error) {
27
+ // If the callback throws an error, reject the promise
28
+ return Promise.reject(error);
29
+ }
30
+ }
31
+
32
+ export const safePromiseTry = (() => {
33
+ if (typeof Promise.try === "function") {
34
+ return Promise.try;
35
+ } else {
36
+ return promiseTry;
37
+ }
38
+ })();