nfx-ui 0.5.2 → 0.6.1

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/events.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * 定义品牌类型:用于 defineXxx 返回值,仅对应 createXxx/constructor 可接受;Tag 区分不同 define(如 "events" | "router")。
2
+ * 定义品牌类型:用于 defineXxx 的返回值,仅对应的 createXxx/构造函数可接受;Tag 区分不同定义(如 "events" | "router")。
3
3
  * Define brand type: for defineXxx return value, only the matching createXxx accepts; Tag discriminates (e.g. "events" | "router").
4
4
  * @example Defined<{ HOME: "/" }, "router"> 仅能传入 createRouter;Defined<{ FOO: "x" }, "events"> 仅能传入 EventEmitter
5
5
  */
@@ -132,7 +132,7 @@ export declare const NFX_NAMESPACES: readonly ["theme", "language", "layout", "p
132
132
  export declare const NFX_NAMESPACES_MAP: NameSpacesMap;
133
133
 
134
134
  /**
135
- * 可为 null 或 undefined。T | null | undefined.
135
+ * 可为 null 或 undefined。Nilable: T | null | undefined.
136
136
  * @example Nilable<boolean> => boolean | null | undefined
137
137
  */
138
138
  declare type Nilable<T> = T | null | undefined;
package/dist/layouts.d.ts CHANGED
@@ -133,7 +133,7 @@ export declare interface MainWrapperProps {
133
133
  }
134
134
 
135
135
  /**
136
- * 可为 null 或 undefined。T | null | undefined.
136
+ * 可为 null 或 undefined。Nilable: T | null | undefined.
137
137
  * @example Nilable<boolean> => boolean | null | undefined
138
138
  */
139
139
  declare type Nilable<T> = T | null | undefined;
@@ -12,7 +12,7 @@ export declare function createRouter<R extends Record<string, string>>(routes: D
12
12
  };
13
13
 
14
14
  /**
15
- * 定义品牌类型:用于 defineXxx 返回值,仅对应 createXxx/constructor 可接受;Tag 区分不同 define(如 "events" | "router")。
15
+ * 定义品牌类型:用于 defineXxx 的返回值,仅对应的 createXxx/构造函数可接受;Tag 区分不同定义(如 "events" | "router")。
16
16
  * Define brand type: for defineXxx return value, only the matching createXxx accepts; Tag discriminates (e.g. "events" | "router").
17
17
  * @example Defined<{ HOME: "/" }, "router"> 仅能传入 createRouter;Defined<{ FOO: "x" }, "events"> 仅能传入 EventEmitter
18
18
  */
@@ -52,7 +52,7 @@ export declare function defineRouter<R extends Record<string, string>>(routes: R
52
52
  export declare function isActiveRoute(currentPath: string, targetPath: string): boolean;
53
53
 
54
54
  /**
55
- * 对象 T 的键类型(即 keyof T)。Key type of T (keyof T).
55
+ * 对象 T 的键字面量联合类型,即 keyof T。KeyOf: key type of T.
56
56
  * @example KeyOf<{ a: 1; b: 2 }> => "a" | "b"
57
57
  */
58
58
  declare type KeyOf<T> = keyof T;
package/dist/themes.d.ts CHANGED
@@ -149,7 +149,7 @@ export declare function getThemeBaseStorage(): Nilable<BaseEnum>;
149
149
  export declare function getThemeColorStorage(): Nilable<ThemeEnum>;
150
150
 
151
151
  /**
152
- * 可为 null 或 undefined。T | null | undefined.
152
+ * 可为 null 或 undefined。Nilable: T | null | undefined.
153
153
  * @example Nilable<boolean> => boolean | null | undefined
154
154
  */
155
155
  declare type Nilable<T> = T | null | undefined;
package/dist/types.d.ts CHANGED
@@ -20,7 +20,15 @@ declare type Array_2<T> = T[];
20
20
  export { Array_2 as Array }
21
21
 
22
22
  /**
23
- * 数组或单个元素类型。Array or single element type.
23
+ * 可为空数组;T 为元素类型,等价于 ArrayType<T> | []。
24
+ * Arrayable: T[] | [],T 为数组元素类型。
25
+ * @example Arrayable<string> => string[] | []
26
+ */
27
+ export declare type Arrayable<T> = Array_2<T> | [];
28
+
29
+ /**
30
+ * 数组或单个元素:T[] | T。用于接口/参数既可传数组也可传单元素时。
31
+ * Array or single element: T[] | T. For APIs that accept either an array or one element.
24
32
  * @example ArrayOrSingle<string> => string[] | string
25
33
  */
26
34
  export declare type ArrayOrSingle<T> = T[] | T;
@@ -44,7 +52,7 @@ export declare interface DataResponse<T, M = Record<string, unknown>> extends Ba
44
52
  }
45
53
 
46
54
  /**
47
- * 定义品牌类型:用于 defineXxx 返回值,仅对应 createXxx/constructor 可接受;Tag 区分不同 define(如 "events" | "router")。
55
+ * 定义品牌类型:用于 defineXxx 的返回值,仅对应的 createXxx/构造函数可接受;Tag 区分不同定义(如 "events" | "router")。
48
56
  * Define brand type: for defineXxx return value, only the matching createXxx accepts; Tag discriminates (e.g. "events" | "router").
49
57
  * @example Defined<{ HOME: "/" }, "router"> 仅能传入 createRouter;Defined<{ FOO: "x" }, "events"> 仅能传入 EventEmitter
50
58
  */
@@ -53,19 +61,27 @@ export declare type Defined<T, Tag extends string> = T & {
53
61
  };
54
62
 
55
63
  /**
56
- * 存在性结果元组:[值, true] 表示有值,[null, false] 表示无。Existence result tuple: [value, true] or [null, false].
64
+ * 可为 null、undefined 或空字符串。Emptyable: T | null | undefined | "".
65
+ * @example Emptyable<string> => string | null | undefined | ""
66
+ */
67
+ export declare type Emptyable<T> = T | null | undefined | "";
68
+
69
+ /**
70
+ * 存在性结果元组:有值时为 [T, true],无值时为 [null, false]。用于区分「有值」与「无」而不依赖 undefined。
71
+ * Existence result tuple: [T, true] when present, [null, false] when absent.
57
72
  * @example ExistenceResult<{ id: string }> => [{ id: string }, true] | [null, false]
58
73
  */
59
74
  export declare type ExistenceResult<T> = [T, true] | [null, false];
60
75
 
61
76
  /**
62
- * 判断结果是否为 OK。Check if result is OK.
63
- * @example isOK<{ id: string }> => [value, true] | [null, false]
77
+ * 判断结果为 OK 时的元组形态:[T, true];与 ExistenceResult 的「有值」分支一致。
78
+ * Result is OK: tuple [T, true]; same as the present branch of ExistenceResult.
79
+ * @example isOK<{ id: string }> => [T, true]
64
80
  */
65
81
  export declare type isOK<T> = [T, true] | [null, false];
66
82
 
67
83
  /**
68
- * 对象 T 的键类型(即 keyof T)。Key type of T (keyof T).
84
+ * 对象 T 的键字面量联合类型,即 keyof T。KeyOf: key type of T.
69
85
  * @example KeyOf<{ a: 1; b: 2 }> => "a" | "b"
70
86
  */
71
87
  export declare type KeyOf<T> = keyof T;
@@ -93,23 +109,29 @@ export declare interface ListMeta {
93
109
  }
94
110
 
95
111
  /**
96
- * 可为 undefined。T | undefined.
112
+ * 可为 undefined。Maybe: T | undefined.
97
113
  * @example Maybe<number> => number | undefined
98
114
  */
99
115
  export declare type Maybe<T> = T | undefined;
100
116
 
101
117
  /**
102
- * 可为 null 或 undefined。T | null | undefined.
118
+ * 可为 null 或 undefined。Nilable: T | null | undefined.
103
119
  * @example Nilable<boolean> => boolean | null | undefined
104
120
  */
105
121
  export declare type Nilable<T> = T | null | undefined;
106
122
 
107
123
  /**
108
- * 可为 null。T | null.
124
+ * 可为 null。Nullable: T | null.
109
125
  * @example Nullable<string> => string | null
110
126
  */
111
127
  export declare type Nullable<T> = T | null;
112
128
 
129
+ /**
130
+ * 可为空对象。Objectable: T | {},T 为对象类型。
131
+ * @example Objectable<{ a: number }> => { a: number } | {}
132
+ */
133
+ export declare type Objectable<T extends Record<string, unknown>> = T | {};
134
+
113
135
  /** 数字 offset 分页参数(用于 makeCursorFetchFunction 等)。Number offset pagination params; used by makeCursorFetchFunction etc. */
114
136
  export declare interface OffsetLimitNumber {
115
137
  /** 偏移量。Offset. */
@@ -139,9 +161,21 @@ export declare interface PaginatedResponse<T> {
139
161
  }
140
162
 
141
163
  /**
142
- * 对象 T 所有值的联合类型。Union of all value types of T.
164
+ * 可为空字符串的类型。Stringable: T | "",T string 子类型。
165
+ * @example Stringable<string> => string | ""
166
+ */
167
+ export declare type Stringable<T extends string> = T | "";
168
+
169
+ /**
170
+ * 对象 T 所有属性值的联合类型。ValueOf: union of all property value types of T.
143
171
  * @example ValueOf<{ a: number; b: string }> => number | string
144
172
  */
145
173
  export declare type ValueOf<T> = T[keyof T];
146
174
 
175
+ /**
176
+ * 可为 0 的数值类型。Zeroable: T | 0,T 为 number 子类型。
177
+ * @example Zeroable<number> => number | 0
178
+ */
179
+ export declare type Zeroable<T extends number> = T | 0;
180
+
147
181
  export { }
package/dist/utils.cjs CHANGED
@@ -1,5 +1,5 @@
1
1
  (function(){try{if(typeof document<"u"){var e=document.createElement("style");e.appendChild(document.createTextNode(`.styles-module__loading___bgEAk{justify-content:center;align-items:center;display:inline-flex}.styles-module__loading___bgEAk svg polyline{fill:none;stroke-width:3px;stroke-linecap:round;stroke-linejoin:round}.styles-module__loading___bgEAk svg polyline.styles-module__back___XLPvD{fill:none;stroke:var(--color-primary-alpha,#ff4d5033)}.styles-module__loading___bgEAk svg polyline.styles-module__front___hSxig{fill:none;stroke:var(--color-primary,#ff4d4f);stroke-dasharray:48 144;stroke-dashoffset:192px;animation:1.4s linear infinite styles-module__dash_682___AQzo6}@keyframes styles-module__dash_682___AQzo6{72.5%{opacity:0}to{stroke-dashoffset:0}}.styles-module__loader___E7OIM{justify-content:center;align-items:center;width:fit-content;height:fit-content;display:flex}.styles-module__truckWrapper___Sk4zX{flex-direction:column;justify-content:flex-end;align-items:center;width:100%;height:100%;display:flex;position:relative;overflow-x:hidden}.styles-module__truckBody___j7w2C{width:65%;height:fit-content;margin-bottom:6px;animation:1s linear infinite styles-module__motion___IiNlW}@keyframes styles-module__motion___IiNlW{0%{transform:translateY(0)}50%{transform:translateY(3px)}to{transform:translateY(0)}}.styles-module__truckTires___4ncTl{justify-content:space-between;align-items:center;width:65%;height:fit-content;padding:0 10px 0 15px;display:flex;position:absolute;bottom:0}.styles-module__tiresvg___IBQcN{width:24px}.styles-module__road___sxx-E{background-color:var(--color-fg-heading);border-radius:3px;align-self:flex-end;width:100%;height:1.5px;position:relative;bottom:0}.styles-module__road___sxx-E:before{content:"";background-color:var(--color-fg-heading);border-left:10px solid var(--color-bg);border-radius:3px;width:20px;height:100%;animation:1.4s linear infinite styles-module__roadAnimation___yvrHP;position:absolute;right:-50%}.styles-module__road___sxx-E:after{content:"";background-color:var(--color-fg-heading);border-left:4px solid var(--color-bg);border-radius:3px;width:10px;height:100%;animation:1.4s linear infinite styles-module__roadAnimation___yvrHP;position:absolute;right:-65%}.styles-module__lampPost___okcN5{height:45%;animation:1.4s linear infinite styles-module__roadAnimation___yvrHP;position:absolute;bottom:0;right:-90%}@keyframes styles-module__roadAnimation___yvrHP{0%{transform:translate(0)}to{transform:translate(-350px)}}.styles-module__loader___pFUzL{justify-content:center;align-items:center;display:inline-flex;position:relative}.styles-module__loader___pFUzL:before{content:"";background:var(--color-primary);opacity:.3;border-radius:50%;width:100%;height:5px;animation:.5s linear infinite styles-module__shadow324___sutUe;position:absolute;top:calc(100% + 12px);left:0}.styles-module__loader___pFUzL:after{content:"";background:var(--color-primary);width:100%;height:100%;position:absolute;top:0;left:0}.styles-module__square___GKjhm:after{border-radius:4px;animation:.5s linear infinite styles-module__jump7456Square___Z-D9b}.styles-module__circle___bUpSN:after{border-radius:50%;animation:.5s linear infinite styles-module__jump7456Circle___2AfF7}@keyframes styles-module__jump7456Square___Z-D9b{15%{border-bottom-right-radius:3px}25%{transform:translateY(9px)rotate(22.5deg)}50%{border-bottom-right-radius:40px;transform:translateY(18px)scaleY(.9)rotate(45deg)}75%{transform:translateY(9px)rotate(67.5deg)}to{transform:translateY(0)rotate(90deg)}}@keyframes styles-module__jump7456Circle___2AfF7{15%{border-bottom-right-radius:50%}25%{transform:translateY(9px)rotate(22.5deg)}50%{border-bottom-right-radius:40px;transform:translateY(18px)scaleY(.9)rotate(45deg)}75%{transform:translateY(9px)rotate(67.5deg)}to{transform:translateY(0)rotate(90deg)}}@keyframes styles-module__shadow324___sutUe{0%,to{transform:scale(1)}50%{transform:scaleX(1.2)}}.style-module__waves___oR3u7{width:100%;height:100%;margin:0;padding:0;position:absolute;top:0;left:0;overflow:hidden}.style-module__waves___oR3u7:before{content:"";width:.5rem;height:.5rem;transform:translate3d(calc(var(--x) - 50%), calc(var(--y) - 50%), 0);will-change:transform;background:#160000;border-radius:50%;position:absolute;top:0;left:0}.style-module__wavesCanvas___zkhTC{width:100%;height:100%;display:block}.style-module__squaresCanvas___22kMc{border:none;width:100%;height:100%;display:block}.style-module__container___HkxIe{background-color:#000;width:100%;height:100%;position:relative;overflow:hidden}.style-module__canvas___MR9JX{width:100%;height:100%;display:block}.style-module__outerVignette___i75nw{pointer-events:none;background:radial-gradient(circle,#0000 60%,#000 100%);width:100%;height:100%;position:absolute;top:0;left:0}.style-module__centerVignette___n9uVG{pointer-events:none;background:radial-gradient(circle,#000c 0%,#0000 60%);width:100%;height:100%;position:absolute;top:0;left:0}.style-module__container___Hh3-c{width:100%;height:100%;position:relative;overflow:hidden}.styles-module__button___RbliA{cursor:pointer;border:1px solid #0000;border-radius:.5rem;outline:none;justify-content:center;align-items:center;gap:.5rem;font-family:inherit;font-weight:500;transition:all .2s;display:inline-flex;position:relative}.styles-module__button___RbliA:focus-visible{outline:2px solid var(--color-primary);outline-offset:2px}.styles-module__button___RbliA:disabled{cursor:not-allowed;opacity:.6}.styles-module__button___RbliA.styles-module__small___yK0Yp{min-height:2rem;padding:.5rem 1rem;font-size:.8125rem}.styles-module__button___RbliA.styles-module__medium___eBTch{min-height:2.5rem;padding:.75rem 1.5rem;font-size:.875rem}.styles-module__button___RbliA.styles-module__large___pIZkl{min-height:3rem;padding:1rem 2rem;font-size:1rem}.styles-module__button___RbliA.styles-module__primary___qsZpA{background:var(--color-primary);border-color:var(--color-primary);color:var(--color-primary-fg,#fff)}.styles-module__button___RbliA.styles-module__primary___qsZpA:hover:not(:disabled){background:var(--color-primary-light);border-color:var(--color-primary-light);transform:translateY(-1px)}.styles-module__button___RbliA.styles-module__primary___qsZpA:active:not(:disabled){transform:translateY(0)}.styles-module__button___RbliA.styles-module__secondary___SCpPv{background:var(--color-bg-2);border-color:var(--color-border-4);color:var(--color-fg-text)}.styles-module__button___RbliA.styles-module__secondary___SCpPv:hover:not(:disabled){background:var(--color-bg-3);border-color:var(--color-primary)}.styles-module__button___RbliA.styles-module__outline___rrlk9{border-color:var(--color-border-4);color:var(--color-fg-text);background:0 0}.styles-module__button___RbliA.styles-module__outline___rrlk9:hover:not(:disabled){background:var(--color-bg-2);border-color:var(--color-primary)}.styles-module__button___RbliA.styles-module__ghost___QMoiH{color:var(--color-fg-text);background:0 0;border-color:#0000}.styles-module__button___RbliA.styles-module__ghost___QMoiH:hover:not(:disabled){background:var(--color-bg-2)}.styles-module__button___RbliA.styles-module__danger___MW-pg{background:var(--color-danger);border-color:var(--color-danger);color:#fff}.styles-module__button___RbliA.styles-module__danger___MW-pg:hover:not(:disabled){background:var(--color-danger-light);border-color:var(--color-danger-light);transform:translateY(-1px)}.styles-module__button___RbliA.styles-module__danger___MW-pg:active:not(:disabled){transform:translateY(0)}.styles-module__button___RbliA.styles-module__fullWidth___-dXBR{width:100%}.styles-module__button___RbliA.styles-module__loading___UWw6V{pointer-events:none}.styles-module__spinner___KpJ-L{border:2px solid;border-top-color:#0000;border-radius:50%;flex-shrink:0;width:1rem;height:1rem;animation:.6s linear infinite styles-module__spin___ntZKM}@keyframes styles-module__spin___ntZKM{to{transform:rotate(360deg)}}.styles-module__button___RbliA.styles-module__small___yK0Yp .styles-module__spinner___KpJ-L{border-width:1.5px;width:.875rem;height:.875rem}.styles-module__button___RbliA.styles-module__large___pIZkl .styles-module__spinner___KpJ-L{border-width:2.5px;width:1.25rem;height:1.25rem}.styles-module__content___oyZRO{align-items:center;display:flex}.styles-module__leftIcon___cDktv,.styles-module__rightIcon___1mpk0{flex-shrink:0;align-items:center;display:flex}.styles-module__leftIcon___cDktv{margin-right:-.25rem}.styles-module__rightIcon___1mpk0{margin-left:-.25rem}.styles-module__button___RbliA.styles-module__iconOnly___iOv-U{aspect-ratio:1;min-width:2.5rem;min-height:2.5rem;padding:0}.styles-module__button___RbliA.styles-module__iconOnly___iOv-U.styles-module__small___yK0Yp{min-width:2rem;min-height:2rem}.styles-module__button___RbliA.styles-module__iconOnly___iOv-U.styles-module__large___pIZkl{min-width:3rem;min-height:3rem}.styles-module__iconContainer___bwvE3{justify-content:center;align-items:center;width:100%;height:100%;display:flex}.styles-module__layout___WaGy9{flex-direction:column;justify-content:center;align-items:center;gap:.25rem;width:100%;display:flex}.styles-module__horizontal___lKpGq{justify-content:center;align-items:center;gap:.5rem;width:100%;display:flex}.styles-module__topIcon___bzw6s,.styles-module__bottomIcon___suPfa{flex-shrink:0;justify-content:center;align-items:center;display:flex}.styles-module__iconOnly___iOv-U .styles-module__iconContainer___bwvE3 .styles-module__leftIcon___cDktv,.styles-module__iconOnly___iOv-U .styles-module__iconContainer___bwvE3 .styles-module__rightIcon___1mpk0{margin:0}.Dropdown-module__dropdown___c9wIp{width:100%;display:inline-block;position:relative}.Dropdown-module__dropdownButton___suNh-{border:1px solid var(--color-separator);cursor:pointer;text-align:left;background:var(--color-bg-2);width:100%;color:var(--color-fg-text);border-radius:8px;outline:none;justify-content:space-between;align-items:center;padding:.75rem 2.5rem .75rem 1rem;font-family:inherit;font-size:1rem;font-weight:400;line-height:1.5rem;transition:all .2s ease-in-out;display:flex;position:relative}.Dropdown-module__dropdownButton___suNh-:hover:not(:disabled){border-color:var(--color-primary);background:var(--color-bg-1)}.Dropdown-module__dropdownButton___suNh-:focus{border-color:var(--color-primary);background:var(--color-bg-1);box-shadow:0 0 0 2px var(--color-primary-transparent)}.Dropdown-module__dropdownButton___suNh-.Dropdown-module__disabled___Rz0pX{cursor:not-allowed;opacity:.6;background:var(--color-bg-3)}.Dropdown-module__dropdownButton___suNh-.Dropdown-module__error___F7dDu{border-color:#ef4444}.Dropdown-module__buttonText___D2zxn{text-overflow:ellipsis;white-space:nowrap;flex:1;overflow:hidden}.Dropdown-module__chevronIcon___uyawp{color:var(--color-fg-highlight);flex-shrink:0;transition:transform .15s;position:absolute;right:1rem}.Dropdown-module__chevronIcon___uyawp.Dropdown-module__open___APoXd{transform:rotate(180deg)}.Dropdown-module__dropdownMenu___WJ-QO{background:var(--color-bg);border:1px solid var(--color-border-4);z-index:1000;border-radius:8px;min-width:100%;animation:.15s ease-out Dropdown-module__slideDown___sRCmO;position:absolute;top:calc(100% + .5rem);left:0;right:0;overflow:hidden;box-shadow:0 .5rem 1rem #0000002d}@keyframes Dropdown-module__slideDown___sRCmO{0%{opacity:0;transform:translateY(-.5rem)}to{opacity:1;transform:translateY(0)}}.Dropdown-module__optionsList___Be6RN{margin:0;padding:.5rem 0;list-style:none}.Dropdown-module__option___q-NgT{cursor:pointer;color:var(--color-fg-text);align-items:center;padding:.75rem 1rem;font-size:.875rem;transition:all .15s;display:flex}.Dropdown-module__option___q-NgT:hover{background-color:var(--color-bg-2)}.Dropdown-module__option___q-NgT.Dropdown-module__selected___-OTW-{background-color:var(--color-bg-3);color:var(--color-fg-highlight);font-weight:600}@media (width<=768px){.Dropdown-module__dropdownButton___suNh-{padding:.625rem 2.25rem .625rem .875rem;font-size:.875rem}.Dropdown-module__chevronIcon___uyawp{right:.875rem}.Dropdown-module__option___q-NgT{padding:.625rem .875rem;font-size:.8125rem}}.styles-module__wrapper___COeGm{flex-direction:column;gap:.5rem;display:flex}.styles-module__wrapper___COeGm.styles-module__fullWidth___lVU49{width:100%}.styles-module__label___gfOA7{color:var(--color-fg-heading);align-items:center;gap:.25rem;font-size:.875rem;font-weight:500;display:flex}.styles-module__required___-zOy5{color:var(--color-danger)}.styles-module__inputContainer___wXY53{align-items:center;width:100%;display:flex;position:relative}.styles-module__input___IZDc3{border:1px solid var(--color-border-4);background:var(--color-bg);width:100%;color:var(--color-fg-text);border-radius:.5rem;outline:none;font-family:inherit;font-size:.875rem;transition:all .2s}.styles-module__input___IZDc3[type=password]::-webkit-credentials-auto-fill-button{visibility:hidden!important;opacity:0!important;pointer-events:none!important;appearance:none!important;display:none!important;position:absolute!important;left:-9999px!important}.styles-module__input___IZDc3[type=password]::-webkit-strong-password-toggle-button{visibility:hidden!important;opacity:0!important;pointer-events:none!important;appearance:none!important;width:0!important;height:0!important;margin:0!important;padding:0!important;display:none!important;position:absolute!important;left:-9999px!important}.styles-module__input___IZDc3[type=password]::-ms-reveal{visibility:hidden!important;opacity:0!important;pointer-events:none!important;width:0!important;height:0!important;display:none!important;position:absolute!important;left:-9999px!important}.styles-module__input___IZDc3[type=password]::-ms-clear{visibility:hidden!important;opacity:0!important;pointer-events:none!important;width:0!important;height:0!important;display:none!important;position:absolute!important;left:-9999px!important}.styles-module__input___IZDc3:focus{border-color:var(--color-primary);box-shadow:0 0 0 3px rgba(var(--color-primary-rgb,102, 126, 234), .1)}.styles-module__input___IZDc3::placeholder{color:var(--color-fg-muted,var(--color-fg-text));opacity:.5}.styles-module__input___IZDc3.styles-module__small___cuGyE{padding:.5rem .75rem;font-size:.8125rem}.styles-module__input___IZDc3.styles-module__medium___JdDPu{padding:.75rem;font-size:.875rem}.styles-module__input___IZDc3.styles-module__large___fMmiG{padding:1rem;font-size:1rem}.styles-module__input___IZDc3.styles-module__default___-eZTG{background:var(--color-bg);border-color:var(--color-border-4)}.styles-module__input___IZDc3.styles-module__filled___EQ5PQ{background:var(--color-bg-2);border-color:var(--color-border-3)}.styles-module__inputContainer___wXY53.styles-module__withLeftIcon___NFn6- .styles-module__input___IZDc3{padding-left:2.5rem}.styles-module__inputContainer___wXY53.styles-module__withRightIcon___mz-hn .styles-module__input___IZDc3{padding-right:2.5rem}.styles-module__inputContainer___wXY53.styles-module__withLeftIcon___NFn6- .styles-module__input___IZDc3.styles-module__small___cuGyE{padding-left:2rem}.styles-module__inputContainer___wXY53.styles-module__withRightIcon___mz-hn .styles-module__input___IZDc3.styles-module__small___cuGyE{padding-right:2rem}.styles-module__inputContainer___wXY53.styles-module__withLeftIcon___NFn6- .styles-module__input___IZDc3.styles-module__large___fMmiG{padding-left:3rem}.styles-module__inputContainer___wXY53.styles-module__withRightIcon___mz-hn .styles-module__input___IZDc3.styles-module__large___fMmiG{padding-right:3rem}.styles-module__leftIcon___jEW5H,.styles-module__rightIcon___bA3Eo{color:var(--color-fg-muted,var(--color-fg-text));pointer-events:none;z-index:1;justify-content:center;align-items:center;display:flex;position:absolute}.styles-module__leftIcon___jEW5H{left:.75rem}.styles-module__rightIcon___bA3Eo{right:.75rem}.styles-module__rightIcon___bA3Eo.styles-module__rightIconInteractive___ccypw{pointer-events:auto}.styles-module__inputContainer___wXY53.styles-module__containerSmall___20ICq .styles-module__leftIcon___jEW5H{left:.5rem}.styles-module__inputContainer___wXY53.styles-module__containerSmall___20ICq .styles-module__rightIcon___bA3Eo{right:.5rem}.styles-module__inputContainer___wXY53.styles-module__containerLarge___IztuN .styles-module__leftIcon___jEW5H{left:1rem}.styles-module__inputContainer___wXY53.styles-module__containerLarge___IztuN .styles-module__rightIcon___bA3Eo{right:1rem}.styles-module__input___IZDc3.styles-module__error___946qV{border-color:var(--color-danger)}.styles-module__input___IZDc3.styles-module__error___946qV:focus{border-color:var(--color-danger);box-shadow:0 0 0 3px rgba(var(--color-danger-rgb,244, 67, 54), .1)}.styles-module__input___IZDc3.styles-module__disabled___sJvLd{background:var(--color-bg-3);border-color:var(--color-border-5);color:var(--color-fg-muted);cursor:not-allowed;opacity:.6}.styles-module__errorMessage___5Mn22{color:var(--color-danger);margin:0;font-size:.75rem}.styles-module__helperText___T2eui{color:var(--color-fg-muted);margin:0;font-size:.75rem}.styles-module__container___xeg8O{flex-direction:column;gap:1rem;display:flex}.styles-module__label___qhbKe{color:var(--color-fg-text);font-size:.875rem;font-weight:500}.styles-module__error___O-7AY{color:var(--color-error);font-size:.875rem}.styles-module__pairs___owGV3{flex-direction:column;gap:.75rem;display:flex}.styles-module__pair___G4WFY{grid-template-columns:1fr 2fr auto;align-items:start;gap:.75rem;display:grid}.styles-module__keyInput___wl1M1,.styles-module__valueInput___HiwJ5{flex:1}.styles-module__removeButton___4-X0b{min-width:auto;color:var(--color-error);padding:.5rem}.styles-module__removeButton___4-X0b:hover{background-color:var(--color-error);color:#fff}.styles-module__addButton___QFljt{align-self:flex-start}@media (width<=768px){.styles-module__pair___G4WFY{grid-template-columns:1fr}.styles-module__removeButton___4-X0b{align-self:flex-end}}.styles-module__searchContainer___86TGN{width:100%;max-width:400px;position:relative}.styles-module__searchIcon___ZInry{color:var(--color-fg-muted);pointer-events:none;position:absolute;top:50%;left:1rem;transform:translateY(-50%)}.styles-module__searchInput___SqZXQ{border:1px solid var(--color-border);width:100%;color:var(--color-fg-text);background:var(--color-bg-2);border-radius:.5rem;padding:.75rem 3rem;font-size:1rem;transition:all .2s}.styles-module__searchInput___SqZXQ:focus{border-color:var(--color-primary);box-shadow:0 0 0 3px var(--color-primary-bg);outline:none}.styles-module__searchInput___SqZXQ::placeholder{color:var(--color-fg-muted)}.styles-module__clearBtn___7jExr{width:1.5rem;height:1.5rem;color:var(--color-fg-muted);cursor:pointer;background:0 0;border:none;justify-content:center;align-items:center;padding:0;transition:all .2s;display:flex;position:absolute;top:50%;right:.75rem;transform:translateY(-50%)}.styles-module__clearBtn___7jExr:hover{color:var(--color-fg-text)}@media (width<=768px){.styles-module__searchContainer___86TGN{max-width:100%}}.styles-module__container___nGxdM{flex-direction:column;gap:.75rem;display:flex}.styles-module__toggleContainer___VrsKI{align-items:center;display:flex}.styles-module__toggleButton___IzD6Z{border:1px solid var(--color-border-4);background:var(--color-bg);color:var(--color-fg-muted);cursor:pointer;border-radius:.5rem;align-items:center;gap:.5rem;padding:.5rem 1rem;font-size:.875rem;transition:all .2s;display:flex}.styles-module__toggleButton___IzD6Z:hover{background:var(--color-bg-2);border-color:var(--color-border-3)}.styles-module__toggleButton___IzD6Z.styles-module__enabled___cao0G{background:var(--color-primary);color:#fff;border-color:var(--color-primary)}.styles-module__toggleButton___IzD6Z.styles-module__enabled___cao0G:hover{background:var(--color-primary-hover);border-color:var(--color-primary-hover)}.styles-module__optionsContainer___LOzlO{background:var(--color-bg-2);border:1px solid var(--color-border-4);border-radius:.5rem;gap:.5rem;padding:.5rem;display:flex}.styles-module__option___abhnC{border:1px solid var(--color-border-4);background:var(--color-bg);color:var(--color-fg);cursor:pointer;border-radius:.375rem;flex:1;justify-content:center;align-items:center;gap:.375rem;padding:.5rem .875rem;font-size:.875rem;font-weight:500;transition:all .2s;display:flex}.styles-module__option___abhnC:hover{background:var(--color-bg-3);border-color:var(--color-border-3)}.styles-module__option___abhnC.styles-module__active___pqmSi{background:var(--color-primary);color:#fff;border-color:var(--color-primary)}.styles-module__option___abhnC.styles-module__active___pqmSi:hover{background:var(--color-primary-hover);border-color:var(--color-primary-hover)}.styles-module__nbSelect___KjxSc{font-family:inherit;font-size:.9375rem;font-weight:600;line-height:1.5rem;display:inline-block;position:relative}.styles-module__selectButton___AKZe4{cursor:pointer;text-align:left;width:100%;min-width:8rem;font-family:inherit;font-size:inherit;font-weight:inherit;line-height:inherit;appearance:none;border:1px solid;border-radius:.25rem;outline:none;justify-content:space-between;align-items:center;padding:.4375rem 2.2rem .4375rem 1.125rem;transition:all .15s ease-in-out;display:flex;position:relative}.styles-module__selectButton___AKZe4.styles-module__primary___HU36a{background-color:var(--color-primary);border-color:var(--color-primary);color:var(--color-primary-fg)}.styles-module__selectButton___AKZe4.styles-module__primary___HU36a:hover:not(:disabled){background-color:var(--color-primary-light);border-color:var(--color-primary-light)}.styles-module__selectButton___AKZe4.styles-module__primary___HU36a:focus,.styles-module__selectButton___AKZe4.styles-module__primary___HU36a.styles-module__open___9AKMb{background-color:var(--color-primary-light);border-color:var(--color-primary-light);box-shadow:0 0 0 .125rem #8f9bb340}.styles-module__selectButton___AKZe4.styles-module__default___LedYP{background-color:var(--color-bg);border-color:var(--color-border-4);color:var(--color-fg-text)}.styles-module__selectButton___AKZe4.styles-module__default___LedYP:hover:not(:disabled){background-color:var(--color-bg-2);border-color:var(--color-primary)}.styles-module__selectButton___AKZe4.styles-module__default___LedYP:focus,.styles-module__selectButton___AKZe4.styles-module__default___LedYP.styles-module__open___9AKMb{background-color:var(--color-bg-2);border-color:var(--color-primary);box-shadow:0 0 0 .125rem #8f9bb340}.styles-module__selectButton___AKZe4:disabled{cursor:not-allowed;opacity:.6}.styles-module__buttonText___QSLO1{text-overflow:ellipsis;white-space:nowrap;flex:1;overflow:hidden}.styles-module__chevronIcon___qAHaD{flex-shrink:0;width:1.5rem;height:1.5rem;transition:transform .15s;position:absolute;right:.41rem}.styles-module__selectButton___AKZe4.styles-module__primary___HU36a .styles-module__chevronIcon___qAHaD{color:var(--color-primary-fg)}.styles-module__selectButton___AKZe4.styles-module__default___LedYP .styles-module__chevronIcon___qAHaD{color:var(--color-fg)}.styles-module__chevronIcon___qAHaD.styles-module__open___9AKMb{transform:rotate(180deg)}.styles-module__optionsPanel___NWAp3{background-color:var(--color-bg);border:1px solid var(--color-border-4);z-index:1000;transform-origin:top;visibility:hidden;pointer-events:none;border-radius:.25rem;min-width:100%;position:absolute;top:calc(100% + .5rem);left:0;right:0;overflow:hidden;box-shadow:0 .5rem 1rem #00000026}.styles-module__optionsPanel___NWAp3.styles-module__open___9AKMb{visibility:visible;pointer-events:auto;animation:.25s ease-out styles-module__expandDown___eD4lh}.styles-module__optionsPanel___NWAp3.styles-module__closed___bez-q{visibility:hidden;pointer-events:none;animation:.25s ease-out styles-module__collapseUp___dt1E7}@keyframes styles-module__expandDown___eD4lh{0%{opacity:0;transform:scaleY(0)}to{opacity:1;transform:scaleY(1)}}@keyframes styles-module__collapseUp___dt1E7{0%{opacity:1;transform:scaleY(1)}to{opacity:0;transform:scaleY(0)}}.styles-module__optionsPanel___NWAp3.styles-module__primary___HU36a{border-color:var(--color-primary)}.styles-module__optionsPanel___NWAp3.styles-module__default___LedYP{border-color:var(--color-border-4)}.styles-module__optionsList___AxhVp{margin:0;padding:.5rem 0;list-style:none}.styles-module__option___t1SSw{cursor:pointer;color:var(--color-fg-text);justify-content:space-between;align-items:center;padding:.75rem 1.125rem;transition:all .15s;display:flex}.styles-module__option___t1SSw:hover{background-color:var(--color-bg-2)}.styles-module__option___t1SSw.styles-module__selected___azD4A{background-color:var(--color-primary);color:var(--color-primary-fg);font-weight:700}@media (prefers-color-scheme:dark){.styles-module__optionsPanel___NWAp3{box-shadow:0 .5rem 1rem #0000004d}}.styles-module__sliderContainer___lgpaD{flex-direction:column;gap:.5rem;display:flex}.styles-module__sliderContainer___lgpaD.styles-module__fullWidth___ZS8W5{width:100%}.styles-module__labelRow___dkVVM{justify-content:space-between;align-items:center;min-height:1.5rem;margin-bottom:.5rem;display:flex}.styles-module__label___bXGZs{color:var(--color-fg-text);font-size:.875rem;font-weight:500}.styles-module__required___FuyfE{color:var(--color-danger);margin-left:.25rem}.styles-module__sliderWrapper___1Wdti{touch-action:none;-webkit-user-select:none;user-select:none;justify-content:center;align-items:center;gap:.75rem;width:100%;display:flex}.styles-module__sliderRoot___yzOjR{cursor:grab;touch-action:none;-webkit-user-select:none;user-select:none;flex-grow:1;align-items:center;width:100%;padding:.75rem 0;display:flex;position:relative}.styles-module__sliderRoot___yzOjR:active{cursor:grabbing}.styles-module__sliderTrackWrapper___N72WQ{flex-grow:1;display:flex;position:relative}.styles-module__sliderTrack___AltcD{background-color:var(--color-bg-3);border-radius:9999px;width:100%;height:.375rem;position:relative;overflow:hidden}.styles-module__sliderRange___U3g0-{background-color:var(--color-primary);border-radius:9999px;height:100%;position:absolute}.styles-module__valueIndicator___aLf0I{color:var(--color-primary);text-align:center;flex-shrink:0;min-width:2rem;font-size:.875rem;font-weight:600;line-height:1}.styles-module__iconWrapper___wL-b8{flex-shrink:0;justify-content:center;align-items:center;display:flex}.styles-module__icon___vHsID{width:20px;height:20px;color:var(--color-fg-muted)}.styles-module__errorMessage___qFgGe{color:var(--color-danger);margin:0;font-size:.75rem}.styles-module__helperText___tgBGl{color:var(--color-fg-muted);margin:0;font-size:.75rem}.styles-module__loadingContainer___OMgwS{flex-direction:column;justify-content:center;align-items:center;gap:1.5rem;min-height:60vh;display:flex}.styles-module__loadingText___KRZqR{color:var(--color-fg-muted);font-size:1rem}.styles-module__errorContainer___qhAg2{text-align:center;min-height:60vh;color:var(--color-fg-text);flex-direction:column;justify-content:center;align-items:center;gap:2rem;padding:3rem 2rem;display:flex}.styles-module__errorIconWrapper___IHZrN{background:linear-gradient(135deg, rgba(var(--color-error-rgb,239, 68, 68), .1), rgba(var(--color-error-rgb,239, 68, 68), .05));border:2px solid rgba(var(--color-error-rgb,239, 68, 68), .2);border-radius:50%;justify-content:center;align-items:center;width:120px;height:120px;animation:2s ease-in-out infinite styles-module__pulse___Lju-z;display:flex}@keyframes styles-module__pulse___Lju-z{0%,to{opacity:1;transform:scale(1)}50%{opacity:.9;transform:scale(1.05)}}.styles-module__errorIcon___oGDIP{color:var(--color-error,#ef4444);stroke-width:1.5px}.styles-module__errorContent___F55rQ{flex-direction:column;align-items:center;gap:1rem;width:100%;max-width:600px;display:flex}.styles-module__errorTitle___hC8D-{color:var(--color-fg-heading);letter-spacing:-.02em;margin:0;font-size:1.75rem;font-weight:700;line-height:1.2}.styles-module__errorDescription___hZtuP{color:var(--color-fg-muted);max-width:480px;margin:0;font-size:1rem;line-height:1.6}.styles-module__errorDetailsWrapper___-sp8b{width:100%;max-width:600px;margin-top:.5rem}.styles-module__errorDetailsContainer___eywMo{text-align:left;border:1px solid var(--color-border,#0000001a);background:var(--color-bg-2,#00000005);border-radius:.75rem;width:100%;transition:all .2s;overflow:hidden}.styles-module__errorDetailsContainer___eywMo:hover{border-color:var(--color-border-hover,#00000026);background:var(--color-bg-3,#00000008)}.styles-module__errorDetailsSummary___hGEoD{cursor:pointer;color:var(--color-fg-muted);-webkit-user-select:none;user-select:none;padding:.875rem 1.25rem;font-size:.875rem;font-weight:500;list-style:none;transition:color .2s}.styles-module__errorDetailsSummary___hGEoD:hover{color:var(--color-fg-text)}.styles-module__errorDetailsSummary___hGEoD::-webkit-details-marker{display:none}.styles-module__errorDetailsSummary___hGEoD:before{content:"▶";margin-right:.5rem;font-size:.75rem;transition:transform .2s;display:inline-block}.styles-module__errorDetailsContainer___eywMo[open] .styles-module__errorDetailsSummary___hGEoD:before{transform:rotate(90deg)}.styles-module__errorDetails___tzCT-{background:var(--color-bg-3,#00000008);border-top:1px solid var(--color-border,#0000001a);max-width:100%;color:var(--color-fg-muted);white-space:pre-wrap;word-break:break-word;margin:0;padding:1rem 1.25rem;font-family:Monaco,Menlo,Ubuntu Mono,Consolas,source-code-pro,monospace;font-size:.8125rem;line-height:1.6;overflow-x:auto}.styles-module__errorActions___gtqYw{flex-wrap:wrap;justify-content:center;align-items:center;gap:.75rem;margin-top:1rem;display:flex}.styles-module__retryButton___TA4jm{background:var(--color-primary);color:var(--color-primary-fg);cursor:pointer;border:none;border-radius:.5rem;justify-content:center;align-items:center;gap:.5rem;padding:.75rem 2rem;font-size:.9375rem;font-weight:500;transition:all .2s;display:inline-flex;box-shadow:0 2px 8px #00000014}.styles-module__retryButton___TA4jm:hover{background:var(--color-primary-hover,var(--color-primary));transform:translateY(-2px);box-shadow:0 4px 16px #0000001f}.styles-module__retryButton___TA4jm:active{transform:translateY(0);box-shadow:0 2px 8px #00000014}.styles-module__retryButtonIcon___vzfp-{font-size:1.125rem;line-height:1;transition:transform .3s;display:inline-block}.styles-module__retryButton___TA4jm:hover .styles-module__retryButtonIcon___vzfp-{transform:rotate(180deg)}.styles-module__clearLocalDataButton___T3B-K{color:var(--color-fg-muted);border:1px solid var(--color-border-4);cursor:pointer;background:0 0;border-radius:.5rem;justify-content:center;align-items:center;padding:.75rem 1.5rem;font-size:.9375rem;font-weight:500;transition:all .2s;display:inline-flex}.styles-module__clearLocalDataButton___T3B-K:hover{color:var(--color-fg-text);border-color:var(--color-border-3);background:var(--color-bg-secondary)}.styles-module__wrapper___jXPV3{flex-direction:column;gap:.5rem;display:flex}.styles-module__wrapper___jXPV3.styles-module__fullWidth___eKyGB{width:100%}.styles-module__label___MyTeo{color:var(--color-fg-heading);align-items:center;gap:.25rem;font-size:.875rem;font-weight:500;display:flex}.styles-module__required___TV-m8{color:var(--color-danger)}.styles-module__inputContainer___VvGBl{align-items:flex-start;width:100%;display:flex;position:relative}.styles-module__textarea___AjX9N{border:1px solid var(--color-border-4);background:var(--color-bg);width:100%;color:var(--color-fg-text);resize:vertical;border-radius:.5rem;outline:none;min-height:4rem;font-family:inherit;font-size:.875rem;transition:all .2s}.styles-module__textarea___AjX9N:focus{border-color:var(--color-primary);box-shadow:0 0 0 3px rgba(var(--color-primary-rgb,102, 126, 234), .1)}.styles-module__textarea___AjX9N::placeholder{color:var(--color-fg-muted,var(--color-fg-text));opacity:.5}.styles-module__textarea___AjX9N.styles-module__small___zMx0G{min-height:3rem;padding:.5rem .75rem;font-size:.8125rem}.styles-module__textarea___AjX9N.styles-module__medium___tO2Lb{min-height:4rem;padding:.75rem;font-size:.875rem}.styles-module__textarea___AjX9N.styles-module__large___3eU2g{min-height:5rem;padding:1rem;font-size:1rem}.styles-module__textarea___AjX9N.styles-module__default___vsSxX{background:var(--color-bg);border-color:var(--color-border-4)}.styles-module__textarea___AjX9N.styles-module__filled___TPQgI{background:var(--color-bg-2);border-color:var(--color-border-3)}.styles-module__inputContainer___VvGBl .styles-module__leftIcon___FHp2R{color:var(--color-fg-muted,var(--color-fg-text));pointer-events:none;z-index:1;justify-content:center;align-items:center;display:flex;position:absolute;top:.75rem;left:.75rem}.styles-module__inputContainer___VvGBl .styles-module__rightIcon___dkyvP{color:var(--color-fg-muted,var(--color-fg-text));pointer-events:none;z-index:1;justify-content:center;align-items:center;display:flex;position:absolute;top:.75rem;right:.75rem}.styles-module__inputContainer___VvGBl .styles-module__textarea___AjX9N.styles-module__small___zMx0G+.styles-module__leftIcon___FHp2R,.styles-module__inputContainer___VvGBl .styles-module__textarea___AjX9N.styles-module__small___zMx0G+.styles-module__rightIcon___dkyvP{top:.5rem}.styles-module__inputContainer___VvGBl .styles-module__textarea___AjX9N.styles-module__large___3eU2g+.styles-module__leftIcon___FHp2R,.styles-module__inputContainer___VvGBl .styles-module__textarea___AjX9N.styles-module__large___3eU2g+.styles-module__rightIcon___dkyvP{top:1rem}.styles-module__textarea___AjX9N.styles-module__error___d8gLK{border-color:var(--color-danger)}.styles-module__textarea___AjX9N.styles-module__error___d8gLK:focus{border-color:var(--color-danger);box-shadow:0 0 0 3px rgba(var(--color-danger-rgb,244, 67, 54), .1)}.styles-module__textarea___AjX9N.styles-module__disabled___IzbfE{background:var(--color-bg-3);border-color:var(--color-border-5);color:var(--color-fg-muted);cursor:not-allowed;opacity:.6}.styles-module__errorText___Nom7x{color:var(--color-danger);margin:0;font-size:.75rem}.styles-module__helperText___97Def{color:var(--color-fg-muted);margin:0;font-size:.75rem}.styles-module__virtualList___pM6rN{contain:strict;width:100%;padding-bottom:300px;position:relative;overflow:auto}.styles-module__emptyContainer___rm938{height:100%;color:var(--color-fg-muted);justify-content:center;align-items:center;display:flex}.styles-module__virtualListInner___HxRzc{width:100%;position:relative}.styles-module__virtualListItems___p19-a{width:100%;position:absolute;top:0;left:0}.styles-module__loadingMore___zAdLM{color:var(--color-fg-muted);justify-content:center;align-items:center;gap:.75rem;padding:2rem 0;font-size:.875rem;display:flex}.styles-module__endOfList___5cYFh{color:var(--color-fg-muted);border-top:1px solid var(--color-border-3);justify-content:center;margin-top:1rem;padding:2rem 0;font-size:.875rem;display:flex}.styles-module__endOfList___5cYFh span{background-color:var(--color-bg-2);border-radius:.25rem;padding:.5rem 1rem}.styles-module__virtualList___xmj2v{width:100%;padding-bottom:300px;position:relative;overflow:visible}.styles-module__virtualListInner___quD74{width:100%;position:relative}.styles-module__virtualListItems___-Muml{width:100%;position:absolute;top:0;left:0}.styles-module__virtualListRow___h3pzX{width:100%}.styles-module__emptyContainer___LIy8r{color:var(--color-fg-muted);justify-content:center;align-items:center;padding:2rem 0;display:flex}.styles-module__loadingMore___Mzf-p{color:var(--color-fg-muted);justify-content:center;align-items:center;padding:1.5rem 0;display:flex}.styles-module__endOfList___g5G--{color:var(--color-fg-muted);border-top:1px solid var(--color-border-3);justify-content:center;padding:1.5rem 0;display:flex}.styles-module__sidebar___BMt0j{flex-direction:column;height:100%;min-height:0;display:flex;overflow:hidden}.styles-module__sidebarContent___iAwiw{flex-direction:column;height:100%;min-height:0;padding-top:1rem;display:flex;overflow:hidden}.styles-module__menuWrapper___DKuEv{flex:1;min-height:0;overflow:hidden auto}.styles-module__sidebarContent___iAwiw::-webkit-scrollbar{width:0}.styles-module__sidebarContent___iAwiw::-webkit-scrollbar-track{background:0 0}.styles-module__sidebarContent___iAwiw::-webkit-scrollbar-thumb{background:0 0}.styles-module__sidebarContent___iAwiw::-webkit-scrollbar-thumb:hover{background:0 0}.styles-module__sidebar___BMt0j a{margin-left:0}.styles-module__sidebar___BMt0j .ps-sidebar-root{background-color:var(--color-bg-2)!important;border-right:1px solid var(--color-separator)!important;flex-direction:column!important;height:100%!important;min-height:0!important;display:flex!important;overflow:hidden!important}.styles-module__sidebar___BMt0j .ps-sidebar-container{background-color:var(--color-bg-2)!important;height:100%!important;min-height:0!important;color:var(--color-fg-text)!important;flex-direction:column!important;flex:1!important;display:flex!important;overflow:hidden!important}.styles-module__sidebar___BMt0j .ps-menu-button{color:var(--color-fg-text)!important;border-radius:.375rem!important;margin:.125rem .5rem!important;padding:.5rem .75rem!important;font-size:.875rem!important;transition:all .2s!important}.styles-module__sidebar___BMt0j .ps-menu-button:hover{background-color:var(--color-bg-3)!important;color:var(--color-fg-text)!important}.styles-module__sidebar___BMt0j .ps-menu-button.ps-active{background-color:var(--color-primary)!important;color:#fff!important}.styles-module__sidebar___BMt0j .ps-menu-button.ps-active *,.styles-module__sidebar___BMt0j .ps-menu-button.ps-active .ps-menu-icon,.styles-module__sidebar___BMt0j .ps-menu-button.ps-active .ps-menu-label{color:#fff!important}.styles-module__sidebar___BMt0j .ps-menu-icon{width:1.25rem!important;height:1.25rem!important;color:var(--color-fg-text)!important;margin-right:.5rem!important}.styles-module__sidebar___BMt0j .ps-submenu-content{background-color:var(--color-bg-3)!important;padding-left:.5rem!important;overflow:hidden!important}.styles-module__sidebar___BMt0j .ps-submenu-content .ps-menu-button{margin:.125rem .25rem!important;padding:.375rem .5rem!important;font-size:.8125rem!important;transition:all .2s!important}.styles-module__sidebar___BMt0j .ps-menu-label{color:var(--color-fg-text)!important;font-weight:500!important}.styles-module__sidebar___BMt0j .ps-menu-button.ps-active,.styles-module__sidebar___BMt0j .ps-menu-button.ps-active span,.styles-module__sidebar___BMt0j .ps-menu-button.ps-active div,.styles-module__sidebar___BMt0j .ps-menu-button.ps-active .ps-menu-icon,.styles-module__sidebar___BMt0j .ps-menu-button.ps-active .ps-menu-label{color:#fff!important}.styles-module__logoutContainer___7NwBL{border-top:1px solid var(--color-separator);writing-mode:horizontal-tb;text-orientation:mixed;flex-shrink:0;margin-top:auto;padding:.5rem}.styles-module__logoutButton___rfD6u{width:100%;color:var(--color-fg-text);cursor:pointer;white-space:nowrap;writing-mode:horizontal-tb;text-orientation:mixed;direction:ltr;background:0 0;border:none;border-radius:.375rem;justify-content:flex-start;align-items:center;gap:.5rem;padding:.5rem .75rem;font-size:.875rem;transition:background-color .2s,color .2s;display:flex;overflow:hidden}.styles-module__logoutButton___rfD6u span{opacity:1;max-width:200px;transition:opacity .3s,max-width .3s;display:inline-block;writing-mode:horizontal-tb!important;text-orientation:mixed!important;direction:ltr!important;transform:none!important}.styles-module__logoutButton___rfD6u .styles-module__hiddenText___z183T{opacity:0;width:0;max-width:0;overflow:hidden}.styles-module__logoutButton___rfD6u .styles-module__visibleText___lfsv-{opacity:1;width:auto;max-width:200px}.styles-module__logoutButton___rfD6u:hover{background-color:var(--color-bg-3)}.styles-module__logoutButton___rfD6u:active{transform:scale(.98)}.styles-module__logoutButton___rfD6u svg{color:inherit;flex-shrink:0}.styles-module__footer___g-hY7{background:var(--color-bg-2);width:100%;padding:0 2rem}.styles-module__footerContent___Oen5n{justify-content:space-between;align-items:center;max-width:100%;margin:0 auto;display:flex}.styles-module__copyright___1R2o3{color:var(--color-fg-text);font-size:.875rem}.styles-module__links___dQyvB{gap:1.5rem;display:flex}.styles-module__link___UWfyK{color:var(--color-fg-text);font-size:.875rem;text-decoration:none;transition:color .3s}.styles-module__link___UWfyK:hover{color:var(--color-primary)}@media (width<=768px){.styles-module__footerContent___Oen5n{flex-direction:column;gap:1rem}.styles-module__links___dQyvB{gap:1rem}}.styles-module__header___4Uc8h{width:100%;padding:.5rem 2rem;display:flex}.styles-module__header___4Uc8h>:first-child{flex:1;justify-content:flex-start;align-items:center;display:flex}.styles-module__header___4Uc8h>:last-child{flex:1;justify-content:flex-end;align-items:center;display:flex}.styles-module__layout___ZQKU7{background:var(--color-bg);flex-direction:column;width:100%;height:100vh;display:flex;position:relative}.styles-module__header___XIaFb{z-index:100;background:var(--color-bg);width:100%;position:fixed;top:0;box-shadow:0 .125rem .25rem #2c33491a}.styles-module__footer___r8ASO{z-index:100;width:100%;position:fixed;bottom:0}.styles-module__mainWrapper___i4tEI{flex:1;display:flex;position:relative}.styles-module__sidebar___ij-8-{position:fixed;top:0}.styles-module__content___mTgUr{background:var(--color-bg);flex:1;min-height:calc(100vh - 10rem);margin:0;padding-top:0;padding-bottom:0;overflow:hidden auto}@media (width<=768px){.styles-module__content___mTgUr{width:100%}.styles-module__sidebar___ij-8-{z-index:999;height:100vh;position:fixed;top:0;left:0}}.styles-module__mainWrapper___AiTAw{flex-direction:row;flex:1;display:flex;position:relative}.styles-module__sidebarContainer___X4yj8{z-index:99;flex-direction:column;display:flex;position:fixed;left:0;overflow:hidden}.styles-module__sidebar___VQTlH{flex-shrink:0;top:0;left:0}.styles-module__content___htJlH{background:var(--color-bg);flex:1;margin:0;padding-top:0;padding-bottom:0;overflow:hidden auto}@media (width<=1024px){.styles-module__content___htJlH{padding:1.5rem}}@media (width<=768px){.styles-module__content___htJlH{width:100%}.styles-module__sidebar___VQTlH{z-index:999;height:100vh;position:fixed;top:0;left:0}}.styles-module__wavesWrapper___TK7DI{pointer-events:none;z-index:0;width:100vw;height:100vh;position:fixed;inset:0;overflow:hidden}.styles-module__wavesWrapper___TK7DI>*{pointer-events:auto}.styles-module__squaresWrapper___P25uF{pointer-events:none;z-index:0;width:100vw;height:100vh;position:fixed;inset:0;overflow:hidden}.styles-module__squaresWrapper___P25uF>*{pointer-events:auto}.styles-module__letterGlitchWrapper___KWtWU{pointer-events:none;z-index:0;width:100vw;height:100vh;position:fixed;inset:0;overflow:hidden}.styles-module__letterGlitchWrapper___KWtWU>*{pointer-events:auto}.styles-module__pixelBlastWrapper___YptHh{pointer-events:none;z-index:0;width:100vw;height:100vh;position:fixed;inset:0;overflow:hidden}.styles-module__pixelBlastWrapper___YptHh>*{pointer-events:auto}
2
2
  /*$vite$:1*/`)),document.head.appendChild(e)}}catch(o){console.error("vite-plugin-css-injected-by-js",o)}})();
3
- Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const A=require("./chunk-chunk-BFrxaqQT.cjs"),m=require("./chunk-lstorage-BnxLXHgH.cjs");require("./chunk-types-BE3JCLff.cjs");const D=require("./chunk-i18n-daHSL0Nm.cjs");let M=require("google-libphonenumber"),d=require("async-retry");d=A.__toESM(d);function N(t){return t.trim().replace(/\s+/g," ")}function h(t){const e=t?.response?.data;return!e||typeof e!="object"?null:{status:e.status,errCode:e.errCode,message:e.message,details:e.details,traceId:e.traceId}}function T(t,e){const r=h(t);if(r?.errCode){const o=D.i18n_default.t(`errors:${r.errCode}`);if(o&&o!==`errors:${r.errCode}`)return o}return r?.message?r.message:e}function R(t,e,r,o,n){if(e.length===0)return t;switch(n){case"insert":return C(t,e,r,o);case"upsert":return E(t,e,r,o)}}function C(t,e,r,o){const n=new Set(t.map(r)),s=e.filter(i=>{const a=r(i);return n.has(a)?!1:(n.add(a),!0)});return s.length===0?t:o==="prepend"?[...s,...t]:[...t,...s]}function E(t,e,r,o){const n=new Map;t.forEach((u,c)=>n.set(r(u),c));let s=!1;const i=t.slice();for(const u of e){const c=r(u),f=n.get(c);f!==void 0&&i[f]!==u&&(i[f]=u,s=!0)}const a=[];for(const u of e){const c=r(u);n.has(c)||a.push(u)}return a.length>0?(s=!0,o==="prepend"?[...a,...i]:[...i,...a]):s?i:t}function P(t,e,r){let o=!1;const n=t.filter(s=>{const i=!e.has(r(s));return i||(o=!0),i});return o?n:t}const k=(t,e)=>{const r=t.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*[\d.]+)?\)/);if(r){const[,o,n,s]=r;return`rgba(${o}, ${n}, ${s}, ${e})`}return $(t,e)},q=(t,e)=>{if(t.startsWith("rgba"))return t;const r=t.match(/rgb\((\d+),\s*(\d+),\s*(\d+)\)/);if(r){const[,o,n,s]=r;return`rgba(${o}, ${n}, ${s}, ${e})`}return console.warn("Invalid RGB color format:",t),t},$=(t,e)=>{if(t.indexOf("rgb")!==-1)return t;let r=0,o=0,n=0;return t.startsWith("#")&&(t=t.slice(1)),t.length===3?(r=parseInt(t[0]+t[0],16),o=parseInt(t[1]+t[1],16),n=parseInt(t[2]+t[2],16)):t.length===6?(r=parseInt(t.slice(0,2),16),o=parseInt(t.slice(2,4),16),n=parseInt(t.slice(4,6),16)):console.warn("Unsupported HEX color format"),`rgba(${r}, ${o}, ${n}, ${e})`},B=(t,e,r)=>{const o=f=>{const l=f.match(/^#?([\da-f]{2})([\da-f]{2})([\da-f]{2})$/i);if(l)return[parseInt(l[1],16),parseInt(l[2],16),parseInt(l[3],16)];const g=f.match(/^rgb\((\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3})\)$/i);return g?[parseInt(g[1]),parseInt(g[2]),parseInt(g[3])]:[0,0,0]},[n,s,i]=o(t),[a,u,c]=o(e);return`rgb(${Math.round(n+(a-n)*r)}, ${Math.round(s+(u-s)*r)}, ${Math.round(i+(c-i)*r)})`},_=t=>/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(t);function j(t){return typeof t=="string"?t:t==null?"":typeof t=="number"||typeof t=="boolean"?String(t):Array.isArray(t)?t.map(e=>String(e)).join(", "):typeof t=="object"?JSON.stringify(t):String(t)}function V(t){return t==null?"":typeof t=="number"?Number.isFinite(t)?String(t):"":typeof t=="string"?t:typeof t=="boolean"?t?"1":"0":Array.isArray(t)&&t.length>0?String(t[0]):""}function F(t,e,...r){const o={},n=Array.isArray(e)?e:[e,...r],s=new Set(n);for(const i of Object.keys(t))s.has(i)||(o[i]=t[i]);return o}var p=M.PhoneNumberUtil.getInstance();const U=t=>p.parseAndKeepRawInput(t).getCountryCode(),H=t=>{if(!t||!t.replace(/\D/g,"").length)return!1;try{const e=p.parseAndKeepRawInput(t);return p.isValidNumber(e)}catch{return!1}},b=t=>[t,null],I=t=>[null,t instanceof Error?t:new Error(String(t))];async function w(t,e){try{return b(await(0,d.default)(async(r,o)=>{try{return await t(r,o)}catch(n){throw e?.isNonRetryable?.(n)&&r(n),n}},e))}catch(r){return I(r)}}async function K(t,e,r){return w(async(o,n)=>{const s=await t();if(!e(s))throw new Error("Condition not satisfied");return s},r)}function O(t){return Math.round(Number(t)*100)}function S(t){return(Number(t)/100).toFixed(2)}function W(t){return Number(S(t))}function Y(t){return t<1e3?t.toString():t<1e6?`${(t/1e3).toFixed(1).replace(/\.0$/,"")}k+`:t<1e9?`${(t/1e6).toFixed(1).replace(/\.0$/,"")}M+`:`${(t/1e9).toFixed(1).replace(/\.0$/,"")}B+`}function G(t){let e=null;return async(...r)=>e||(e=t(...r).finally(()=>e=null),e)}function z(t,e){const r=new Map;return async(...o)=>{const n=e(...o),s=r.get(n);if(s)return s;const i=t(...o).finally(()=>r.delete(n));return r.set(n,i),i}}function y(t,e){if(Object.is(t,e))return!0;if(typeof t!=typeof e)return!1;if(t===null||e===null||typeof t!="object"||typeof e!="object")return t===e;const r=Array.isArray(t),o=Array.isArray(e);if(r!==o)return!1;if(r&&o)return t.length!==e.length?!1:t.every((i,a)=>y(i,e[a]));const n=Object.keys(t).sort(),s=Object.keys(e).sort();return n.length!==s.length||!n.every((i,a)=>i===s[a])?!1:n.every(i=>y(t[i],e[i]))}const J=t=>{let e,r=[];return new Proxy(t,{construct(o,n){if(e||(e=new t(...n),r=n),!y(n,r))throw new Error("Cannot create multiple instances with different parameters");return e}})};function x(t,e=100){if(t==null)throw new Promise(r=>setTimeout(r,e));return t}const L=t=>{if(!t)return"";const e=new Date(t);return isNaN(e.getTime())?"":`${e.getFullYear()}/${(e.getMonth()+1).toString().padStart(2,"0")}/${e.getDate().toString().padStart(2,"0")}`},X=t=>{if(!t)return"Unknown";const e=new Date(t);if(isNaN(e.getTime()))return"Invalid date";const r=new Date().getTime()-e.getTime(),o=Math.floor(r/1e3),n=Math.floor(o/60),s=Math.floor(n/60),i=Math.floor(s/24),a=Math.floor(i/30),u=Math.floor(i/365);return u>0?`${u} year${u>1?"s":""} ago`:a>0?`${a} month${a>1?"s":""} ago`:i>0?`${i} day${i>1?"s":""} ago`:s>0?`${s} hour${s>1?"s":""} ago`:n>0?`${n} minute${n>1?"s":""} ago`:"Just now"};function Q(t){try{return new Date(t).toLocaleDateString(void 0,{month:"short",day:"numeric",hour:"2-digit",minute:"2-digit"})}catch{return t}}const Z=t=>{try{const e=new Date(t);return isNaN(e.getTime())?t:`${e.getFullYear()}/${String(e.getMonth()+1).padStart(2,"0")}/${String(e.getDate()).padStart(2,"0")} ${String(e.getHours()).padStart(2,"0")}:${String(e.getMinutes()).padStart(2,"0")}`}catch{return t}},v=t=>{try{const e=new Date(t);return isNaN(e.getTime())?t:`${String(e.getMonth()+1).padStart(2,"0")}/${String(e.getDate()).padStart(2,"0")} ${String(e.getHours()).padStart(2,"0")}:${String(e.getMinutes()).padStart(2,"0")}`}catch{return t}},tt=t=>{try{const e=new Date(t);return isNaN(e.getTime())?t:`${e.getFullYear()}/${String(e.getMonth()+1).padStart(2,"0")}/${String(e.getDate()).padStart(2,"0")}`}catch{return t}},et=t=>{try{const e=new Date(t);return isNaN(e.getTime())?t:`${String(e.getHours()).padStart(2,"0")}:${String(e.getMinutes()).padStart(2,"0")}`}catch{return t}};function rt(t,e){return t.reduce((r,o,n)=>(r[o]=e?e(o,n):o,r),{})}exports.createMap=rt;exports.err=I;exports.formatDate=tt;exports.formatDateTime=Z;exports.formatDisplayDate=L;exports.formatMonthDayTime=v;exports.formatNumberAbbreviated=Y;exports.formatRelativeTime=X;exports.formatTickDate=Q;exports.formatTime=et;exports.getApiError=h;exports.getApiErrorMessage=T;exports.getCountryCode=U;exports.getItem=m.getItem;exports.hexToRGBA=$;exports.interpolateColor=B;exports.isValidEmail=_;exports.isValidPhoneNumber=H;exports.mergeById=R;exports.normalizeAddress=N;exports.ok=b;exports.omit=F;exports.onceAsync=G;exports.onceAsyncByKey=z;exports.pollUntil=K;exports.pruneArray=P;exports.removeItem=m.removeItem;exports.rgbToRgba=q;exports.setItem=m.setItem;exports.singleton=J;exports.suspenseIfNull=x;exports.toDatabasePrice=O;exports.toDisplayPrice=S;exports.toDisplayPriceNumber=W;exports.toNumberInputValue=V;exports.toRgbaWithAlpha=k;exports.toTextInputValue=j;exports.withRetryResult=w;
3
+ Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const w=require("./chunk-chunk-BFrxaqQT.cjs"),m=require("./chunk-lstorage-BnxLXHgH.cjs");require("./chunk-types-BE3JCLff.cjs");const A=require("./chunk-i18n-daHSL0Nm.cjs");let D=require("google-libphonenumber"),d=require("async-retry");d=w.__toESM(d);function M(t){return t.trim().replace(/\s+/g," ")}function h(t){const e=t?.response?.data;return!e||typeof e!="object"?null:{status:e.status,errCode:e.errCode,message:e.message,details:e.details,traceId:e.traceId}}function T(t,e){const r=h(t);if(r?.errCode){const o=A.i18n_default.t(`errors:${r.errCode}`);if(o&&o!==`errors:${r.errCode}`)return o}return r?.message?r.message:e}function R(t,e,r,o,n){if(e.length===0)return t;switch(n){case"insert":return C(t,e,r,o);case"upsert":return E(t,e,r,o)}}function C(t,e,r,o){const n=new Set(t.map(r)),s=e.filter(i=>{const a=r(i);return n.has(a)?!1:(n.add(a),!0)});return s.length===0?t:o==="prepend"?[...s,...t]:[...t,...s]}function E(t,e,r,o){const n=new Map;t.forEach((u,f)=>n.set(r(u),f));let s=!1;const i=t.slice();for(const u of e){const f=r(u),c=n.get(f);c!==void 0&&i[c]!==u&&(i[c]=u,s=!0)}const a=[];for(const u of e){const f=r(u);n.has(f)||a.push(u)}return a.length>0?(s=!0,o==="prepend"?[...a,...i]:[...i,...a]):s?i:t}function P(t,e,r){let o=!1;const n=t.filter(s=>{const i=!e.has(r(s));return i||(o=!0),i});return o?n:t}const k=(t,e)=>{const r=t.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*[\d.]+)?\)/);if(r){const[,o,n,s]=r;return`rgba(${o}, ${n}, ${s}, ${e})`}return b(t,e)},q=(t,e)=>{if(t.startsWith("rgba"))return t;const r=t.match(/rgb\((\d+),\s*(\d+),\s*(\d+)\)/);if(r){const[,o,n,s]=r;return`rgba(${o}, ${n}, ${s}, ${e})`}return console.warn("Invalid RGB color format:",t),t},b=(t,e)=>{if(t.indexOf("rgb")!==-1)return t;let r=0,o=0,n=0;return t.startsWith("#")&&(t=t.slice(1)),t.length===3?(r=parseInt(t[0]+t[0],16),o=parseInt(t[1]+t[1],16),n=parseInt(t[2]+t[2],16)):t.length===6?(r=parseInt(t.slice(0,2),16),o=parseInt(t.slice(2,4),16),n=parseInt(t.slice(4,6),16)):console.warn("Unsupported HEX color format"),`rgba(${r}, ${o}, ${n}, ${e})`},B=(t,e,r)=>{const o=c=>{const l=c.match(/^#?([\da-f]{2})([\da-f]{2})([\da-f]{2})$/i);if(l)return[parseInt(l[1],16),parseInt(l[2],16),parseInt(l[3],16)];const g=c.match(/^rgb\((\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3})\)$/i);return g?[parseInt(g[1]),parseInt(g[2]),parseInt(g[3])]:[0,0,0]},[n,s,i]=o(t),[a,u,f]=o(e);return`rgb(${Math.round(n+(a-n)*r)}, ${Math.round(s+(u-s)*r)}, ${Math.round(i+(f-i)*r)})`},_=t=>/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(t);function j(t){return typeof t=="string"?t:t==null?"":typeof t=="number"||typeof t=="boolean"?String(t):Array.isArray(t)?t.map(e=>String(e)).join(", "):typeof t=="object"?JSON.stringify(t):String(t)}function V(t){return t==null?"":typeof t=="number"?Number.isFinite(t)?String(t):"":typeof t=="string"?t:typeof t=="boolean"?t?"1":"0":Array.isArray(t)&&t.length>0?String(t[0]):""}function F(t,e,...r){const o={},n=Array.isArray(e)?e:[e,...r],s=new Set(n);for(const i of Object.keys(t))s.has(i)||(o[i]=t[i]);return o}var p=D.PhoneNumberUtil.getInstance();const U=t=>p.parseAndKeepRawInput(t).getCountryCode(),O=t=>{if(!t||!t.replace(/\D/g,"").length)return!1;try{const e=p.parseAndKeepRawInput(t);return p.isValidNumber(e)}catch{return!1}},$=t=>[t,null],N=t=>[null,t instanceof Error?t:new Error(String(t))];async function I(t,e){try{return $(await(0,d.default)(async(r,o)=>{try{return await t(r,o)}catch(n){throw e?.isNonRetryable?.(n)&&r(n),n}},e))}catch(r){return N(r)}}async function H(t,e,r){return I(async(o,n)=>{const s=await t();if(!e(s))throw new Error("Condition not satisfied");return s},r)}function K(t){return Math.round(Number(t)*100)}function S(t){return(Number(t)/100).toFixed(2)}function W(t){return Number(S(t))}function Y(t){return t<1e3?t.toString():t<1e6?`${(t/1e3).toFixed(1).replace(/\.0$/,"")}k+`:t<1e9?`${(t/1e6).toFixed(1).replace(/\.0$/,"")}M+`:`${(t/1e9).toFixed(1).replace(/\.0$/,"")}B+`}function G(t){let e=null;return async(...r)=>e||(e=t(...r).finally(()=>e=null),e)}function z(t,e){const r=new Map;return async(...o)=>{const n=e(...o),s=r.get(n);if(s)return s;const i=t(...o).finally(()=>r.delete(n));return r.set(n,i),i}}function J(t){return t===void 0?null:t}function Z(t){return t??void 0}function x(t){return t===""?void 0:t}function L(t,e){return t??e??[]}function X(t){return t??0}function Q(t){return t==null||t===""?"":t}function v(t,e){return t??e}function tt(t){if(t==null)return;const e=Number(t);return Number.isNaN(e)?void 0:e}function y(t,e){if(Object.is(t,e))return!0;if(typeof t!=typeof e)return!1;if(t===null||e===null||typeof t!="object"||typeof e!="object")return t===e;const r=Array.isArray(t),o=Array.isArray(e);if(r!==o)return!1;if(r&&o)return t.length!==e.length?!1:t.every((i,a)=>y(i,e[a]));const n=Object.keys(t).sort(),s=Object.keys(e).sort();return n.length!==s.length||!n.every((i,a)=>i===s[a])?!1:n.every(i=>y(t[i],e[i]))}const et=t=>{let e,r=[];return new Proxy(t,{construct(o,n){if(e||(e=new t(...n),r=n),!y(n,r))throw new Error("Cannot create multiple instances with different parameters");return e}})};function rt(t,e=100){if(t==null)throw new Promise(r=>setTimeout(r,e));return t}const nt=t=>{if(!t)return"";const e=new Date(t);return isNaN(e.getTime())?"":`${e.getFullYear()}/${(e.getMonth()+1).toString().padStart(2,"0")}/${e.getDate().toString().padStart(2,"0")}`},ot=t=>{if(!t)return"Unknown";const e=new Date(t);if(isNaN(e.getTime()))return"Invalid date";const r=new Date().getTime()-e.getTime(),o=Math.floor(r/1e3),n=Math.floor(o/60),s=Math.floor(n/60),i=Math.floor(s/24),a=Math.floor(i/30),u=Math.floor(i/365);return u>0?`${u} year${u>1?"s":""} ago`:a>0?`${a} month${a>1?"s":""} ago`:i>0?`${i} day${i>1?"s":""} ago`:s>0?`${s} hour${s>1?"s":""} ago`:n>0?`${n} minute${n>1?"s":""} ago`:"Just now"};function it(t){try{return new Date(t).toLocaleDateString(void 0,{month:"short",day:"numeric",hour:"2-digit",minute:"2-digit"})}catch{return t}}const st=t=>{try{const e=new Date(t);return isNaN(e.getTime())?t:`${e.getFullYear()}/${String(e.getMonth()+1).padStart(2,"0")}/${String(e.getDate()).padStart(2,"0")} ${String(e.getHours()).padStart(2,"0")}:${String(e.getMinutes()).padStart(2,"0")}`}catch{return t}},at=t=>{try{const e=new Date(t);return isNaN(e.getTime())?t:`${String(e.getMonth()+1).padStart(2,"0")}/${String(e.getDate()).padStart(2,"0")} ${String(e.getHours()).padStart(2,"0")}:${String(e.getMinutes()).padStart(2,"0")}`}catch{return t}},ut=t=>{try{const e=new Date(t);return isNaN(e.getTime())?t:`${e.getFullYear()}/${String(e.getMonth()+1).padStart(2,"0")}/${String(e.getDate()).padStart(2,"0")}`}catch{return t}},ft=t=>{try{const e=new Date(t);return isNaN(e.getTime())?t:`${String(e.getHours()).padStart(2,"0")}:${String(e.getMinutes()).padStart(2,"0")}`}catch{return t}};function ct(t,e){return t.reduce((r,o,n)=>(r[o]=e?e(o,n):o,r),{})}exports.createMap=ct;exports.err=N;exports.formatDate=ut;exports.formatDateTime=st;exports.formatDisplayDate=nt;exports.formatMonthDayTime=at;exports.formatNumberAbbreviated=Y;exports.formatRelativeTime=ot;exports.formatTickDate=it;exports.formatTime=ft;exports.getApiError=h;exports.getApiErrorMessage=T;exports.getCountryCode=U;exports.getItem=m.getItem;exports.hexToRGBA=b;exports.interpolateColor=B;exports.isValidEmail=_;exports.isValidPhoneNumber=O;exports.mergeById=R;exports.normalizeAddress=M;exports.ok=$;exports.omit=F;exports.onceAsync=G;exports.onceAsyncByKey=z;exports.pollUntil=H;exports.pruneArray=P;exports.removeItem=m.removeItem;exports.rgbToRgba=q;exports.safeArray=L;exports.safeMaybe=Z;exports.safeNilable=x;exports.safeNullable=J;exports.safeNum=tt;exports.safeOr=v;exports.safeStringable=Q;exports.safeZeroable=X;exports.setItem=m.setItem;exports.singleton=et;exports.suspenseIfNull=rt;exports.toDatabasePrice=K;exports.toDisplayPrice=S;exports.toDisplayPriceNumber=W;exports.toNumberInputValue=V;exports.toRgbaWithAlpha=k;exports.toTextInputValue=j;exports.withRetryResult=I;
4
4
 
5
5
  //# sourceMappingURL=utils.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"utils.cjs","names":[],"sources":["../src/utils/address.ts","../src/utils/apiError.ts","../src/utils/array.ts","../src/utils/colors.ts","../src/utils/email.ts","../src/utils/form.ts","../src/utils/object.ts","../src/utils/phone.ts","../src/utils/result.ts","../src/utils/retry.ts","../src/utils/polling.ts","../src/utils/price.ts","../src/utils/promise.ts","../src/utils/singleton.ts","../src/utils/suspense.ts","../src/utils/time.ts","../src/utils/types.ts"],"sourcesContent":["/**\n * 规范化地址字符串:去除首尾空白并将连续空白合并为单个空格\n * Normalize address string: trim and collapse consecutive spaces to one.\n * @param address - 原始地址字符串\n * @returns 规范化后的地址\n */\nexport function normalizeAddress(address: string): string {\n return address.trim().replace(/\\s+/g, \" \");\n}\n","/**\n * 与约定一致:HTTP 错误经 fiberx.Error/ErrorFromErrx → httpx.BuildErrorResp,\n * 响应体固定为 { status, err_code, message, details?, trace_id? }(axios 转 camelCase)。\n * 前端只认「有 response.data」= 后端错误体;UI 展示与 NFX 一致:先 errors 命名空间(后端拉取),再 message,再 fallback。\n */\n\nimport type { AxiosError } from \"axios\";\nimport type { ApiErrorBody } from \"@/types/api\";\n\nimport i18n from \"@/languages/languages/i18n\";\n\n/**\n * 仅当为后端错误(有 response.data)时解析为 ApiErrorBody,否则返回 null\n * Parse to ApiErrorBody only when response.data exists; otherwise null.\n * @param error - 通常为 Axios 错误或 unknown\n * @returns ApiErrorBody 或 null\n */\nexport function getApiError(error: unknown): ApiErrorBody | null {\n const d = (error as AxiosError<ApiErrorBody>)?.response?.data;\n if (!d || typeof d !== \"object\") return null;\n return {\n status: d.status,\n errCode: d.errCode,\n message: d.message,\n details: d.details,\n traceId: d.traceId,\n };\n}\n\n/**\n * UI 展示用错误文案:优先 i18n errors 命名空间(errCode),其次 api.message,最后 fallback\n * Get display message: i18n errors namespace (by errCode) → api.message → fallback.\n * @param error - 通常为 Axios 错误\n * @param fallback - 无法解析时的默认文案\n * @returns 展示用字符串\n */\nexport function getApiErrorMessage(error: unknown, fallback: string): string {\n const api = getApiError(error);\n if (api?.errCode) {\n const out = i18n.t(`errors:${api.errCode}`);\n if (out && out !== `errors:${api.errCode}`) return out;\n }\n if (api?.message) return api.message;\n return fallback;\n}\n","/**\n * 按 id 将 items 合并进数组:insert 仅插入新 id,upsert 覆盖同 id 并插入新 id;可 prepend 或 append\n * Merge items into array by id: insert = only new ids, upsert = overwrite same id + insert new; place = prepend | append.\n * @param arr - 原数组\n * @param items - 待合并项\n * @param idOf - 取 id 的函数\n * @param place - 新项插入位置\n * @param mode - insert 不覆盖已有 id;upsert 覆盖同 id\n * @returns 合并后的新数组\n */\nexport function mergeById<T>(arr: T[], items: T[], idOf: (x: T) => string, place: \"prepend\" | \"append\", mode: \"insert\" | \"upsert\") {\n if (items.length === 0) return arr;\n\n switch (mode) {\n case \"insert\":\n return insertUnique(arr, items, idOf, place);\n case \"upsert\":\n return upsertArray(arr, items, idOf, place);\n }\n}\n\nfunction insertUnique<T>(arr: T[], items: T[], idOf: (x: T) => string, place: \"prepend\" | \"append\") {\n const seen = new Set(arr.map(idOf));\n\n const toInsert = items.filter((x) => {\n const id = idOf(x);\n if (seen.has(id)) return false;\n seen.add(id);\n return true;\n });\n if (toInsert.length === 0) return arr;\n\n return place === \"prepend\" ? [...toInsert, ...arr] : [...arr, ...toInsert];\n}\n\nfunction upsertArray<T>(arr: T[], items: T[], idOf: (x: T) => string, place: \"prepend\" | \"append\") {\n const idxById = new Map<string, number>();\n arr.forEach((x, i) => idxById.set(idOf(x), i));\n\n let changed = false;\n const next = arr.slice();\n\n // Overwrite: elements with the same id are replaced with the real items\n for (const it of items) {\n const id = idOf(it);\n const idx = idxById.get(id);\n if (idx !== undefined) {\n if (next[idx] !== it) {\n next[idx] = it;\n changed = true;\n }\n }\n }\n\n // Insert: elements with the same id are replaced with the real items\n const toInsert: T[] = [];\n for (const it of items) {\n const id = idOf(it);\n if (!idxById.has(id)) toInsert.push(it);\n }\n if (toInsert.length > 0) {\n changed = true;\n return place === \"prepend\" ? [...toInsert, ...next] : [...next, ...toInsert];\n }\n\n return changed ? next : arr;\n}\n\n/**\n * 从数组中移除 id 在集合 ids 中的项,返回新数组\n * Remove elements whose id is in the given set; return new array.\n * @param arr - 原数组\n * @param ids - 要移除的 id 集合\n * @param idOf - 取 id 的函数\n * @returns 若无变化返回原数组,否则返回新数组\n */\nexport function pruneArray<T>(arr: T[], ids: ReadonlySet<string>, idOf: (x: T) => string) {\n let changed = false;\n const next = arr.filter((v) => {\n const keep = !ids.has(idOf(v));\n if (!keep) changed = true;\n return keep;\n });\n return changed ? next : arr;\n}\n","/**\n * 将任意颜色统一为指定透明度的 RGBA(rgb/rgba/hex 均用新 alpha 覆盖)\n * Convert any color to RGBA with given alpha (overwrites existing alpha).\n * @param color - rgb / rgba / hex 字符串\n * @param alpha - 透明度 0–1\n * @returns rgba(r, g, b, alpha) 字符串\n */\nexport const toRgbaWithAlpha = (color: string, alpha: number): string => {\n const rgbaMatch = color.match(/rgba?\\((\\d+),\\s*(\\d+),\\s*(\\d+)(?:,\\s*[\\d.]+)?\\)/);\n if (rgbaMatch) {\n const [, r, g, b] = rgbaMatch;\n return `rgba(${r}, ${g}, ${b}, ${alpha})`;\n }\n return hexToRGBA(color, alpha);\n};\n\n/**\n * 将 RGB 颜色转换为 RGBA\n * @param rgb - RGB 颜色字符串,格式: \"rgb(r, g, b)\"\n * @param alpha - 透明度,范围 0-1\n * @returns RGBA 颜色字符串,格式: \"rgba(r, g, b, alpha)\"\n *\n * @example\n * rgbToRgba(\"rgb(250, 30, 22)\", 0.3) // \"rgba(250, 30, 22, 0.3)\"\n */\nexport const rgbToRgba = (rgb: string, alpha: number): string => {\n if (rgb.startsWith(\"rgba\")) return rgb;\n const match = rgb.match(/rgb\\((\\d+),\\s*(\\d+),\\s*(\\d+)\\)/);\n if (match) {\n const [, r, g, b] = match;\n return `rgba(${r}, ${g}, ${b}, ${alpha})`;\n }\n console.warn(\"Invalid RGB color format:\", rgb);\n return rgb;\n};\n\n/**\n * 将 HEX 颜色转换为 RGBA\n * @param hex - HEX 颜色字符串,格式: \"#RRGGBB\" 或 \"#RGB\"\n * @param alpha - 透明度,范围 0-1\n * @returns RGBA 颜色字符串,格式: \"rgba(r, g, b, alpha)\"\n *\n * @example\n * hexToRGBA(\"#FA1E16\", 0.3) // \"rgba(250, 30, 22, 0.3)\"\n */\nexport const hexToRGBA = (hex: string, alpha: number): string => {\n // 如果传入是 rgb 或者 rgba 直接返回\n if (hex.indexOf(\"rgb\") !== -1) return hex;\n\n let r = 0,\n g = 0,\n b = 0;\n\n // 去除开头的 '#' 符号\n if (hex.startsWith(\"#\")) {\n hex = hex.slice(1);\n }\n\n // 处理 3 位或 6 位十六进制颜色\n if (hex.length === 3) {\n r = parseInt(hex[0] + hex[0], 16);\n g = parseInt(hex[1] + hex[1], 16);\n b = parseInt(hex[2] + hex[2], 16);\n } else if (hex.length === 6) {\n r = parseInt(hex.slice(0, 2), 16);\n g = parseInt(hex.slice(2, 4), 16);\n b = parseInt(hex.slice(4, 6), 16);\n } else {\n console.warn(\"Unsupported HEX color format\");\n }\n\n return `rgba(${r}, ${g}, ${b}, ${alpha})`;\n};\n\n/**\n * 在两色之间按 factor 线性插值(支持 hex / rgb)\n * Linear color interpolation between start and end by factor (0–1); supports hex and rgb.\n * @param start - 起始颜色\n * @param end - 结束颜色\n * @param factor - 插值系数 0–1\n * @returns rgb(r, g, b) 字符串\n */\nexport const interpolateColor = (start: string, end: string, factor: number): string => {\n const parse = (color: string): [number, number, number] => {\n const hex = color.match(/^#?([\\da-f]{2})([\\da-f]{2})([\\da-f]{2})$/i);\n if (hex) return [parseInt(hex[1], 16), parseInt(hex[2], 16), parseInt(hex[3], 16)];\n const rgb = color.match(/^rgb\\((\\d{1,3}),\\s*(\\d{1,3}),\\s*(\\d{1,3})\\)$/i);\n if (rgb) return [parseInt(rgb[1]), parseInt(rgb[2]), parseInt(rgb[3])];\n return [0, 0, 0];\n };\n const [r1, g1, b1] = parse(start);\n const [r2, g2, b2] = parse(end);\n const r = Math.round(r1 + (r2 - r1) * factor);\n const g = Math.round(g1 + (g2 - g1) * factor);\n const b = Math.round(b1 + (b2 - b1) * factor);\n return `rgb(${r}, ${g}, ${b})`;\n};\n","/**\n * 校验邮箱格式是否有效(简单正则:含 @ 与点)\n * Check if email format is valid (simple regex: contains @ and dot).\n * @param email - 待校验的邮箱字符串\n * @returns 是否有效\n */\nexport const isValidEmail = (email: string): boolean => {\n const emailRegex = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/;\n return emailRegex.test(email);\n};\n","/**\n * 将任意值转为文本输入框可用的字符串(用于表单回显)\n * Convert any value to a string suitable for text input (e.g. form display).\n * @param value - 任意值\n * @returns 字符串;null/undefined → \"\";数组 → 逗号拼接;对象 → JSON 字符串\n */\nexport function toTextInputValue(value: unknown): string {\n if (typeof value === \"string\") return value;\n if (value === null || value === undefined) return \"\";\n if (typeof value === \"number\" || typeof value === \"boolean\") return String(value);\n if (Array.isArray(value)) return value.map((item) => String(item)).join(\", \");\n if (typeof value === \"object\") return JSON.stringify(value);\n return String(value);\n}\n\n/**\n * 将任意值转为数字输入框可用的字符串(用于表单回显)\n * Convert any value to a string suitable for number input (e.g. form display).\n * @param value - 任意值\n * @returns 字符串;null/undefined → \"\";非有限数字 → \"\";数组取首项\n */\nexport function toNumberInputValue(value: unknown): string {\n if (value === null || value === undefined) return \"\";\n if (typeof value === \"number\") return Number.isFinite(value) ? String(value) : \"\";\n if (typeof value === \"string\") return value;\n if (typeof value === \"boolean\") return value ? \"1\" : \"0\";\n if (Array.isArray(value) && value.length > 0) return String(value[0]);\n return \"\";\n}\n","/**\n * 从对象中剔除指定键,返回新对象(不修改原对象)\n * Omit specified keys from object and return a new object.\n * @param obj - 源对象\n * @param keys - 要剔除的键(数组或单个键,可再接多个键)\n * @returns 剔除后的新对象\n * @example omit({ a: 1, b: 2, c: 3 }, ['a', 'c']) // { b: 2 }\n * @example omit({ a: 1, b: 2, c: 3 }, 'a', 'b') // { c: 3 }\n */\nexport function omit<Obj extends object, Key extends keyof Obj>(obj: Obj, keys: Key[] | Key, ...restKeys: Key[]): Omit<Obj, Key> {\n const result = {} as Omit<Obj, Key>;\n\n const removeKeys = Array.isArray(keys) ? keys : [keys, ...restKeys];\n const removeSet = new Set(removeKeys);\n\n for (const key of Object.keys(obj) as Key[]) {\n if (!removeSet.has(key)) {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n (result as any)[key] = obj[key];\n }\n }\n\n return result;\n}\n","import { PhoneNumberUtil } from \"google-libphonenumber\";\n\nconst phoneUtil = PhoneNumberUtil.getInstance();\n\n/**\n * 从电话号码解析国家/地区码(基于 google-libphonenumber)\n * Get country code from phone number (google-libphonenumber).\n * @param phoneNumber - 电话号码字符串\n * @returns 国家码数字\n */\nexport const getCountryCode = (phoneNumber: string) => {\n const phoneNumberObject = phoneUtil.parseAndKeepRawInput(phoneNumber);\n return phoneNumberObject.getCountryCode();\n};\n\n/**\n * 校验电话号码是否有效(基于 google-libphonenumber)\n * Check if phone number is valid (google-libphonenumber).\n * @param phoneNumber - 电话号码字符串\n * @returns 是否有效\n */\nexport const isValidPhoneNumber = (phoneNumber: string): boolean => {\n if (!phoneNumber || !phoneNumber.replace(/\\D/g, \"\").length) return false;\n try {\n const phoneNumberObject = phoneUtil.parseAndKeepRawInput(phoneNumber);\n return phoneUtil.isValidNumber(phoneNumberObject);\n } catch {\n return false;\n }\n};\n","/**\n * 结果类型:成功为 [T, null],失败为 [null, Error]。\n * Result type: success [T, null], failure [null, Error].\n * @template T - 成功时的值类型。Value type on success.\n * @example const [data, err] = await withRetryResult(() => fetchData()); if (err) return; use(data);\n */\nexport type Result<T> = [T, null] | [null, Error];\n\n/**\n * 构造成功结果。Create a successful result.\n * @param v - 结果值。Result value.\n * @returns [v, null]\n * @example ok(42) // [42, null]\n */\nexport const ok = <T>(v: T): Result<T> => [v, null];\n\n/**\n * 构造失败结果(非 Error 会包装为 Error)。Create a failed result; non-Error wrapped in Error.\n * @param e - 错误或任意值。Error or any value.\n * @returns [null, Error]\n * @example err(new Error(\"fail\")) // [null, Error]\n */\nexport const err = (e: unknown): Result<never> => [null, e instanceof Error ? e : new Error(String(e))];\n","import retry from \"async-retry\";\nimport type { Options as RetryOptions } from \"async-retry\";\n\nimport { err, ok } from \"./result\";\nimport type { Result } from \"./result\";\n\nexport type WithRetryOptions = RetryOptions & {\n /** 若返回 true 则不再重试,直接 bail。If true, do not retry and bail. */\n isNonRetryable?: (e: Error) => boolean;\n};\n\n/**\n * 带重试执行异步函数,成功返回 ok(value),失败返回 err(error)。\n * Retry async function until success or max attempts; returns Result.\n * @param fn - 可接收 bail 与 attempt 的异步函数。Async function receiving bail and attempt.\n * @param opts - async-retry 选项及 isNonRetryable。async-retry options and isNonRetryable.\n * @returns Promise<Result<T>>\n * @example const [data, err] = await withRetryResult(() => fetchData(), { retries: 3 });\n */\nexport async function withRetryResult<T>(fn: (bail: (e: Error) => void, attempt: number) => Promise<T>, opts?: WithRetryOptions): Promise<Result<T>> {\n try {\n const value = await retry(async (bail: (e: Error) => void, attempt: number) => {\n try {\n return await fn(bail, attempt);\n } catch (e) {\n if (opts?.isNonRetryable?.(e as Error)) bail(e as Error);\n throw e;\n }\n }, opts);\n return ok(value);\n } catch (e) {\n return err(e);\n }\n}\n","import type { Result } from \"./result\";\nimport type { WithRetryOptions } from \"./retry\";\n\nimport { withRetryResult } from \"./retry\";\n\n/**\n * 轮询直到 isOK(data) 为 true(内部用 withRetryResult 重试)。\n * Poll until fetcher returns data that satisfies isOK; uses withRetryResult.\n * @param fetcher - 拉取数据的函数。Function that fetches data.\n * @param isOK - 判断数据是否满足条件。Predicate to check if data is OK.\n * @param opts - 重试选项。Retry options.\n * @returns Promise<Result<T>>\n * @example const [job] = await pollUntil(() => getJob(id), (j) => j.status === \"done\", { retries: 10 });\n */\nexport async function pollUntil<T>(fetcher: () => Promise<T>, isOK: (data: T) => boolean, opts?: WithRetryOptions): Promise<Result<T>> {\n return withRetryResult<T>(async (_bail, _attempt) => {\n const data = await fetcher();\n if (!isOK(data)) {\n throw new Error(\"Condition not satisfied\");\n }\n return data;\n }, opts);\n}\n","/**\n * 显示价格 → 数据库价格(分)。用于提交到后端、保存草稿等\n * Display price to database price (cents). For submit/save.\n * @param displayPrice - 展示用价格(元)\n * @returns 数据库存储价格(分)\n */\nexport function toDatabasePrice(displayPrice: number): number {\n return Math.round(Number(displayPrice) * 100);\n}\n\n/**\n * 数据库价格(分)→ 显示价格字符串(两位小数)\n * Database price (cents) to display string (2 decimal places).\n * @param databasePrice - 数据库价格(分)\n * @returns 如 \"12.34\"\n */\nexport function toDisplayPrice(databasePrice: number): string {\n return (Number(databasePrice) / 100).toFixed(2);\n}\n\n/**\n * 数据库价格(分)→ 显示价格数字。用于表单、计算等\n * Database price (cents) to display number.\n * @param databasePrice - 数据库价格(分)\n * @returns 数字(元)\n */\nexport function toDisplayPriceNumber(databasePrice: number): number {\n return Number(toDisplayPrice(databasePrice));\n}\n\n/**\n * 数字缩写显示(如 1.2k+、3.5M+、1.1B+)\n * Format number to abbreviated string (e.g. 1.2k+, 3.5M+, 1.1B+).\n * @param num - 数字\n * @returns 缩写字符串\n */\nexport function formatNumberAbbreviated(num: number): string {\n if (num < 1_000) {\n return num.toString();\n }\n\n if (num < 1_000_000) {\n return `${(num / 1_000).toFixed(1).replace(/\\.0$/, \"\")}k+`;\n }\n\n if (num < 1_000_000_000) {\n return `${(num / 1_000_000).toFixed(1).replace(/\\.0$/, \"\")}M+`;\n }\n\n return `${(num / 1_000_000_000).toFixed(1).replace(/\\.0$/, \"\")}B+`;\n}\n","/**\n * 包装为「仅执行一次」的异步函数(重复调用返回同一 Promise)。\n * Wrap async function so it runs only once; repeated calls return the same promise.\n * @param fn - 要执行的异步函数。Async function to run once.\n * @returns 包装后的函数,多次调用只执行一次。Wrapped function; only first call runs.\n * @example const loadOnce = onceAsync(() => fetch(\"/api/config\")); loadOnce(); loadOnce(); // 只请求一次\n */\nexport function onceAsync<T, Args extends unknown[]>(fn: (...args: Args) => Promise<T>) {\n let promise: Promise<T> | null = null;\n\n return async (...args: Args) => {\n if (promise) return promise;\n promise = fn(...args).finally(() => (promise = null));\n return promise;\n };\n}\n\n/**\n * 按 key 仅执行一次的异步函数(同一 key 重复调用返回同一 Promise)。\n * Async function that runs only once per key; same key returns same promise.\n * @param fn - 要执行的异步函数。Async function to run.\n * @param keyExtractor - 从参数中提取 key 的函数。Function to extract key from args.\n * @returns 包装后的函数。Wrapped function.\n * @example const fetchUser = onceAsyncByKey((id: string) => api.getUser(id), (id) => id); fetchUser(\"1\"); fetchUser(\"1\"); // 同 id 只请求一次\n */\nexport function onceAsyncByKey<T, Args extends unknown[]>(fn: (...args: Args) => Promise<T>, keyExtractor: (...args: Args) => string) {\n const promises = new Map<string, Promise<T>>();\n\n return async (...args: Args) => {\n const key = keyExtractor(...args);\n const existingPromise = promises.get(key);\n if (existingPromise) return existingPromise;\n\n const promise = fn(...args).finally(() => promises.delete(key));\n promises.set(key, promise);\n return promise;\n };\n}\n","/** 可实例化的构造函数类型。Instantiable constructor type. */\ntype Constructor<T extends object = object> = new (...args: unknown[]) => T;\n\n/** 深度比较两个值是否相等(支持 primitives、数组、plain objects)。Deep equal for primitives, arrays, plain objects. */\nfunction isEqual(a: unknown, b: unknown): boolean {\n if (Object.is(a, b)) return true;\n if (typeof a !== typeof b) return false;\n if (a === null || b === null) return a === b;\n if (typeof a !== \"object\" || typeof b !== \"object\") return a === b;\n\n const arrA = Array.isArray(a);\n const arrB = Array.isArray(b);\n if (arrA !== arrB) return false;\n if (arrA && arrB) {\n if (a.length !== b.length) return false;\n return a.every((v, i) => isEqual(v, (b as unknown[])[i]));\n }\n\n const keysA = Object.keys(a as object).sort();\n const keysB = Object.keys(b as object).sort();\n if (keysA.length !== keysB.length) return false;\n if (!keysA.every((k, i) => k === keysB[i])) return false;\n return keysA.every((k) => isEqual((a as Record<string, unknown>)[k], (b as Record<string, unknown>)[k]));\n}\n\n/**\n * 单例工厂:将传入的构造函数包装为单例模式;Proxy 拦截 construct,多次 new 返回同一实例。\n * Singleton factory: wrap constructor so that multiple new calls return the same instance.\n *\n * @template T - 实例类型(需为 object)。Instance type (must be object).\n * @param className - 被包装的构造函数(class)。Constructor (class) to wrap.\n * @returns 代理后的构造函数,调用时始终返回同一实例。Proxied constructor returning same instance.\n *\n * @example\n * ```ts\n * class MyService { ... }\n * const SingletonService = singleton(MyService);\n * const a = new SingletonService();\n * const b = new SingletonService();\n * console.log(a === b); // true\n * ```\n */\nexport const singleton = <T extends object>(className: Constructor<T>): Constructor<T> => {\n let instance: T | undefined;\n let parameters: unknown[] = [];\n return new Proxy(className, {\n construct(_target: Constructor<T>, args: unknown[]): T {\n if (!instance) {\n instance = new className(...args);\n parameters = args;\n }\n if (!isEqual(args, parameters)) {\n throw new Error(\"Cannot create multiple instances with different parameters\");\n }\n return instance;\n },\n }) as Constructor<T>;\n};\n","import type { Nilable } from \"@/types\";\n\n/**\n * 若值为 null/undefined 则抛出 Promise 使 React Suspense 挂起,否则返回该值。\n * If value is null/undefined, throw a promise to suspend (React Suspense); otherwise return value.\n * @param value - 待检查的值。Value to check.\n * @param delay - 重试前的延迟(毫秒)。Delay in ms before retry.\n * @returns 非空时的 value。Value when non-null.\n * @example const data = suspenseIfNull(resource); // 在 Suspense 边界内,data 非空时才渲染\n */\nexport function suspenseIfNull<T>(value: Nilable<T>, delay = 100): T {\n if (value == null) {\n throw new Promise<void>((resolve) => setTimeout(resolve, delay));\n }\n return value;\n}\n","/**\n * 格式化日期为 YYYY/MM/DD(空或无效返回 \"\")\n * Format date as YYYY/MM/DD; empty or invalid returns \"\".\n * @param dateString - 日期字符串\n * @returns 格式化后的日期字符串,无效则 \"\"\n */\nexport const formatDisplayDate = (dateString: string | undefined | null): string => {\n if (!dateString) return \"\";\n const date = new Date(dateString);\n if (isNaN(date.getTime())) return \"\";\n const year = date.getFullYear();\n const month = (date.getMonth() + 1).toString().padStart(2, \"0\");\n const day = date.getDate().toString().padStart(2, \"0\");\n return `${year}/${month}/${day}`;\n};\n\n/**\n * 格式化为相对时间(如 2 days ago, 3 months ago)\n * Format as relative time (e.g. 2 days ago, 3 months ago).\n * @param dateString - 日期字符串\n * @returns 相对时间字符串,无效为 \"Invalid date\",空为 \"Unknown\"\n */\nexport const formatRelativeTime = (dateString: string | undefined | null): string => {\n if (!dateString) return \"Unknown\";\n const date = new Date(dateString);\n if (isNaN(date.getTime())) return \"Invalid date\";\n\n const now = new Date();\n const diffMs = now.getTime() - date.getTime();\n const diffSeconds = Math.floor(diffMs / 1000);\n const diffMinutes = Math.floor(diffSeconds / 60);\n const diffHours = Math.floor(diffMinutes / 60);\n const diffDays = Math.floor(diffHours / 24);\n const diffMonths = Math.floor(diffDays / 30);\n const diffYears = Math.floor(diffDays / 365);\n\n if (diffYears > 0) return `${diffYears} year${diffYears > 1 ? \"s\" : \"\"} ago`;\n if (diffMonths > 0) return `${diffMonths} month${diffMonths > 1 ? \"s\" : \"\"} ago`;\n if (diffDays > 0) return `${diffDays} day${diffDays > 1 ? \"s\" : \"\"} ago`;\n if (diffHours > 0) return `${diffHours} hour${diffHours > 1 ? \"s\" : \"\"} ago`;\n if (diffMinutes > 0) return `${diffMinutes} minute${diffMinutes > 1 ? \"s\" : \"\"} ago`;\n return \"Just now\";\n};\n\n/**\n * 格式化 ISO 日期为图表轴/提示用短格式(如:Jan 4, 02:30 PM)\n * Format ISO date for chart axis/tooltip (e.g. Jan 4, 02:30 PM).\n * @param iso - ISO 日期字符串\n * @returns 格式化字符串,解析失败则返回原串\n */\nexport function formatTickDate(iso: string): string {\n try {\n const d = new Date(iso);\n return d.toLocaleDateString(undefined, {\n month: \"short\",\n day: \"numeric\",\n hour: \"2-digit\",\n minute: \"2-digit\",\n });\n } catch {\n return iso;\n }\n}\n\n/**\n * 格式化时间显示年月日时分 (YYYY/MM/DD HH:mm)\n * Format date-time as year/month/day hour:minute.\n * @param dateString - 时间字符串\n * @returns 格式化后的时间字符串,无效则返回原串\n */\nexport const formatDateTime = (dateString: string): string => {\n try {\n const date = new Date(dateString);\n if (isNaN(date.getTime())) return dateString;\n const year = date.getFullYear();\n const month = String(date.getMonth() + 1).padStart(2, \"0\");\n const day = String(date.getDate()).padStart(2, \"0\");\n const hour = String(date.getHours()).padStart(2, \"0\");\n const minute = String(date.getMinutes()).padStart(2, \"0\");\n return `${year}/${month}/${day} ${hour}:${minute}`;\n } catch {\n return dateString;\n }\n};\n\n/**\n * 格式化时间显示月日时分 (MM/DD HH:mm)\n * Format date-time as month/day hour:minute.\n * @param dateString - 时间字符串\n * @returns 格式化后的时间字符串,无效则返回原串\n */\nexport const formatMonthDayTime = (dateString: string): string => {\n try {\n const date = new Date(dateString);\n if (isNaN(date.getTime())) return dateString;\n const month = String(date.getMonth() + 1).padStart(2, \"0\");\n const day = String(date.getDate()).padStart(2, \"0\");\n const hour = String(date.getHours()).padStart(2, \"0\");\n const minute = String(date.getMinutes()).padStart(2, \"0\");\n return `${month}/${day} ${hour}:${minute}`;\n } catch {\n return dateString;\n }\n};\n\n/**\n * 格式化日期仅年月日 (YYYY/MM/DD),用于生日等\n * Format date as year/month/day only.\n * @param dateString - 日期字符串,如 \"2000-01-15\" 或 ISO\n * @returns 格式化后的日期字符串,无效则返回原串\n */\nexport const formatDate = (dateString: string): string => {\n try {\n const date = new Date(dateString);\n if (isNaN(date.getTime())) return dateString;\n const year = date.getFullYear();\n const month = String(date.getMonth() + 1).padStart(2, \"0\");\n const day = String(date.getDate()).padStart(2, \"0\");\n return `${year}/${month}/${day}`;\n } catch {\n return dateString;\n }\n};\n\n/**\n * 格式化时间显示时分 (HH:mm)\n * Format time as hour:minute.\n * @param dateString - 时间字符串\n * @returns 格式化后的时间字符串,无效则返回原串\n */\nexport const formatTime = (dateString: string): string => {\n try {\n const date = new Date(dateString);\n if (isNaN(date.getTime())) return dateString;\n const hour = String(date.getHours()).padStart(2, \"0\");\n const minute = String(date.getMinutes()).padStart(2, \"0\");\n return `${hour}:${minute}`;\n } catch {\n return dateString;\n }\n};\n","/**\n * 从字符串列表创建键值映射对象;可选 valueMapper 指定值\n * Create record from string list; optional valueMapper for values.\n * @param list - 字符串数组(作为键)\n * @param valueMapper - 可选,(item, index) => value,缺省时值为键本身\n * @returns Record<list[number], V>\n * @example createMap([\"a\",\"b\",\"c\"]) // { a: \"a\", b: \"b\", c: \"c\" }\n * @example createMap([\"a\",\"b\",\"c\"], (_, i) => i) // { a: 0, b: 1, c: 2 }\n */\nexport function createMap<T extends string, V = T>(list: ReadonlyArray<T>, valueMapper?: (item: T, index: number) => V): Record<T, V> {\n return list.reduce(\n (acc, item, index) => {\n acc[item] = valueMapper ? valueMapper(item, index) : (item as unknown as V);\n return acc;\n },\n {} as Record<T, V>,\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"utils.cjs","names":[],"sources":["../src/utils/address.ts","../src/utils/apiError.ts","../src/utils/array.ts","../src/utils/colors.ts","../src/utils/email.ts","../src/utils/form.ts","../src/utils/object.ts","../src/utils/phone.ts","../src/utils/result.ts","../src/utils/retry.ts","../src/utils/polling.ts","../src/utils/price.ts","../src/utils/promise.ts","../src/utils/safe.ts","../src/utils/singleton.ts","../src/utils/suspense.ts","../src/utils/time.ts","../src/utils/types.ts"],"sourcesContent":["/**\n * 规范化地址字符串:去除首尾空白并将连续空白合并为单个空格\n * Normalize address string: trim and collapse consecutive spaces to one.\n * @param address - 原始地址字符串\n * @returns 规范化后的地址\n */\nexport function normalizeAddress(address: string): string {\n return address.trim().replace(/\\s+/g, \" \");\n}\n","/**\n * 与约定一致:HTTP 错误经 fiberx.Error/ErrorFromErrx → httpx.BuildErrorResp,\n * 响应体固定为 { status, err_code, message, details?, trace_id? }(axios 转 camelCase)。\n * 前端只认「有 response.data」= 后端错误体;UI 展示与 NFX 一致:先 errors 命名空间(后端拉取),再 message,再 fallback。\n */\n\nimport type { AxiosError } from \"axios\";\nimport type { ApiErrorBody } from \"@/types/api\";\n\nimport i18n from \"@/languages/languages/i18n\";\n\n/**\n * 仅当为后端错误(有 response.data)时解析为 ApiErrorBody,否则返回 null\n * Parse to ApiErrorBody only when response.data exists; otherwise null.\n * @param error - 通常为 Axios 错误或 unknown\n * @returns ApiErrorBody 或 null\n */\nexport function getApiError(error: unknown): ApiErrorBody | null {\n const d = (error as AxiosError<ApiErrorBody>)?.response?.data;\n if (!d || typeof d !== \"object\") return null;\n return {\n status: d.status,\n errCode: d.errCode,\n message: d.message,\n details: d.details,\n traceId: d.traceId,\n };\n}\n\n/**\n * UI 展示用错误文案:优先 i18n errors 命名空间(errCode),其次 api.message,最后 fallback\n * Get display message: i18n errors namespace (by errCode) → api.message → fallback.\n * @param error - 通常为 Axios 错误\n * @param fallback - 无法解析时的默认文案\n * @returns 展示用字符串\n */\nexport function getApiErrorMessage(error: unknown, fallback: string): string {\n const api = getApiError(error);\n if (api?.errCode) {\n const out = i18n.t(`errors:${api.errCode}`);\n if (out && out !== `errors:${api.errCode}`) return out;\n }\n if (api?.message) return api.message;\n return fallback;\n}\n","import type { Array } from \"@/types\";\n\n/**\n * 按 id 将 items 合并进数组:insert 仅插入新 id,upsert 覆盖同 id 并插入新 id;可 prepend 或 append\n * Merge items into array by id: insert = only new ids, upsert = overwrite same id + insert new; place = prepend | append.\n * @param arr - 原数组\n * @param items - 待合并项\n * @param idOf - 取 id 的函数\n * @param place - 新项插入位置\n * @param mode - insert 不覆盖已有 id;upsert 覆盖同 id\n * @returns 合并后的新数组\n */\nexport function mergeById<T>(arr: Array<T>, items: Array<T>, idOf: (x: T) => string, place: \"prepend\" | \"append\", mode: \"insert\" | \"upsert\") {\n if (items.length === 0) return arr;\n\n switch (mode) {\n case \"insert\":\n return insertUnique(arr, items, idOf, place);\n case \"upsert\":\n return upsertArray(arr, items, idOf, place);\n }\n}\n\nfunction insertUnique<T>(arr: Array<T>, items: Array<T>, idOf: (x: T) => string, place: \"prepend\" | \"append\") {\n const seen = new Set(arr.map(idOf));\n\n const toInsert = items.filter((x) => {\n const id = idOf(x);\n if (seen.has(id)) return false;\n seen.add(id);\n return true;\n });\n if (toInsert.length === 0) return arr;\n\n return place === \"prepend\" ? [...toInsert, ...arr] : [...arr, ...toInsert];\n}\n\nfunction upsertArray<T>(arr: Array<T>, items: Array<T>, idOf: (x: T) => string, place: \"prepend\" | \"append\") {\n const idxById = new Map<string, number>();\n arr.forEach((x, i) => idxById.set(idOf(x), i));\n\n let changed = false;\n const next = arr.slice();\n\n // Overwrite: elements with the same id are replaced with the real items\n for (const it of items) {\n const id = idOf(it);\n const idx = idxById.get(id);\n if (idx !== undefined) {\n if (next[idx] !== it) {\n next[idx] = it;\n changed = true;\n }\n }\n }\n\n // Insert: elements with the same id are replaced with the real items\n const toInsert: Array<T> = [];\n for (const it of items) {\n const id = idOf(it);\n if (!idxById.has(id)) toInsert.push(it);\n }\n if (toInsert.length > 0) {\n changed = true;\n return place === \"prepend\" ? [...toInsert, ...next] : [...next, ...toInsert];\n }\n\n return changed ? next : arr;\n}\n\n/**\n * 从数组中移除 id 在集合 ids 中的项,返回新数组\n * Remove elements whose id is in the given set; return new array.\n * @param arr - 原数组\n * @param ids - 要移除的 id 集合\n * @param idOf - 取 id 的函数\n * @returns 若无变化返回原数组,否则返回新数组\n */\nexport function pruneArray<T>(arr: Array<T>, ids: ReadonlySet<string>, idOf: (x: T) => string) {\n let changed = false;\n const next = arr.filter((v) => {\n const keep = !ids.has(idOf(v));\n if (!keep) changed = true;\n return keep;\n });\n return changed ? next : arr;\n}\n","/**\n * 将任意颜色统一为指定透明度的 RGBA(rgb/rgba/hex 均用新 alpha 覆盖)\n * Convert any color to RGBA with given alpha (overwrites existing alpha).\n * @param color - rgb / rgba / hex 字符串\n * @param alpha - 透明度 0–1\n * @returns rgba(r, g, b, alpha) 字符串\n */\nexport const toRgbaWithAlpha = (color: string, alpha: number): string => {\n const rgbaMatch = color.match(/rgba?\\((\\d+),\\s*(\\d+),\\s*(\\d+)(?:,\\s*[\\d.]+)?\\)/);\n if (rgbaMatch) {\n const [, r, g, b] = rgbaMatch;\n return `rgba(${r}, ${g}, ${b}, ${alpha})`;\n }\n return hexToRGBA(color, alpha);\n};\n\n/**\n * 将 RGB 颜色转换为 RGBA\n * @param rgb - RGB 颜色字符串,格式: \"rgb(r, g, b)\"\n * @param alpha - 透明度,范围 0-1\n * @returns RGBA 颜色字符串,格式: \"rgba(r, g, b, alpha)\"\n *\n * @example\n * rgbToRgba(\"rgb(250, 30, 22)\", 0.3) // \"rgba(250, 30, 22, 0.3)\"\n */\nexport const rgbToRgba = (rgb: string, alpha: number): string => {\n if (rgb.startsWith(\"rgba\")) return rgb;\n const match = rgb.match(/rgb\\((\\d+),\\s*(\\d+),\\s*(\\d+)\\)/);\n if (match) {\n const [, r, g, b] = match;\n return `rgba(${r}, ${g}, ${b}, ${alpha})`;\n }\n console.warn(\"Invalid RGB color format:\", rgb);\n return rgb;\n};\n\n/**\n * 将 HEX 颜色转换为 RGBA\n * @param hex - HEX 颜色字符串,格式: \"#RRGGBB\" 或 \"#RGB\"\n * @param alpha - 透明度,范围 0-1\n * @returns RGBA 颜色字符串,格式: \"rgba(r, g, b, alpha)\"\n *\n * @example\n * hexToRGBA(\"#FA1E16\", 0.3) // \"rgba(250, 30, 22, 0.3)\"\n */\nexport const hexToRGBA = (hex: string, alpha: number): string => {\n // 如果传入是 rgb 或者 rgba 直接返回\n if (hex.indexOf(\"rgb\") !== -1) return hex;\n\n let r = 0,\n g = 0,\n b = 0;\n\n // 去除开头的 '#' 符号\n if (hex.startsWith(\"#\")) {\n hex = hex.slice(1);\n }\n\n // 处理 3 位或 6 位十六进制颜色\n if (hex.length === 3) {\n r = parseInt(hex[0] + hex[0], 16);\n g = parseInt(hex[1] + hex[1], 16);\n b = parseInt(hex[2] + hex[2], 16);\n } else if (hex.length === 6) {\n r = parseInt(hex.slice(0, 2), 16);\n g = parseInt(hex.slice(2, 4), 16);\n b = parseInt(hex.slice(4, 6), 16);\n } else {\n console.warn(\"Unsupported HEX color format\");\n }\n\n return `rgba(${r}, ${g}, ${b}, ${alpha})`;\n};\n\n/**\n * 在两色之间按 factor 线性插值(支持 hex / rgb)\n * Linear color interpolation between start and end by factor (0–1); supports hex and rgb.\n * @param start - 起始颜色\n * @param end - 结束颜色\n * @param factor - 插值系数 0–1\n * @returns rgb(r, g, b) 字符串\n */\nexport const interpolateColor = (start: string, end: string, factor: number): string => {\n const parse = (color: string): [number, number, number] => {\n const hex = color.match(/^#?([\\da-f]{2})([\\da-f]{2})([\\da-f]{2})$/i);\n if (hex) return [parseInt(hex[1], 16), parseInt(hex[2], 16), parseInt(hex[3], 16)];\n const rgb = color.match(/^rgb\\((\\d{1,3}),\\s*(\\d{1,3}),\\s*(\\d{1,3})\\)$/i);\n if (rgb) return [parseInt(rgb[1]), parseInt(rgb[2]), parseInt(rgb[3])];\n return [0, 0, 0];\n };\n const [r1, g1, b1] = parse(start);\n const [r2, g2, b2] = parse(end);\n const r = Math.round(r1 + (r2 - r1) * factor);\n const g = Math.round(g1 + (g2 - g1) * factor);\n const b = Math.round(b1 + (b2 - b1) * factor);\n return `rgb(${r}, ${g}, ${b})`;\n};\n","/**\n * 校验邮箱格式是否有效(简单正则:含 @ 与点)\n * Check if email format is valid (simple regex: contains @ and dot).\n * @param email - 待校验的邮箱字符串\n * @returns 是否有效\n */\nexport const isValidEmail = (email: string): boolean => {\n const emailRegex = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/;\n return emailRegex.test(email);\n};\n","/**\n * 将任意值转为文本输入框可用的字符串(用于表单回显)\n * Convert any value to a string suitable for text input (e.g. form display).\n * @param value - 任意值\n * @returns 字符串;null/undefined → \"\";数组 → 逗号拼接;对象 → JSON 字符串\n */\nexport function toTextInputValue(value: unknown): string {\n if (typeof value === \"string\") return value;\n if (value === null || value === undefined) return \"\";\n if (typeof value === \"number\" || typeof value === \"boolean\") return String(value);\n if (Array.isArray(value)) return value.map((item) => String(item)).join(\", \");\n if (typeof value === \"object\") return JSON.stringify(value);\n return String(value);\n}\n\n/**\n * 将任意值转为数字输入框可用的字符串(用于表单回显)\n * Convert any value to a string suitable for number input (e.g. form display).\n * @param value - 任意值\n * @returns 字符串;null/undefined → \"\";非有限数字 → \"\";数组取首项\n */\nexport function toNumberInputValue(value: unknown): string {\n if (value === null || value === undefined) return \"\";\n if (typeof value === \"number\") return Number.isFinite(value) ? String(value) : \"\";\n if (typeof value === \"string\") return value;\n if (typeof value === \"boolean\") return value ? \"1\" : \"0\";\n if (Array.isArray(value) && value.length > 0) return String(value[0]);\n return \"\";\n}\n","/**\n * 从对象中剔除指定键,返回新对象(不修改原对象)\n * Omit specified keys from object and return a new object.\n * @param obj - 源对象\n * @param keys - 要剔除的键(数组或单个键,可再接多个键)\n * @returns 剔除后的新对象\n * @example omit({ a: 1, b: 2, c: 3 }, ['a', 'c']) // { b: 2 }\n * @example omit({ a: 1, b: 2, c: 3 }, 'a', 'b') // { c: 3 }\n */\nexport function omit<Obj extends object, Key extends keyof Obj>(obj: Obj, keys: Key[] | Key, ...restKeys: Key[]): Omit<Obj, Key> {\n const result = {} as Omit<Obj, Key>;\n\n const removeKeys = Array.isArray(keys) ? keys : [keys, ...restKeys];\n const removeSet = new Set(removeKeys);\n\n for (const key of Object.keys(obj) as Key[]) {\n if (!removeSet.has(key)) {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n (result as any)[key] = obj[key];\n }\n }\n\n return result;\n}\n","import { PhoneNumberUtil } from \"google-libphonenumber\";\n\nconst phoneUtil = PhoneNumberUtil.getInstance();\n\n/**\n * 从电话号码解析国家/地区码(基于 google-libphonenumber)\n * Get country code from phone number (google-libphonenumber).\n * @param phoneNumber - 电话号码字符串\n * @returns 国家码数字\n */\nexport const getCountryCode = (phoneNumber: string) => {\n const phoneNumberObject = phoneUtil.parseAndKeepRawInput(phoneNumber);\n return phoneNumberObject.getCountryCode();\n};\n\n/**\n * 校验电话号码是否有效(基于 google-libphonenumber)\n * Check if phone number is valid (google-libphonenumber).\n * @param phoneNumber - 电话号码字符串\n * @returns 是否有效\n */\nexport const isValidPhoneNumber = (phoneNumber: string): boolean => {\n if (!phoneNumber || !phoneNumber.replace(/\\D/g, \"\").length) return false;\n try {\n const phoneNumberObject = phoneUtil.parseAndKeepRawInput(phoneNumber);\n return phoneUtil.isValidNumber(phoneNumberObject);\n } catch {\n return false;\n }\n};\n","/**\n * 结果类型:成功为 [T, null],失败为 [null, Error]。\n * Result type: success [T, null], failure [null, Error].\n * @template T - 成功时的值类型。Value type on success.\n * @example const [data, err] = await withRetryResult(() => fetchData()); if (err) return; use(data);\n */\nexport type Result<T> = [T, null] | [null, Error];\n\n/**\n * 构造成功结果。Create a successful result.\n * @param v - 结果值。Result value.\n * @returns [v, null]\n * @example ok(42) // [42, null]\n */\nexport const ok = <T>(v: T): Result<T> => [v, null];\n\n/**\n * 构造失败结果(非 Error 会包装为 Error)。Create a failed result; non-Error wrapped in Error.\n * @param e - 错误或任意值。Error or any value.\n * @returns [null, Error]\n * @example err(new Error(\"fail\")) // [null, Error]\n */\nexport const err = (e: unknown): Result<never> => [null, e instanceof Error ? e : new Error(String(e))];\n","import retry from \"async-retry\";\nimport type { Options as RetryOptions } from \"async-retry\";\n\nimport { err, ok } from \"./result\";\nimport type { Result } from \"./result\";\n\nexport type WithRetryOptions = RetryOptions & {\n /** 若返回 true 则不再重试,直接 bail。If true, do not retry and bail. */\n isNonRetryable?: (e: Error) => boolean;\n};\n\n/**\n * 带重试执行异步函数,成功返回 ok(value),失败返回 err(error)。\n * Retry async function until success or max attempts; returns Result.\n * @param fn - 可接收 bail 与 attempt 的异步函数。Async function receiving bail and attempt.\n * @param opts - async-retry 选项及 isNonRetryable。async-retry options and isNonRetryable.\n * @returns Promise<Result<T>>\n * @example const [data, err] = await withRetryResult(() => fetchData(), { retries: 3 });\n */\nexport async function withRetryResult<T>(fn: (bail: (e: Error) => void, attempt: number) => Promise<T>, opts?: WithRetryOptions): Promise<Result<T>> {\n try {\n const value = await retry(async (bail: (e: Error) => void, attempt: number) => {\n try {\n return await fn(bail, attempt);\n } catch (e) {\n if (opts?.isNonRetryable?.(e as Error)) bail(e as Error);\n throw e;\n }\n }, opts);\n return ok(value);\n } catch (e) {\n return err(e);\n }\n}\n","import type { Result } from \"./result\";\nimport type { WithRetryOptions } from \"./retry\";\n\nimport { withRetryResult } from \"./retry\";\n\n/**\n * 轮询直到 isOK(data) 为 true(内部用 withRetryResult 重试)。\n * Poll until fetcher returns data that satisfies isOK; uses withRetryResult.\n * @param fetcher - 拉取数据的函数。Function that fetches data.\n * @param isOK - 判断数据是否满足条件。Predicate to check if data is OK.\n * @param opts - 重试选项。Retry options.\n * @returns Promise<Result<T>>\n * @example const [job] = await pollUntil(() => getJob(id), (j) => j.status === \"done\", { retries: 10 });\n */\nexport async function pollUntil<T>(fetcher: () => Promise<T>, isOK: (data: T) => boolean, opts?: WithRetryOptions): Promise<Result<T>> {\n return withRetryResult<T>(async (_bail, _attempt) => {\n const data = await fetcher();\n if (!isOK(data)) {\n throw new Error(\"Condition not satisfied\");\n }\n return data;\n }, opts);\n}\n","/**\n * 显示价格 → 数据库价格(分)。用于提交到后端、保存草稿等\n * Display price to database price (cents). For submit/save.\n * @param displayPrice - 展示用价格(元)\n * @returns 数据库存储价格(分)\n */\nexport function toDatabasePrice(displayPrice: number): number {\n return Math.round(Number(displayPrice) * 100);\n}\n\n/**\n * 数据库价格(分)→ 显示价格字符串(两位小数)\n * Database price (cents) to display string (2 decimal places).\n * @param databasePrice - 数据库价格(分)\n * @returns 如 \"12.34\"\n */\nexport function toDisplayPrice(databasePrice: number): string {\n return (Number(databasePrice) / 100).toFixed(2);\n}\n\n/**\n * 数据库价格(分)→ 显示价格数字。用于表单、计算等\n * Database price (cents) to display number.\n * @param databasePrice - 数据库价格(分)\n * @returns 数字(元)\n */\nexport function toDisplayPriceNumber(databasePrice: number): number {\n return Number(toDisplayPrice(databasePrice));\n}\n\n/**\n * 数字缩写显示(如 1.2k+、3.5M+、1.1B+)\n * Format number to abbreviated string (e.g. 1.2k+, 3.5M+, 1.1B+).\n * @param num - 数字\n * @returns 缩写字符串\n */\nexport function formatNumberAbbreviated(num: number): string {\n if (num < 1_000) {\n return num.toString();\n }\n\n if (num < 1_000_000) {\n return `${(num / 1_000).toFixed(1).replace(/\\.0$/, \"\")}k+`;\n }\n\n if (num < 1_000_000_000) {\n return `${(num / 1_000_000).toFixed(1).replace(/\\.0$/, \"\")}M+`;\n }\n\n return `${(num / 1_000_000_000).toFixed(1).replace(/\\.0$/, \"\")}B+`;\n}\n","/**\n * 包装为「仅执行一次」的异步函数(重复调用返回同一 Promise)。\n * Wrap async function so it runs only once; repeated calls return the same promise.\n * @param fn - 要执行的异步函数。Async function to run once.\n * @returns 包装后的函数,多次调用只执行一次。Wrapped function; only first call runs.\n * @example const loadOnce = onceAsync(() => fetch(\"/api/config\")); loadOnce(); loadOnce(); // 只请求一次\n */\nexport function onceAsync<T, Args extends unknown[]>(fn: (...args: Args) => Promise<T>) {\n let promise: Promise<T> | null = null;\n\n return async (...args: Args) => {\n if (promise) return promise;\n promise = fn(...args).finally(() => (promise = null));\n return promise;\n };\n}\n\n/**\n * 按 key 仅执行一次的异步函数(同一 key 重复调用返回同一 Promise)。\n * Async function that runs only once per key; same key returns same promise.\n * @param fn - 要执行的异步函数。Async function to run.\n * @param keyExtractor - 从参数中提取 key 的函数。Function to extract key from args.\n * @returns 包装后的函数。Wrapped function.\n * @example const fetchUser = onceAsyncByKey((id: string) => api.getUser(id), (id) => id); fetchUser(\"1\"); fetchUser(\"1\"); // 同 id 只请求一次\n */\nexport function onceAsyncByKey<T, Args extends unknown[]>(fn: (...args: Args) => Promise<T>, keyExtractor: (...args: Args) => string) {\n const promises = new Map<string, Promise<T>>();\n\n return async (...args: Args) => {\n const key = keyExtractor(...args);\n const existingPromise = promises.get(key);\n if (existingPromise) return existingPromise;\n\n const promise = fn(...args).finally(() => promises.delete(key));\n promises.set(key, promise);\n return promise;\n };\n}\n","/**\n * Safe 工具:将 Nilable / Emptyable / Array 等规范为 Nullable、Maybe、Zeroable、Stringable、Array,避免到处写 ?? undefined / ?? null / ?? 0 / ?? \"\" / ?? []。\n * Safe utils: normalize Nilable/Emptyable/array to Nullable, Maybe, Zeroable, Stringable, Array.\n */\n\nimport type { Nullable, Maybe, Nilable, Emptyable, Zeroable, Stringable, Array } from \"@/types\";\n\n/**\n * 将 Nilable 规范为 Nullable:undefined 转为 null,null 与 T 原样返回。返回类型 Nullable&lt;T&gt;。\n * Normalize Nilable to Nullable: convert undefined to null; null and T unchanged. Returns Nullable&lt;T&gt;.\n * @param value - 可能为 null 或 undefined 的值 (value that may be null or undefined)\n * @returns 若为 undefined 则 null,否则原值 (null if undefined, otherwise value)\n * @example safeNullable(apiResponse.data) // undefined → null,便于区分「未设置」与「空」\n */\nexport function safeNullable<T>(value: Nilable<T>): Nullable<T> {\n return (value === undefined ? null : value) as Nullable<T>;\n}\n\n/**\n * 将 Nilable 规范为 Maybe:null 转为 undefined,undefined 与 T 原样返回。返回类型 Maybe&lt;T&gt;。等同 value ?? undefined。\n * Normalize Nilable to Maybe: convert null to undefined; undefined and T unchanged. Returns Maybe&lt;T&gt;. Same as value ?? undefined.\n * @param value - 可能为 null 或 undefined 的值 (value that may be null or undefined)\n * @returns 若为 null 或 undefined 则 undefined,否则原值 (undefined if null/undefined, otherwise value)\n * @example safeMaybe(product.price) // 代替 product.price ?? undefined\n */\nexport function safeMaybe<T>(value: Nilable<T>): Maybe<T> {\n return value ?? undefined;\n}\n\n/**\n * 将 Emptyable 字符串规范为 Nilable:空字符串转为 undefined,null/undefined/非空串原样。返回类型 Nilable&lt;T&gt;。常用于表单、接口字段。\n * Normalize Emptyable string to Nilable: \"\" to undefined; null, undefined, non-empty string unchanged. Returns Nilable&lt;T&gt;. For form/API fields.\n * @param value - 可能为 null、undefined 或空字符串 (value that may be null, undefined or \"\")\n * @returns 若为 \"\" 则 undefined,否则原值 (undefined if \"\", otherwise value)\n * @example safeNilable(product.remark) // \"\" 也视为「无值」\n */\nexport function safeNilable<T extends string>(value: Emptyable<T>): Nilable<T> {\n return (value === \"\" ? undefined : value) as Nilable<T>;\n}\n\n/**\n * 将 Nilable 数组规范为数组:null/undefined 转为 [] 或 defaultValue。返回类型 Array&lt;T&gt;(即 T[])。\n * Normalize Nilable array to array: null/undefined to [] or defaultValue. Returns Array&lt;T&gt; (i.e. T[]).\n * @param value - 可能为 null 或 undefined 的数组 (array that may be null or undefined)\n * @param defaultValue - 可选,当 value 为 null/undefined 时使用;缺省为 [] (optional default when value is null/undefined; default is [])\n * @returns value ?? defaultValue ?? [] (never null/undefined)\n * @example safeArray(product.tags) // product.tags ?? []\n * @example safeArray(product.tags, ['default'])\n */\nexport function safeArray<T>(value: Nilable<Array<T>>, defaultValue?: Array<T>): Array<T> {\n return (value ?? defaultValue ?? []) as Array<T>;\n}\n\n/**\n * 将 Nilable 数值规范为 Zeroable:null/undefined 转为 0。返回类型 Zeroable&lt;number&gt;(即 number,0 为兜底)。\n * Normalize Nilable number to Zeroable: null/undefined to 0. Returns Zeroable&lt;number&gt; (number with 0 as fallback).\n * @param value - 可能为 null 或 undefined 的数值 (number that may be null or undefined)\n * @returns value ?? 0 (never null/undefined)\n * @example safeZeroable(product.stock) // 代替 product.stock ?? 0\n */\nexport function safeZeroable(value: Nilable<number>): Zeroable<number> {\n return (value ?? 0) as Zeroable<number>;\n}\n\n/**\n * 将 Emptyable 字符串规范为 Stringable:null/undefined/\"\" 转为 \"\"。返回类型 Stringable&lt;string&gt;(即 string,\"\" 为兜底)。\n * Normalize Emptyable string to Stringable: null/undefined/\"\" to \"\". Returns Stringable&lt;string&gt; (string with \"\" as fallback).\n * @param value - 可能为 null、undefined 或空字符串 (value that may be null, undefined or \"\")\n * @returns 若为 null、undefined 或 \"\" 则 \"\",否则原值 (\"\" if null/undefined/\"\", otherwise value)\n * @example safeStringable(product.name) // 代替 product.name ?? \"\"\n */\nexport function safeStringable<T extends string>(value: Emptyable<T>): Stringable<T> {\n if (value === null || value === undefined || value === \"\") return \"\" as Stringable<T>;\n return value as Stringable<T>;\n}\n\n/**\n * 若值为 null/undefined 则返回默认值,否则返回原值。等同 value ?? defaultValue。\n * Return defaultValue when value is null/undefined, otherwise value. Same as value ?? defaultValue.\n * @param value - 可能为 null 或 undefined 的值 (value that may be null or undefined)\n * @param defaultValue - 默认值 (default value)\n * @returns value ?? defaultValue\n * @example safeOr(product.stock, 0)\n * @example safeOr(product.name, '')\n */\nexport function safeOr<T, D>(value: Nilable<T>, defaultValue: D): T | D {\n return value ?? defaultValue;\n}\n\n/**\n * 安全取数字:null/undefined/NaN 转为 undefined,合法数字原样。返回类型 Maybe&lt;number&gt;。\n * Safe number: null/undefined/NaN to undefined; valid number unchanged. Returns Maybe&lt;number&gt;.\n * @param value - 可能为 null、undefined 或非数字 (value that may be null, undefined or NaN)\n * @returns 若为 null、undefined 或 NaN 则 undefined,否则数字 (undefined if null/undefined/NaN, otherwise number)\n * @example safeNum(product.price) // 用于严格「有值才用」的场景\n */\nexport function safeNum(value: Nilable<number>): Maybe<number> {\n if (value === null || value === undefined) return undefined;\n const n = Number(value);\n return Number.isNaN(n) ? undefined : n;\n}\n","/** 可实例化的构造函数类型。Instantiable constructor type. */\ntype Constructor<T extends object = object> = new (...args: unknown[]) => T;\n\n/** 深度比较两个值是否相等(支持 primitives、数组、plain objects)。Deep equal for primitives, arrays, plain objects. */\nfunction isEqual(a: unknown, b: unknown): boolean {\n if (Object.is(a, b)) return true;\n if (typeof a !== typeof b) return false;\n if (a === null || b === null) return a === b;\n if (typeof a !== \"object\" || typeof b !== \"object\") return a === b;\n\n const arrA = Array.isArray(a);\n const arrB = Array.isArray(b);\n if (arrA !== arrB) return false;\n if (arrA && arrB) {\n if (a.length !== b.length) return false;\n return a.every((v, i) => isEqual(v, (b as unknown[])[i]));\n }\n\n const keysA = Object.keys(a as object).sort();\n const keysB = Object.keys(b as object).sort();\n if (keysA.length !== keysB.length) return false;\n if (!keysA.every((k, i) => k === keysB[i])) return false;\n return keysA.every((k) => isEqual((a as Record<string, unknown>)[k], (b as Record<string, unknown>)[k]));\n}\n\n/**\n * 单例工厂:将传入的构造函数包装为单例模式;Proxy 拦截 construct,多次 new 返回同一实例。\n * Singleton factory: wrap constructor so that multiple new calls return the same instance.\n *\n * @template T - 实例类型(需为 object)。Instance type (must be object).\n * @param className - 被包装的构造函数(class)。Constructor (class) to wrap.\n * @returns 代理后的构造函数,调用时始终返回同一实例。Proxied constructor returning same instance.\n *\n * @example\n * ```ts\n * class MyService { ... }\n * const SingletonService = singleton(MyService);\n * const a = new SingletonService();\n * const b = new SingletonService();\n * console.log(a === b); // true\n * ```\n */\nexport const singleton = <T extends object>(className: Constructor<T>): Constructor<T> => {\n let instance: T | undefined;\n let parameters: unknown[] = [];\n return new Proxy(className, {\n construct(_target: Constructor<T>, args: unknown[]): T {\n if (!instance) {\n instance = new className(...args);\n parameters = args;\n }\n if (!isEqual(args, parameters)) {\n throw new Error(\"Cannot create multiple instances with different parameters\");\n }\n return instance;\n },\n }) as Constructor<T>;\n};\n","import type { Nilable } from \"@/types\";\n\n/**\n * 若值为 null/undefined 则抛出 Promise 使 React Suspense 挂起,否则返回该值。\n * If value is null/undefined, throw a promise to suspend (React Suspense); otherwise return value.\n * @param value - 待检查的值。Value to check.\n * @param delay - 重试前的延迟(毫秒)。Delay in ms before retry.\n * @returns 非空时的 value。Value when non-null.\n * @example const data = suspenseIfNull(resource); // 在 Suspense 边界内,data 非空时才渲染\n */\nexport function suspenseIfNull<T>(value: Nilable<T>, delay = 100): T {\n if (value == null) {\n throw new Promise<void>((resolve) => setTimeout(resolve, delay));\n }\n return value;\n}\n","/**\n * 格式化日期为 YYYY/MM/DD(空或无效返回 \"\")\n * Format date as YYYY/MM/DD; empty or invalid returns \"\".\n * @param dateString - 日期字符串\n * @returns 格式化后的日期字符串,无效则 \"\"\n */\nexport const formatDisplayDate = (dateString: string | undefined | null): string => {\n if (!dateString) return \"\";\n const date = new Date(dateString);\n if (isNaN(date.getTime())) return \"\";\n const year = date.getFullYear();\n const month = (date.getMonth() + 1).toString().padStart(2, \"0\");\n const day = date.getDate().toString().padStart(2, \"0\");\n return `${year}/${month}/${day}`;\n};\n\n/**\n * 格式化为相对时间(如 2 days ago, 3 months ago)\n * Format as relative time (e.g. 2 days ago, 3 months ago).\n * @param dateString - 日期字符串\n * @returns 相对时间字符串,无效为 \"Invalid date\",空为 \"Unknown\"\n */\nexport const formatRelativeTime = (dateString: string | undefined | null): string => {\n if (!dateString) return \"Unknown\";\n const date = new Date(dateString);\n if (isNaN(date.getTime())) return \"Invalid date\";\n\n const now = new Date();\n const diffMs = now.getTime() - date.getTime();\n const diffSeconds = Math.floor(diffMs / 1000);\n const diffMinutes = Math.floor(diffSeconds / 60);\n const diffHours = Math.floor(diffMinutes / 60);\n const diffDays = Math.floor(diffHours / 24);\n const diffMonths = Math.floor(diffDays / 30);\n const diffYears = Math.floor(diffDays / 365);\n\n if (diffYears > 0) return `${diffYears} year${diffYears > 1 ? \"s\" : \"\"} ago`;\n if (diffMonths > 0) return `${diffMonths} month${diffMonths > 1 ? \"s\" : \"\"} ago`;\n if (diffDays > 0) return `${diffDays} day${diffDays > 1 ? \"s\" : \"\"} ago`;\n if (diffHours > 0) return `${diffHours} hour${diffHours > 1 ? \"s\" : \"\"} ago`;\n if (diffMinutes > 0) return `${diffMinutes} minute${diffMinutes > 1 ? \"s\" : \"\"} ago`;\n return \"Just now\";\n};\n\n/**\n * 格式化 ISO 日期为图表轴/提示用短格式(如:Jan 4, 02:30 PM)\n * Format ISO date for chart axis/tooltip (e.g. Jan 4, 02:30 PM).\n * @param iso - ISO 日期字符串\n * @returns 格式化字符串,解析失败则返回原串\n */\nexport function formatTickDate(iso: string): string {\n try {\n const d = new Date(iso);\n return d.toLocaleDateString(undefined, {\n month: \"short\",\n day: \"numeric\",\n hour: \"2-digit\",\n minute: \"2-digit\",\n });\n } catch {\n return iso;\n }\n}\n\n/**\n * 格式化时间显示年月日时分 (YYYY/MM/DD HH:mm)\n * Format date-time as year/month/day hour:minute.\n * @param dateString - 时间字符串\n * @returns 格式化后的时间字符串,无效则返回原串\n */\nexport const formatDateTime = (dateString: string): string => {\n try {\n const date = new Date(dateString);\n if (isNaN(date.getTime())) return dateString;\n const year = date.getFullYear();\n const month = String(date.getMonth() + 1).padStart(2, \"0\");\n const day = String(date.getDate()).padStart(2, \"0\");\n const hour = String(date.getHours()).padStart(2, \"0\");\n const minute = String(date.getMinutes()).padStart(2, \"0\");\n return `${year}/${month}/${day} ${hour}:${minute}`;\n } catch {\n return dateString;\n }\n};\n\n/**\n * 格式化时间显示月日时分 (MM/DD HH:mm)\n * Format date-time as month/day hour:minute.\n * @param dateString - 时间字符串\n * @returns 格式化后的时间字符串,无效则返回原串\n */\nexport const formatMonthDayTime = (dateString: string): string => {\n try {\n const date = new Date(dateString);\n if (isNaN(date.getTime())) return dateString;\n const month = String(date.getMonth() + 1).padStart(2, \"0\");\n const day = String(date.getDate()).padStart(2, \"0\");\n const hour = String(date.getHours()).padStart(2, \"0\");\n const minute = String(date.getMinutes()).padStart(2, \"0\");\n return `${month}/${day} ${hour}:${minute}`;\n } catch {\n return dateString;\n }\n};\n\n/**\n * 格式化日期仅年月日 (YYYY/MM/DD),用于生日等\n * Format date as year/month/day only.\n * @param dateString - 日期字符串,如 \"2000-01-15\" 或 ISO\n * @returns 格式化后的日期字符串,无效则返回原串\n */\nexport const formatDate = (dateString: string): string => {\n try {\n const date = new Date(dateString);\n if (isNaN(date.getTime())) return dateString;\n const year = date.getFullYear();\n const month = String(date.getMonth() + 1).padStart(2, \"0\");\n const day = String(date.getDate()).padStart(2, \"0\");\n return `${year}/${month}/${day}`;\n } catch {\n return dateString;\n }\n};\n\n/**\n * 格式化时间显示时分 (HH:mm)\n * Format time as hour:minute.\n * @param dateString - 时间字符串\n * @returns 格式化后的时间字符串,无效则返回原串\n */\nexport const formatTime = (dateString: string): string => {\n try {\n const date = new Date(dateString);\n if (isNaN(date.getTime())) return dateString;\n const hour = String(date.getHours()).padStart(2, \"0\");\n const minute = String(date.getMinutes()).padStart(2, \"0\");\n return `${hour}:${minute}`;\n } catch {\n return dateString;\n }\n};\n","/**\n * 从字符串列表创建键值映射对象;可选 valueMapper 指定值\n * Create record from string list; optional valueMapper for values.\n * @param list - 字符串数组(作为键)\n * @param valueMapper - 可选,(item, index) => value,缺省时值为键本身\n * @returns Record<list[number], V>\n * @example createMap([\"a\",\"b\",\"c\"]) // { a: \"a\", b: \"b\", c: \"c\" }\n * @example createMap([\"a\",\"b\",\"c\"], (_, i) => i) // { a: 0, b: 1, c: 2 }\n */\nexport function createMap<T extends string, V = T>(list: ReadonlyArray<T>, valueMapper?: (item: T, index: number) => V): Record<T, V> {\n return list.reduce(\n (acc, item, index) => {\n acc[item] = valueMapper ? valueMapper(item, index) : (item as unknown as V);\n return acc;\n },\n {} as Record<T, V>,\n );\n}\n"],"mappings":"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"}