@miaixz/sdk 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (244) hide show
  1. package/LICENSE +202 -0
  2. package/NOTICE +7 -0
  3. package/README.md +412 -0
  4. package/dist/api/client.d.ts +223 -0
  5. package/dist/api/client.d.ts.map +1 -0
  6. package/dist/api/client.js +1069 -0
  7. package/dist/api/client.js.map +1 -0
  8. package/dist/api/endpoint.d.ts +10 -0
  9. package/dist/api/endpoint.d.ts.map +1 -0
  10. package/dist/api/endpoint.js +51 -0
  11. package/dist/api/endpoint.js.map +1 -0
  12. package/dist/api/errors.d.ts +180 -0
  13. package/dist/api/errors.d.ts.map +1 -0
  14. package/dist/api/errors.js +173 -0
  15. package/dist/api/errors.js.map +1 -0
  16. package/dist/api/index.d.ts +6 -0
  17. package/dist/api/index.d.ts.map +1 -0
  18. package/dist/api/index.js +5 -0
  19. package/dist/api/index.js.map +1 -0
  20. package/dist/api/request.d.ts +135 -0
  21. package/dist/api/request.d.ts.map +1 -0
  22. package/dist/api/request.js +2 -0
  23. package/dist/api/request.js.map +1 -0
  24. package/dist/api/response.d.ts +69 -0
  25. package/dist/api/response.d.ts.map +1 -0
  26. package/dist/api/response.js +43 -0
  27. package/dist/api/response.js.map +1 -0
  28. package/dist/api/telemetry.d.ts +139 -0
  29. package/dist/api/telemetry.d.ts.map +1 -0
  30. package/dist/api/telemetry.js +2 -0
  31. package/dist/api/telemetry.js.map +1 -0
  32. package/dist/appearance/appearance.d.ts +161 -0
  33. package/dist/appearance/appearance.d.ts.map +1 -0
  34. package/dist/appearance/appearance.js +614 -0
  35. package/dist/appearance/appearance.js.map +1 -0
  36. package/dist/appearance/index.d.ts +3 -0
  37. package/dist/appearance/index.d.ts.map +1 -0
  38. package/dist/appearance/index.js +3 -0
  39. package/dist/appearance/index.js.map +1 -0
  40. package/dist/auth/auth.d.ts +197 -0
  41. package/dist/auth/auth.d.ts.map +1 -0
  42. package/dist/auth/auth.js +314 -0
  43. package/dist/auth/auth.js.map +1 -0
  44. package/dist/auth/index.d.ts +2 -0
  45. package/dist/auth/index.d.ts.map +1 -0
  46. package/dist/auth/index.js +2 -0
  47. package/dist/auth/index.js.map +1 -0
  48. package/dist/config/config.d.ts +117 -0
  49. package/dist/config/config.d.ts.map +1 -0
  50. package/dist/config/config.js +241 -0
  51. package/dist/config/config.js.map +1 -0
  52. package/dist/config/index.d.ts +2 -0
  53. package/dist/config/index.d.ts.map +1 -0
  54. package/dist/config/index.js +2 -0
  55. package/dist/config/index.js.map +1 -0
  56. package/dist/consts/index.d.ts +35 -0
  57. package/dist/consts/index.d.ts.map +1 -0
  58. package/dist/consts/index.js +35 -0
  59. package/dist/consts/index.js.map +1 -0
  60. package/dist/context/context.d.ts +123 -0
  61. package/dist/context/context.d.ts.map +1 -0
  62. package/dist/context/context.js +252 -0
  63. package/dist/context/context.js.map +1 -0
  64. package/dist/context/index.d.ts +2 -0
  65. package/dist/context/index.d.ts.map +1 -0
  66. package/dist/context/index.js +2 -0
  67. package/dist/context/index.js.map +1 -0
  68. package/dist/contracts/host-context.d.ts +132 -0
  69. package/dist/contracts/host-context.d.ts.map +1 -0
  70. package/dist/contracts/host-context.js +2 -0
  71. package/dist/contracts/host-context.js.map +1 -0
  72. package/dist/contracts/index.d.ts +8 -0
  73. package/dist/contracts/index.d.ts.map +1 -0
  74. package/dist/contracts/index.js +2 -0
  75. package/dist/contracts/index.js.map +1 -0
  76. package/dist/contracts/lifecycle.d.ts +49 -0
  77. package/dist/contracts/lifecycle.d.ts.map +1 -0
  78. package/dist/contracts/lifecycle.js +2 -0
  79. package/dist/contracts/lifecycle.js.map +1 -0
  80. package/dist/contracts/module-manifest.d.ts +110 -0
  81. package/dist/contracts/module-manifest.d.ts.map +1 -0
  82. package/dist/contracts/module-manifest.js +317 -0
  83. package/dist/contracts/module-manifest.js.map +1 -0
  84. package/dist/contracts/navigation.d.ts +36 -0
  85. package/dist/contracts/navigation.d.ts.map +1 -0
  86. package/dist/contracts/navigation.js +11 -0
  87. package/dist/contracts/navigation.js.map +1 -0
  88. package/dist/contracts/permission.d.ts +8 -0
  89. package/dist/contracts/permission.d.ts.map +1 -0
  90. package/dist/contracts/permission.js +14 -0
  91. package/dist/contracts/permission.js.map +1 -0
  92. package/dist/contracts/post-message.d.ts +124 -0
  93. package/dist/contracts/post-message.d.ts.map +1 -0
  94. package/dist/contracts/post-message.js +2 -0
  95. package/dist/contracts/post-message.js.map +1 -0
  96. package/dist/contracts/route.d.ts +45 -0
  97. package/dist/contracts/route.d.ts.map +1 -0
  98. package/dist/contracts/route.js +35 -0
  99. package/dist/contracts/route.js.map +1 -0
  100. package/dist/events/events.d.ts +191 -0
  101. package/dist/events/events.d.ts.map +1 -0
  102. package/dist/events/events.js +486 -0
  103. package/dist/events/events.js.map +1 -0
  104. package/dist/events/index.d.ts +2 -0
  105. package/dist/events/index.d.ts.map +1 -0
  106. package/dist/events/index.js +2 -0
  107. package/dist/events/index.js.map +1 -0
  108. package/dist/files/files.d.ts +138 -0
  109. package/dist/files/files.d.ts.map +1 -0
  110. package/dist/files/files.js +144 -0
  111. package/dist/files/files.js.map +1 -0
  112. package/dist/files/index.d.ts +2 -0
  113. package/dist/files/index.d.ts.map +1 -0
  114. package/dist/files/index.js +2 -0
  115. package/dist/files/index.js.map +1 -0
  116. package/dist/formatters/index.d.ts +44 -0
  117. package/dist/formatters/index.d.ts.map +1 -0
  118. package/dist/formatters/index.js +50 -0
  119. package/dist/formatters/index.js.map +1 -0
  120. package/dist/i18n/default-translator.d.ts +17 -0
  121. package/dist/i18n/default-translator.d.ts.map +1 -0
  122. package/dist/i18n/default-translator.js +19 -0
  123. package/dist/i18n/default-translator.js.map +1 -0
  124. package/dist/i18n/i18n.d.ts +248 -0
  125. package/dist/i18n/i18n.d.ts.map +1 -0
  126. package/dist/i18n/i18n.js +617 -0
  127. package/dist/i18n/i18n.js.map +1 -0
  128. package/dist/i18n/index.d.ts +2 -0
  129. package/dist/i18n/index.d.ts.map +1 -0
  130. package/dist/i18n/index.js +2 -0
  131. package/dist/i18n/index.js.map +1 -0
  132. package/dist/index.d.ts +19 -0
  133. package/dist/index.d.ts.map +1 -0
  134. package/dist/index.js +19 -0
  135. package/dist/index.js.map +1 -0
  136. package/dist/models/index.d.ts +2 -0
  137. package/dist/models/index.d.ts.map +1 -0
  138. package/dist/models/index.js +2 -0
  139. package/dist/models/index.js.map +1 -0
  140. package/dist/permissions/index.d.ts +2 -0
  141. package/dist/permissions/index.d.ts.map +1 -0
  142. package/dist/permissions/index.js +2 -0
  143. package/dist/permissions/index.js.map +1 -0
  144. package/dist/permissions/permissions.d.ts +67 -0
  145. package/dist/permissions/permissions.d.ts.map +1 -0
  146. package/dist/permissions/permissions.js +123 -0
  147. package/dist/permissions/permissions.js.map +1 -0
  148. package/dist/runtime/direct-host-bridge.d.ts +11 -0
  149. package/dist/runtime/direct-host-bridge.d.ts.map +1 -0
  150. package/dist/runtime/direct-host-bridge.js +299 -0
  151. package/dist/runtime/direct-host-bridge.js.map +1 -0
  152. package/dist/runtime/index.d.ts +5 -0
  153. package/dist/runtime/index.d.ts.map +1 -0
  154. package/dist/runtime/index.js +3 -0
  155. package/dist/runtime/index.js.map +1 -0
  156. package/dist/runtime/post-message-bridge.d.ts +20 -0
  157. package/dist/runtime/post-message-bridge.d.ts.map +1 -0
  158. package/dist/runtime/post-message-bridge.js +1115 -0
  159. package/dist/runtime/post-message-bridge.js.map +1 -0
  160. package/dist/sdk.d.ts +177 -0
  161. package/dist/sdk.d.ts.map +1 -0
  162. package/dist/sdk.js +214 -0
  163. package/dist/sdk.js.map +1 -0
  164. package/dist/storage/index.d.ts +2 -0
  165. package/dist/storage/index.d.ts.map +1 -0
  166. package/dist/storage/index.js +2 -0
  167. package/dist/storage/index.js.map +1 -0
  168. package/dist/storage/storage.d.ts +246 -0
  169. package/dist/storage/storage.d.ts.map +1 -0
  170. package/dist/storage/storage.js +358 -0
  171. package/dist/storage/storage.js.map +1 -0
  172. package/dist/types/api.d.ts +82 -0
  173. package/dist/types/api.d.ts.map +1 -0
  174. package/dist/types/api.js +2 -0
  175. package/dist/types/api.js.map +1 -0
  176. package/dist/types/appearance.d.ts +85 -0
  177. package/dist/types/appearance.d.ts.map +1 -0
  178. package/dist/types/appearance.js +48 -0
  179. package/dist/types/appearance.js.map +1 -0
  180. package/dist/types/config.d.ts +49 -0
  181. package/dist/types/config.d.ts.map +1 -0
  182. package/dist/types/config.js +2 -0
  183. package/dist/types/config.js.map +1 -0
  184. package/dist/types/context.d.ts +41 -0
  185. package/dist/types/context.d.ts.map +1 -0
  186. package/dist/types/context.js +2 -0
  187. package/dist/types/context.js.map +1 -0
  188. package/dist/types/file.d.ts +48 -0
  189. package/dist/types/file.d.ts.map +1 -0
  190. package/dist/types/file.js +2 -0
  191. package/dist/types/file.js.map +1 -0
  192. package/dist/types/index.d.ts +12 -0
  193. package/dist/types/index.d.ts.map +1 -0
  194. package/dist/types/index.js +12 -0
  195. package/dist/types/index.js.map +1 -0
  196. package/dist/types/organization.d.ts +56 -0
  197. package/dist/types/organization.d.ts.map +1 -0
  198. package/dist/types/organization.js +2 -0
  199. package/dist/types/organization.js.map +1 -0
  200. package/dist/types/pagination.d.ts +55 -0
  201. package/dist/types/pagination.d.ts.map +1 -0
  202. package/dist/types/pagination.js +13 -0
  203. package/dist/types/pagination.js.map +1 -0
  204. package/dist/types/permissions.d.ts +26 -0
  205. package/dist/types/permissions.d.ts.map +1 -0
  206. package/dist/types/permissions.js +2 -0
  207. package/dist/types/permissions.js.map +1 -0
  208. package/dist/types/space.d.ts +43 -0
  209. package/dist/types/space.d.ts.map +1 -0
  210. package/dist/types/space.js +2 -0
  211. package/dist/types/space.js.map +1 -0
  212. package/dist/types/tenant.d.ts +31 -0
  213. package/dist/types/tenant.d.ts.map +1 -0
  214. package/dist/types/tenant.js +2 -0
  215. package/dist/types/tenant.js.map +1 -0
  216. package/dist/types/user.d.ts +48 -0
  217. package/dist/types/user.d.ts.map +1 -0
  218. package/dist/types/user.js +2 -0
  219. package/dist/types/user.js.map +1 -0
  220. package/dist/utils/date.d.ts +9 -0
  221. package/dist/utils/date.d.ts.map +1 -0
  222. package/dist/utils/date.js +11 -0
  223. package/dist/utils/date.js.map +1 -0
  224. package/dist/utils/index.d.ts +5 -0
  225. package/dist/utils/index.d.ts.map +1 -0
  226. package/dist/utils/index.js +5 -0
  227. package/dist/utils/index.js.map +1 -0
  228. package/dist/utils/number.d.ts +11 -0
  229. package/dist/utils/number.d.ts.map +1 -0
  230. package/dist/utils/number.js +13 -0
  231. package/dist/utils/number.js.map +1 -0
  232. package/dist/utils/object.d.ts +9 -0
  233. package/dist/utils/object.d.ts.map +1 -0
  234. package/dist/utils/object.js +11 -0
  235. package/dist/utils/object.js.map +1 -0
  236. package/dist/utils/string.d.ts +9 -0
  237. package/dist/utils/string.d.ts.map +1 -0
  238. package/dist/utils/string.js +11 -0
  239. package/dist/utils/string.js.map +1 -0
  240. package/dist/validators/index.d.ts +8 -0
  241. package/dist/validators/index.d.ts.map +1 -0
  242. package/dist/validators/index.js +8 -0
  243. package/dist/validators/index.js.map +1 -0
  244. package/package.json +156 -0
@@ -0,0 +1,614 @@
1
+ import { MiaixzSdkError } from "../api/errors.js";
2
+ import { translateMiaixzDefaultMessage } from "../i18n/default-translator.js";
3
+ import { getMiaixzBrowserStorage, readMiaixzVersionedValue, writeMiaixzVersionedValue, } from "../storage/index.js";
4
+ import { miaixzColorModes, miaixzDensities, miaixzThemeColorTokens, } from "../types/index.js";
5
+ /**
6
+ * Identifies the current appearance persistence and event schema.
7
+ */
8
+ const miaixzAppearanceSchemaVersion = 1;
9
+ /**
10
+ * Lists color tokens that may include an alpha channel.
11
+ */
12
+ const miaixzAlphaThemeColorTokens = new Set([
13
+ "backdrop",
14
+ "shadow",
15
+ "shadow-strong",
16
+ "selection",
17
+ ]);
18
+ /**
19
+ * Lists the exact top-level keys accepted by appearance settings.
20
+ */
21
+ const miaixzAppearanceKeys = new Set(["colorMode", "density", "colors"]);
22
+ /**
23
+ * Lists all configurable color tokens for runtime membership checks.
24
+ */
25
+ const miaixzThemeColorTokenSet = new Set(miaixzThemeColorTokens);
26
+ /**
27
+ * Lists foreground and background pairs that require normal-text contrast.
28
+ */
29
+ const miaixzNormalContrastPairs = [
30
+ ["text-primary", "background"],
31
+ ["text-secondary", "background"],
32
+ ["text-muted", "background"],
33
+ ["on-brand", "brand"],
34
+ ["success", "success-soft"],
35
+ ["warning", "warning-soft"],
36
+ ["danger", "danger-soft"],
37
+ ["info", "info-soft"],
38
+ ];
39
+ /**
40
+ * Lists graphical-object pairs that require non-text contrast.
41
+ */
42
+ const miaixzGraphicalContrastPairs = [
43
+ ["focus", "background"],
44
+ ["focus", "surface"],
45
+ ["border-strong", "background"],
46
+ ["border-strong", "surface"],
47
+ ];
48
+ /**
49
+ * Defines the complete built-in light theme used for validation and CSS mirroring.
50
+ *
51
+ * @public
52
+ */
53
+ export const miaixzLightThemeColors = Object.freeze({
54
+ brand: "#58B832",
55
+ "on-brand": "#10160D",
56
+ background: "#F8FAF7",
57
+ surface: "#FFFFFF",
58
+ "surface-secondary": "#F3F6F1",
59
+ "surface-hover": "#EEF3EB",
60
+ "surface-active": "#E6EDE2",
61
+ "surface-selected": "#EBF8E7",
62
+ "text-primary": "#1D211B",
63
+ "text-secondary": "#667061",
64
+ "text-muted": "#6B7567",
65
+ "text-disabled": "#9DA69A",
66
+ "text-inverse": "#F8FAF7",
67
+ border: "#DCE4D8",
68
+ "border-strong": "#74806F",
69
+ focus: "#3F8F22",
70
+ success: "#267A39",
71
+ "success-soft": "#EEF8F0",
72
+ warning: "#8A5500",
73
+ "warning-soft": "#FFF6E5",
74
+ danger: "#B03030",
75
+ "danger-soft": "#FCEEEE",
76
+ info: "#2568B5",
77
+ "info-soft": "#EDF5FD",
78
+ backdrop: "#11180F7A",
79
+ shadow: "#1D211B1A",
80
+ "shadow-strong": "#1D211B2E",
81
+ selection: "#E1F2DA",
82
+ });
83
+ /**
84
+ * Defines the complete built-in dark theme used for validation and CSS mirroring.
85
+ *
86
+ * @public
87
+ */
88
+ export const miaixzDarkThemeColors = Object.freeze({
89
+ brand: "#6BC548",
90
+ "on-brand": "#10160D",
91
+ background: "#121510",
92
+ surface: "#191D17",
93
+ "surface-secondary": "#20251E",
94
+ "surface-hover": "#272D24",
95
+ "surface-active": "#30372C",
96
+ "surface-selected": "#233A1C",
97
+ "text-primary": "#EDF1EB",
98
+ "text-secondary": "#B6C0B2",
99
+ "text-muted": "#929D8E",
100
+ "text-disabled": "#6F796C",
101
+ "text-inverse": "#161A14",
102
+ border: "#343C31",
103
+ "border-strong": "#697A64",
104
+ focus: "#8ADB69",
105
+ success: "#7AD18C",
106
+ "success-soft": "#18321F",
107
+ warning: "#E5B45F",
108
+ "warning-soft": "#382A16",
109
+ danger: "#F08A8A",
110
+ "danger-soft": "#3A1F1F",
111
+ info: "#8ABCF2",
112
+ "info-soft": "#192C40",
113
+ backdrop: "#000000A3",
114
+ shadow: "#00000047",
115
+ "shadow-strong": "#00000070",
116
+ selection: "#294A20",
117
+ });
118
+ /**
119
+ * Defines the default Miaixz appearance settings.
120
+ *
121
+ * @public
122
+ */
123
+ export const miaixzDefaultAppearance = Object.freeze({
124
+ colorMode: "system",
125
+ density: "standard",
126
+ });
127
+ /**
128
+ * Determines whether a value is a supported Miaixz color mode.
129
+ *
130
+ * @param value - Value to inspect.
131
+ * @returns Whether the value is a supported color mode.
132
+ * @public
133
+ */
134
+ export function isMiaixzColorMode(value) {
135
+ return typeof value === "string" && miaixzColorModes.includes(value);
136
+ }
137
+ /**
138
+ * Determines whether a value is a supported Miaixz density.
139
+ *
140
+ * @param value - Value to inspect.
141
+ * @returns Whether the value is a supported density.
142
+ * @public
143
+ */
144
+ export function isMiaixzDensity(value) {
145
+ return typeof value === "string" && miaixzDensities.includes(value);
146
+ }
147
+ /**
148
+ * Parses, normalizes, validates, and freezes appearance settings.
149
+ *
150
+ * @param value - Untrusted appearance settings to parse.
151
+ * @returns A deeply frozen settings snapshot with uppercase custom colors.
152
+ * @throws MiaixzSdkError When object shape, values, colors, or contrast are invalid.
153
+ * @public
154
+ */
155
+ export function parseMiaixzAppearanceSettings(value) {
156
+ return parseAppearanceSettings(value, translateMiaixzDefaultMessage);
157
+ }
158
+ /**
159
+ * Determines whether a value satisfies the complete appearance contract.
160
+ *
161
+ * @param value - Untrusted appearance settings to inspect.
162
+ * @returns Whether parsing and contrast validation both succeed.
163
+ * @public
164
+ */
165
+ export function isMiaixzAppearanceSettings(value) {
166
+ try {
167
+ parseMiaixzAppearanceSettings(value);
168
+ return true;
169
+ }
170
+ catch {
171
+ return false;
172
+ }
173
+ }
174
+ /**
175
+ * Validates all frozen WCAG contrast pairs for an appearance.
176
+ *
177
+ * @param appearance - Appearance settings whose merged themes are validated.
178
+ * @throws MiaixzSdkError When a custom color is invalid or a pair misses its threshold.
179
+ * @public
180
+ */
181
+ export function validateMiaixzThemeContrast(appearance) {
182
+ const normalized = normalizeAppearanceSettings(appearance, translateMiaixzDefaultMessage);
183
+ validateNormalizedThemeContrast(normalized, translateMiaixzDefaultMessage);
184
+ }
185
+ /**
186
+ * Manages versioned, tenant-scoped appearance state without accessing the DOM.
187
+ *
188
+ * @public
189
+ */
190
+ export class MiaixzAppearanceManager {
191
+ #appId;
192
+ #storage;
193
+ #migrations;
194
+ #events;
195
+ #translate;
196
+ #listeners = new Set();
197
+ #tenantId;
198
+ #appearance;
199
+ #stopEventListener;
200
+ #destroyed = false;
201
+ #publishingEvent = false;
202
+ /**
203
+ * Creates a validated appearance manager for one application and tenant scope.
204
+ *
205
+ * @param options - Required application identity and optional runtime adapters.
206
+ * @throws MiaixzSdkError When scope, migrations, initial settings, or contrast are invalid.
207
+ */
208
+ constructor(options) {
209
+ this.#appId = options.appId;
210
+ this.#tenantId = options.tenantId;
211
+ this.#storage = options.storage ?? getMiaixzBrowserStorage();
212
+ this.#migrations = options.migrations;
213
+ this.#events = options.events;
214
+ this.#translate = options.translate ?? translateMiaixzDefaultMessage;
215
+ const scope = this.#createScope(this.#tenantId);
216
+ this.#validateStorageConfiguration(scope);
217
+ const initial = options.initialAppearance === undefined
218
+ ? undefined
219
+ : parseAppearanceSettings(options.initialAppearance, this.#translate);
220
+ const persisted = this.#read(scope);
221
+ this.#appearance = parseAppearanceSettings({
222
+ ...miaixzDefaultAppearance,
223
+ ...persisted,
224
+ ...initial,
225
+ }, this.#translate);
226
+ this.#stopEventListener = this.#events?.on("appearance:changed", (payload) => {
227
+ if (this.#publishingEvent)
228
+ return;
229
+ const appearance = parseAppearancePayload(payload, this.#translate);
230
+ if (appearance !== undefined)
231
+ this.#commit(appearance, false);
232
+ });
233
+ }
234
+ /**
235
+ * Returns the current deeply frozen appearance snapshot.
236
+ *
237
+ * @returns Current normalized appearance settings.
238
+ */
239
+ getSnapshot() {
240
+ return this.#appearance;
241
+ }
242
+ /**
243
+ * Replaces the complete appearance after atomic validation.
244
+ *
245
+ * @param appearance - Complete appearance settings to commit.
246
+ * @throws MiaixzSdkError When settings, colors, or contrast are invalid.
247
+ */
248
+ set(appearance) {
249
+ this.#commit(parseAppearanceSettings(appearance, this.#translate), true);
250
+ }
251
+ /**
252
+ * Shallowly merges and commits a partial appearance update.
253
+ *
254
+ * @param appearance - Top-level settings fields to replace.
255
+ * @throws MiaixzSdkError When merged settings, colors, or contrast are invalid.
256
+ */
257
+ patch(appearance) {
258
+ this.set({ ...this.#appearance, ...appearance });
259
+ }
260
+ /**
261
+ * Reloads appearance state from a new validated tenant scope.
262
+ *
263
+ * @param tenantId - Optional tenant identifier, or undefined for global scope.
264
+ * @throws MiaixzSdkError When the new scope or migration configuration is invalid.
265
+ */
266
+ setScope(tenantId) {
267
+ const scope = this.#createScope(tenantId);
268
+ this.#validateStorageConfiguration(scope);
269
+ const appearance = this.#read(scope) ?? miaixzDefaultAppearance;
270
+ this.#tenantId = tenantId;
271
+ this.#appearance = appearance;
272
+ this.#notify();
273
+ }
274
+ /**
275
+ * Registers a listener for committed appearance snapshots.
276
+ *
277
+ * @param listener - Callback invoked synchronously after each committed change.
278
+ * @returns An idempotent function that removes the listener.
279
+ */
280
+ subscribe(listener) {
281
+ this.#listeners.add(listener);
282
+ let subscribed = true;
283
+ return () => {
284
+ if (!subscribed)
285
+ return;
286
+ subscribed = false;
287
+ this.#listeners.delete(listener);
288
+ };
289
+ }
290
+ /**
291
+ * Releases the event subscription and all local appearance listeners.
292
+ */
293
+ destroy() {
294
+ if (this.#destroyed)
295
+ return;
296
+ this.#destroyed = true;
297
+ this.#stopEventListener?.();
298
+ this.#stopEventListener = undefined;
299
+ this.#listeners.clear();
300
+ }
301
+ /**
302
+ * Creates the current versioned-storage scope.
303
+ *
304
+ * @param tenantId - Optional tenant identifier.
305
+ * @returns Application and tenant storage scope.
306
+ */
307
+ #createScope(tenantId) {
308
+ return tenantId === undefined ? { appId: this.#appId } : { appId: this.#appId, tenantId };
309
+ }
310
+ /**
311
+ * Validates scope, schema, and migrations before reading physical storage.
312
+ *
313
+ * @param scope - Candidate appearance persistence scope.
314
+ * @throws MiaixzSdkError When scope or migrations are invalid.
315
+ */
316
+ #validateStorageConfiguration(scope) {
317
+ readMiaixzVersionedValue({
318
+ scope,
319
+ kind: "appearance",
320
+ schemaVersion: miaixzAppearanceSchemaVersion,
321
+ ...(this.#migrations === undefined ? {} : { migrations: this.#migrations }),
322
+ parse: (value) => parseAppearanceSettings(value, this.#translate),
323
+ });
324
+ }
325
+ /**
326
+ * Reads and normalizes one tenant-scoped appearance value.
327
+ *
328
+ * @param scope - Validated appearance persistence scope.
329
+ * @returns Persisted normalized appearance, or undefined when absent or unusable.
330
+ */
331
+ #read(scope) {
332
+ return readMiaixzVersionedValue({
333
+ ...(this.#storage === undefined ? {} : { storage: this.#storage }),
334
+ scope,
335
+ kind: "appearance",
336
+ schemaVersion: miaixzAppearanceSchemaVersion,
337
+ ...(this.#migrations === undefined ? {} : { migrations: this.#migrations }),
338
+ parse: (value) => parseAppearanceSettings(value, this.#translate),
339
+ });
340
+ }
341
+ /**
342
+ * Commits one already-normalized appearance and optionally broadcasts it.
343
+ *
344
+ * @param appearance - Deeply frozen settings to commit.
345
+ * @param broadcast - Whether to emit the versioned appearance event.
346
+ */
347
+ #commit(appearance, broadcast) {
348
+ this.#appearance = appearance;
349
+ const scope = this.#createScope(this.#tenantId);
350
+ writeMiaixzVersionedValue({
351
+ ...(this.#storage === undefined ? {} : { storage: this.#storage }),
352
+ scope,
353
+ kind: "appearance",
354
+ schemaVersion: miaixzAppearanceSchemaVersion,
355
+ }, appearance);
356
+ this.#notify();
357
+ if (broadcast) {
358
+ const payload = Object.freeze({
359
+ schemaVersion: miaixzAppearanceSchemaVersion,
360
+ value: appearance,
361
+ });
362
+ this.#publishingEvent = true;
363
+ try {
364
+ this.#events?.emit("appearance:changed", payload);
365
+ }
366
+ finally {
367
+ this.#publishingEvent = false;
368
+ }
369
+ }
370
+ }
371
+ /**
372
+ * Delivers the current immutable snapshot to all local listeners.
373
+ */
374
+ #notify() {
375
+ for (const listener of this.#listeners)
376
+ listener(this.#appearance);
377
+ }
378
+ }
379
+ /**
380
+ * Creates a tenant-aware appearance manager without DOM side effects.
381
+ *
382
+ * @param options - Required application identity and optional runtime adapters.
383
+ * @returns Configured appearance manager.
384
+ * @throws MiaixzSdkError When scope, migrations, initial settings, or contrast are invalid.
385
+ * @public
386
+ */
387
+ export function createMiaixzAppearanceManager(options) {
388
+ return new MiaixzAppearanceManager(options);
389
+ }
390
+ /**
391
+ * Parses appearance settings using the supplied translator.
392
+ *
393
+ * @param value - Untrusted appearance settings.
394
+ * @param translate - Translator used for validation errors.
395
+ * @returns Deeply frozen normalized settings.
396
+ * @throws MiaixzSdkError When syntax, colors, or contrast are invalid.
397
+ */
398
+ function parseAppearanceSettings(value, translate) {
399
+ const normalized = normalizeAppearanceSettings(value, translate);
400
+ validateNormalizedThemeContrast(normalized, translate);
401
+ return normalized;
402
+ }
403
+ /**
404
+ * Parses a versioned event payload without surfacing untrusted event failures.
405
+ *
406
+ * @param value - Untrusted event payload.
407
+ * @param translate - Translator used internally by the parser.
408
+ * @returns Normalized settings, or undefined when the payload is invalid.
409
+ */
410
+ function parseAppearancePayload(value, translate) {
411
+ const record = readPlainDataObject(value);
412
+ if (!record || !hasExactKeys(record, ["schemaVersion", "value"]))
413
+ return undefined;
414
+ if (record.schemaVersion !== miaixzAppearanceSchemaVersion)
415
+ return undefined;
416
+ try {
417
+ return parseAppearanceSettings(record.value, translate);
418
+ }
419
+ catch {
420
+ return undefined;
421
+ }
422
+ }
423
+ /**
424
+ * Normalizes appearance structure and custom color syntax.
425
+ *
426
+ * @param value - Untrusted appearance settings.
427
+ * @param translate - Translator used for syntax errors.
428
+ * @returns Deeply frozen normalized settings without contrast evaluation.
429
+ * @throws MiaixzSdkError When object shape, values, tokens, or color syntax are invalid.
430
+ */
431
+ function normalizeAppearanceSettings(value, translate) {
432
+ const record = readPlainDataObject(value);
433
+ if (!record ||
434
+ !Object.keys(record).every((key) => miaixzAppearanceKeys.has(key)) ||
435
+ !Object.hasOwn(record, "colorMode") ||
436
+ !Object.hasOwn(record, "density") ||
437
+ !isMiaixzColorMode(record.colorMode) ||
438
+ !isMiaixzDensity(record.density)) {
439
+ throw createAppearanceColorError(translate);
440
+ }
441
+ let colors;
442
+ if (Object.hasOwn(record, "colors")) {
443
+ const colorRecord = readPlainDataObject(record.colors);
444
+ if (!colorRecord)
445
+ throw createAppearanceColorError(translate);
446
+ const normalizedColors = {};
447
+ for (const [token, color] of Object.entries(colorRecord)) {
448
+ if (!miaixzThemeColorTokenSet.has(token) ||
449
+ typeof color !== "string" ||
450
+ !isValidThemeHex(token, color)) {
451
+ throw createAppearanceColorError(translate, { token });
452
+ }
453
+ normalizedColors[token] = color.toUpperCase();
454
+ }
455
+ colors = Object.freeze(normalizedColors);
456
+ }
457
+ return Object.freeze({
458
+ colorMode: record.colorMode,
459
+ density: record.density,
460
+ ...(colors === undefined ? {} : { colors }),
461
+ });
462
+ }
463
+ /**
464
+ * Validates contrast against every applicable built-in theme.
465
+ *
466
+ * @param appearance - Normalized settings to validate.
467
+ * @param translate - Translator used for contrast errors.
468
+ * @throws MiaixzSdkError When any frozen contrast pair misses its threshold.
469
+ */
470
+ function validateNormalizedThemeContrast(appearance, translate) {
471
+ const themes = appearance.colorMode === "light"
472
+ ? [["light", miaixzLightThemeColors]]
473
+ : appearance.colorMode === "dark"
474
+ ? [["dark", miaixzDarkThemeColors]]
475
+ : [
476
+ ["light", miaixzLightThemeColors],
477
+ ["dark", miaixzDarkThemeColors],
478
+ ];
479
+ for (const [mode, defaults] of themes) {
480
+ const colors = { ...defaults, ...appearance.colors };
481
+ assertContrastPairs(colors, miaixzNormalContrastPairs, 4.5, mode, translate);
482
+ assertContrastPairs(colors, miaixzGraphicalContrastPairs, 3, mode, translate);
483
+ }
484
+ }
485
+ /**
486
+ * Validates a collection of contrast pairs against one threshold.
487
+ *
488
+ * @param colors - Complete merged theme color map.
489
+ * @param pairs - Foreground and background token pairs.
490
+ * @param minimum - Minimum accepted WCAG contrast ratio.
491
+ * @param mode - Theme mode used for safe diagnostic details.
492
+ * @param translate - Translator used for contrast errors.
493
+ * @throws MiaixzSdkError When a pair misses the minimum ratio.
494
+ */
495
+ function assertContrastPairs(colors, pairs, minimum, mode, translate) {
496
+ for (const [foreground, background] of pairs) {
497
+ const ratio = calculateContrastRatio(colors[foreground], colors[background]);
498
+ if (ratio < minimum) {
499
+ throw createAppearanceContrastError(translate, {
500
+ mode,
501
+ foreground,
502
+ background,
503
+ minimum,
504
+ ratio: Number(ratio.toFixed(2)),
505
+ });
506
+ }
507
+ }
508
+ }
509
+ /**
510
+ * Calculates the WCAG contrast ratio between two opaque hexadecimal colors.
511
+ *
512
+ * @param foreground - Foreground color in hexadecimal notation.
513
+ * @param background - Background color in hexadecimal notation.
514
+ * @returns Contrast ratio from one through twenty-one.
515
+ */
516
+ function calculateContrastRatio(foreground, background) {
517
+ const foregroundLuminance = calculateRelativeLuminance(foreground);
518
+ const backgroundLuminance = calculateRelativeLuminance(background);
519
+ const lighter = Math.max(foregroundLuminance, backgroundLuminance);
520
+ const darker = Math.min(foregroundLuminance, backgroundLuminance);
521
+ return (lighter + 0.05) / (darker + 0.05);
522
+ }
523
+ /**
524
+ * Calculates WCAG relative luminance for an opaque hexadecimal color.
525
+ *
526
+ * @param color - Six-digit hexadecimal color.
527
+ * @returns Relative luminance from zero through one.
528
+ */
529
+ function calculateRelativeLuminance(color) {
530
+ const channels = [color.slice(1, 3), color.slice(3, 5), color.slice(5, 7)].map((channel) => {
531
+ const value = Number.parseInt(channel, 16) / 255;
532
+ return value <= 0.04045 ? value / 12.92 : ((value + 0.055) / 1.055) ** 2.4;
533
+ });
534
+ return 0.2126 * (channels[0] ?? 0) + 0.7152 * (channels[1] ?? 0) + 0.0722 * (channels[2] ?? 0);
535
+ }
536
+ /**
537
+ * Determines whether a custom token uses its permitted hexadecimal syntax.
538
+ *
539
+ * @param token - Theme token receiving the value.
540
+ * @param value - Color value to inspect.
541
+ * @returns Whether the color has the exact permitted length and character set.
542
+ */
543
+ function isValidThemeHex(token, value) {
544
+ if (/^#[0-9A-Fa-f]{6}$/.test(value))
545
+ return true;
546
+ return miaixzAlphaThemeColorTokens.has(token) && /^#[0-9A-Fa-f]{8}$/.test(value);
547
+ }
548
+ /**
549
+ * Reads an object without invoking accessors or accepting exotic prototypes.
550
+ *
551
+ * @param value - Runtime value to inspect.
552
+ * @returns A plain data record, or undefined when the value is unsafe.
553
+ */
554
+ function readPlainDataObject(value) {
555
+ if (value === null || typeof value !== "object" || Array.isArray(value))
556
+ return undefined;
557
+ try {
558
+ const prototype = Object.getPrototypeOf(value);
559
+ if (prototype !== Object.prototype && prototype !== null)
560
+ return undefined;
561
+ const descriptors = Object.getOwnPropertyDescriptors(value);
562
+ const record = {};
563
+ for (const key of Object.keys(descriptors)) {
564
+ const descriptor = descriptors[key];
565
+ if (!descriptor?.enumerable)
566
+ continue;
567
+ if (!("value" in descriptor))
568
+ return undefined;
569
+ record[key] = descriptor.value;
570
+ }
571
+ return record;
572
+ }
573
+ catch {
574
+ return undefined;
575
+ }
576
+ }
577
+ /**
578
+ * Determines whether a record has exactly the supplied enumerable keys.
579
+ *
580
+ * @param record - Plain data record to inspect.
581
+ * @param expected - Exact key set required by the contract.
582
+ * @returns Whether every and only expected key is present.
583
+ */
584
+ function hasExactKeys(record, expected) {
585
+ const keys = Object.keys(record);
586
+ return keys.length === expected.length && expected.every((key) => Object.hasOwn(record, key));
587
+ }
588
+ /**
589
+ * Creates a localized appearance syntax or color error.
590
+ *
591
+ * @param translate - Translator used to resolve the public message.
592
+ * @param details - Optional safe token diagnostics.
593
+ * @returns Stable SDK color-validation error.
594
+ */
595
+ function createAppearanceColorError(translate, details) {
596
+ return new MiaixzSdkError(translate("sdk.error.appearance.colorInvalid"), {
597
+ code: "APPEARANCE_COLOR_INVALID",
598
+ ...(details === undefined ? {} : { details }),
599
+ });
600
+ }
601
+ /**
602
+ * Creates a localized appearance contrast error.
603
+ *
604
+ * @param translate - Translator used to resolve the public message.
605
+ * @param details - Safe theme and ratio diagnostics.
606
+ * @returns Stable SDK contrast-validation error.
607
+ */
608
+ function createAppearanceContrastError(translate, details) {
609
+ return new MiaixzSdkError(translate("sdk.error.appearance.contrastInvalid"), {
610
+ code: "APPEARANCE_CONTRAST_INVALID",
611
+ details: Object.freeze({ ...details }),
612
+ });
613
+ }
614
+ //# sourceMappingURL=appearance.js.map