@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.
- package/LICENSE +202 -0
- package/NOTICE +7 -0
- package/README.md +412 -0
- package/dist/api/client.d.ts +223 -0
- package/dist/api/client.d.ts.map +1 -0
- package/dist/api/client.js +1069 -0
- package/dist/api/client.js.map +1 -0
- package/dist/api/endpoint.d.ts +10 -0
- package/dist/api/endpoint.d.ts.map +1 -0
- package/dist/api/endpoint.js +51 -0
- package/dist/api/endpoint.js.map +1 -0
- package/dist/api/errors.d.ts +180 -0
- package/dist/api/errors.d.ts.map +1 -0
- package/dist/api/errors.js +173 -0
- package/dist/api/errors.js.map +1 -0
- package/dist/api/index.d.ts +6 -0
- package/dist/api/index.d.ts.map +1 -0
- package/dist/api/index.js +5 -0
- package/dist/api/index.js.map +1 -0
- package/dist/api/request.d.ts +135 -0
- package/dist/api/request.d.ts.map +1 -0
- package/dist/api/request.js +2 -0
- package/dist/api/request.js.map +1 -0
- package/dist/api/response.d.ts +69 -0
- package/dist/api/response.d.ts.map +1 -0
- package/dist/api/response.js +43 -0
- package/dist/api/response.js.map +1 -0
- package/dist/api/telemetry.d.ts +139 -0
- package/dist/api/telemetry.d.ts.map +1 -0
- package/dist/api/telemetry.js +2 -0
- package/dist/api/telemetry.js.map +1 -0
- package/dist/appearance/appearance.d.ts +161 -0
- package/dist/appearance/appearance.d.ts.map +1 -0
- package/dist/appearance/appearance.js +614 -0
- package/dist/appearance/appearance.js.map +1 -0
- package/dist/appearance/index.d.ts +3 -0
- package/dist/appearance/index.d.ts.map +1 -0
- package/dist/appearance/index.js +3 -0
- package/dist/appearance/index.js.map +1 -0
- package/dist/auth/auth.d.ts +197 -0
- package/dist/auth/auth.d.ts.map +1 -0
- package/dist/auth/auth.js +314 -0
- package/dist/auth/auth.js.map +1 -0
- package/dist/auth/index.d.ts +2 -0
- package/dist/auth/index.d.ts.map +1 -0
- package/dist/auth/index.js +2 -0
- package/dist/auth/index.js.map +1 -0
- package/dist/config/config.d.ts +117 -0
- package/dist/config/config.d.ts.map +1 -0
- package/dist/config/config.js +241 -0
- package/dist/config/config.js.map +1 -0
- package/dist/config/index.d.ts +2 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +2 -0
- package/dist/config/index.js.map +1 -0
- package/dist/consts/index.d.ts +35 -0
- package/dist/consts/index.d.ts.map +1 -0
- package/dist/consts/index.js +35 -0
- package/dist/consts/index.js.map +1 -0
- package/dist/context/context.d.ts +123 -0
- package/dist/context/context.d.ts.map +1 -0
- package/dist/context/context.js +252 -0
- package/dist/context/context.js.map +1 -0
- package/dist/context/index.d.ts +2 -0
- package/dist/context/index.d.ts.map +1 -0
- package/dist/context/index.js +2 -0
- package/dist/context/index.js.map +1 -0
- package/dist/contracts/host-context.d.ts +132 -0
- package/dist/contracts/host-context.d.ts.map +1 -0
- package/dist/contracts/host-context.js +2 -0
- package/dist/contracts/host-context.js.map +1 -0
- package/dist/contracts/index.d.ts +8 -0
- package/dist/contracts/index.d.ts.map +1 -0
- package/dist/contracts/index.js +2 -0
- package/dist/contracts/index.js.map +1 -0
- package/dist/contracts/lifecycle.d.ts +49 -0
- package/dist/contracts/lifecycle.d.ts.map +1 -0
- package/dist/contracts/lifecycle.js +2 -0
- package/dist/contracts/lifecycle.js.map +1 -0
- package/dist/contracts/module-manifest.d.ts +110 -0
- package/dist/contracts/module-manifest.d.ts.map +1 -0
- package/dist/contracts/module-manifest.js +317 -0
- package/dist/contracts/module-manifest.js.map +1 -0
- package/dist/contracts/navigation.d.ts +36 -0
- package/dist/contracts/navigation.d.ts.map +1 -0
- package/dist/contracts/navigation.js +11 -0
- package/dist/contracts/navigation.js.map +1 -0
- package/dist/contracts/permission.d.ts +8 -0
- package/dist/contracts/permission.d.ts.map +1 -0
- package/dist/contracts/permission.js +14 -0
- package/dist/contracts/permission.js.map +1 -0
- package/dist/contracts/post-message.d.ts +124 -0
- package/dist/contracts/post-message.d.ts.map +1 -0
- package/dist/contracts/post-message.js +2 -0
- package/dist/contracts/post-message.js.map +1 -0
- package/dist/contracts/route.d.ts +45 -0
- package/dist/contracts/route.d.ts.map +1 -0
- package/dist/contracts/route.js +35 -0
- package/dist/contracts/route.js.map +1 -0
- package/dist/events/events.d.ts +191 -0
- package/dist/events/events.d.ts.map +1 -0
- package/dist/events/events.js +486 -0
- package/dist/events/events.js.map +1 -0
- package/dist/events/index.d.ts +2 -0
- package/dist/events/index.d.ts.map +1 -0
- package/dist/events/index.js +2 -0
- package/dist/events/index.js.map +1 -0
- package/dist/files/files.d.ts +138 -0
- package/dist/files/files.d.ts.map +1 -0
- package/dist/files/files.js +144 -0
- package/dist/files/files.js.map +1 -0
- package/dist/files/index.d.ts +2 -0
- package/dist/files/index.d.ts.map +1 -0
- package/dist/files/index.js +2 -0
- package/dist/files/index.js.map +1 -0
- package/dist/formatters/index.d.ts +44 -0
- package/dist/formatters/index.d.ts.map +1 -0
- package/dist/formatters/index.js +50 -0
- package/dist/formatters/index.js.map +1 -0
- package/dist/i18n/default-translator.d.ts +17 -0
- package/dist/i18n/default-translator.d.ts.map +1 -0
- package/dist/i18n/default-translator.js +19 -0
- package/dist/i18n/default-translator.js.map +1 -0
- package/dist/i18n/i18n.d.ts +248 -0
- package/dist/i18n/i18n.d.ts.map +1 -0
- package/dist/i18n/i18n.js +617 -0
- package/dist/i18n/i18n.js.map +1 -0
- package/dist/i18n/index.d.ts +2 -0
- package/dist/i18n/index.d.ts.map +1 -0
- package/dist/i18n/index.js +2 -0
- package/dist/i18n/index.js.map +1 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +19 -0
- package/dist/index.js.map +1 -0
- package/dist/models/index.d.ts +2 -0
- package/dist/models/index.d.ts.map +1 -0
- package/dist/models/index.js +2 -0
- package/dist/models/index.js.map +1 -0
- package/dist/permissions/index.d.ts +2 -0
- package/dist/permissions/index.d.ts.map +1 -0
- package/dist/permissions/index.js +2 -0
- package/dist/permissions/index.js.map +1 -0
- package/dist/permissions/permissions.d.ts +67 -0
- package/dist/permissions/permissions.d.ts.map +1 -0
- package/dist/permissions/permissions.js +123 -0
- package/dist/permissions/permissions.js.map +1 -0
- package/dist/runtime/direct-host-bridge.d.ts +11 -0
- package/dist/runtime/direct-host-bridge.d.ts.map +1 -0
- package/dist/runtime/direct-host-bridge.js +299 -0
- package/dist/runtime/direct-host-bridge.js.map +1 -0
- package/dist/runtime/index.d.ts +5 -0
- package/dist/runtime/index.d.ts.map +1 -0
- package/dist/runtime/index.js +3 -0
- package/dist/runtime/index.js.map +1 -0
- package/dist/runtime/post-message-bridge.d.ts +20 -0
- package/dist/runtime/post-message-bridge.d.ts.map +1 -0
- package/dist/runtime/post-message-bridge.js +1115 -0
- package/dist/runtime/post-message-bridge.js.map +1 -0
- package/dist/sdk.d.ts +177 -0
- package/dist/sdk.d.ts.map +1 -0
- package/dist/sdk.js +214 -0
- package/dist/sdk.js.map +1 -0
- package/dist/storage/index.d.ts +2 -0
- package/dist/storage/index.d.ts.map +1 -0
- package/dist/storage/index.js +2 -0
- package/dist/storage/index.js.map +1 -0
- package/dist/storage/storage.d.ts +246 -0
- package/dist/storage/storage.d.ts.map +1 -0
- package/dist/storage/storage.js +358 -0
- package/dist/storage/storage.js.map +1 -0
- package/dist/types/api.d.ts +82 -0
- package/dist/types/api.d.ts.map +1 -0
- package/dist/types/api.js +2 -0
- package/dist/types/api.js.map +1 -0
- package/dist/types/appearance.d.ts +85 -0
- package/dist/types/appearance.d.ts.map +1 -0
- package/dist/types/appearance.js +48 -0
- package/dist/types/appearance.js.map +1 -0
- package/dist/types/config.d.ts +49 -0
- package/dist/types/config.d.ts.map +1 -0
- package/dist/types/config.js +2 -0
- package/dist/types/config.js.map +1 -0
- package/dist/types/context.d.ts +41 -0
- package/dist/types/context.d.ts.map +1 -0
- package/dist/types/context.js +2 -0
- package/dist/types/context.js.map +1 -0
- package/dist/types/file.d.ts +48 -0
- package/dist/types/file.d.ts.map +1 -0
- package/dist/types/file.js +2 -0
- package/dist/types/file.js.map +1 -0
- package/dist/types/index.d.ts +12 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +12 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/organization.d.ts +56 -0
- package/dist/types/organization.d.ts.map +1 -0
- package/dist/types/organization.js +2 -0
- package/dist/types/organization.js.map +1 -0
- package/dist/types/pagination.d.ts +55 -0
- package/dist/types/pagination.d.ts.map +1 -0
- package/dist/types/pagination.js +13 -0
- package/dist/types/pagination.js.map +1 -0
- package/dist/types/permissions.d.ts +26 -0
- package/dist/types/permissions.d.ts.map +1 -0
- package/dist/types/permissions.js +2 -0
- package/dist/types/permissions.js.map +1 -0
- package/dist/types/space.d.ts +43 -0
- package/dist/types/space.d.ts.map +1 -0
- package/dist/types/space.js +2 -0
- package/dist/types/space.js.map +1 -0
- package/dist/types/tenant.d.ts +31 -0
- package/dist/types/tenant.d.ts.map +1 -0
- package/dist/types/tenant.js +2 -0
- package/dist/types/tenant.js.map +1 -0
- package/dist/types/user.d.ts +48 -0
- package/dist/types/user.d.ts.map +1 -0
- package/dist/types/user.js +2 -0
- package/dist/types/user.js.map +1 -0
- package/dist/utils/date.d.ts +9 -0
- package/dist/utils/date.d.ts.map +1 -0
- package/dist/utils/date.js +11 -0
- package/dist/utils/date.js.map +1 -0
- package/dist/utils/index.d.ts +5 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +5 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/number.d.ts +11 -0
- package/dist/utils/number.d.ts.map +1 -0
- package/dist/utils/number.js +13 -0
- package/dist/utils/number.js.map +1 -0
- package/dist/utils/object.d.ts +9 -0
- package/dist/utils/object.d.ts.map +1 -0
- package/dist/utils/object.js +11 -0
- package/dist/utils/object.js.map +1 -0
- package/dist/utils/string.d.ts +9 -0
- package/dist/utils/string.d.ts.map +1 -0
- package/dist/utils/string.js +11 -0
- package/dist/utils/string.js.map +1 -0
- package/dist/validators/index.d.ts +8 -0
- package/dist/validators/index.d.ts.map +1 -0
- package/dist/validators/index.js +8 -0
- package/dist/validators/index.js.map +1 -0
- package/package.json +156 -0
|
@@ -0,0 +1,486 @@
|
|
|
1
|
+
import { MiaixzSdkError } from "../api/errors.js";
|
|
2
|
+
import { isMiaixzAppearanceSettings } from "../appearance/index.js";
|
|
3
|
+
import { isMiaixzSdkConfig } from "../config/index.js";
|
|
4
|
+
import { isMiaixzRuntimeContext } from "../context/index.js";
|
|
5
|
+
import { miaixzDefaultI18n } from "../i18n/index.js";
|
|
6
|
+
const miaixzEventChannelPattern = /^miaixz:v1:[a-z][a-z0-9-]{1,63}:events$/;
|
|
7
|
+
const miaixzUuidPattern = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
8
|
+
const miaixzEventEnvelopeKeys = new Set(["version", "eventId", "sourceId", "type", "payload"]);
|
|
9
|
+
const miaixzRuntimeContextKeys = new Set([
|
|
10
|
+
"userId",
|
|
11
|
+
"tenantId",
|
|
12
|
+
"organizationId",
|
|
13
|
+
"departmentId",
|
|
14
|
+
"spaceId",
|
|
15
|
+
"locale",
|
|
16
|
+
"timezone",
|
|
17
|
+
"traceId",
|
|
18
|
+
]);
|
|
19
|
+
const miaixzSdkConfigKeys = new Set([
|
|
20
|
+
"apiBaseUrl",
|
|
21
|
+
"environment",
|
|
22
|
+
"release",
|
|
23
|
+
"services",
|
|
24
|
+
"features",
|
|
25
|
+
"appearance",
|
|
26
|
+
"requestTimeoutMs",
|
|
27
|
+
]);
|
|
28
|
+
const miaixzSensitiveEventKeys = new Set([
|
|
29
|
+
"authorization",
|
|
30
|
+
"authorizationheader",
|
|
31
|
+
"cookie",
|
|
32
|
+
"cookies",
|
|
33
|
+
"session",
|
|
34
|
+
]);
|
|
35
|
+
const miaixzEventDeduplicationLimit = 1_000;
|
|
36
|
+
const miaixzEventDeduplicationTtlMs = 5 * 60 * 1_000;
|
|
37
|
+
/**
|
|
38
|
+
* Determines whether a property name conventionally contains credential material.
|
|
39
|
+
*
|
|
40
|
+
* @param key - Property or map key to normalize.
|
|
41
|
+
* @returns Whether the key names a token, cookie, authorization value, or full session.
|
|
42
|
+
*/
|
|
43
|
+
function isSensitiveEventKey(key) {
|
|
44
|
+
const normalizedKey = key.replace(/[-_]/g, "").toLowerCase();
|
|
45
|
+
return (miaixzSensitiveEventKeys.has(normalizedKey) ||
|
|
46
|
+
normalizedKey === "token" ||
|
|
47
|
+
normalizedKey.endsWith("token"));
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Determines whether a value is a plain data object without accessors.
|
|
51
|
+
*
|
|
52
|
+
* @param value - Runtime value to inspect.
|
|
53
|
+
* @returns Whether the value contains only enumerable own data properties.
|
|
54
|
+
*/
|
|
55
|
+
function isPlainEventObject(value) {
|
|
56
|
+
if (typeof value !== "object" || value === null || Array.isArray(value))
|
|
57
|
+
return false;
|
|
58
|
+
const prototype = Object.getPrototypeOf(value);
|
|
59
|
+
if (prototype !== Object.prototype && prototype !== null)
|
|
60
|
+
return false;
|
|
61
|
+
if (Object.getOwnPropertySymbols(value).length > 0)
|
|
62
|
+
return false;
|
|
63
|
+
const descriptors = Object.getOwnPropertyDescriptors(value);
|
|
64
|
+
return Object.values(descriptors).every((descriptor) => descriptor.enumerable === true && "value" in descriptor);
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Determines whether a plain object contains exactly the allowed enumerable keys.
|
|
68
|
+
*
|
|
69
|
+
* @param value - Plain object to inspect.
|
|
70
|
+
* @param allowedKeys - Complete allowed-key set.
|
|
71
|
+
* @returns Whether every key is allowed and the key counts match.
|
|
72
|
+
*/
|
|
73
|
+
function hasExactEventKeys(value, allowedKeys) {
|
|
74
|
+
const keys = Object.keys(value);
|
|
75
|
+
return keys.length === allowedKeys.size && keys.every((key) => allowedKeys.has(key));
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Determines whether an event payload contains a credential-shaped field.
|
|
79
|
+
*
|
|
80
|
+
* The traversal never invokes accessors and treats unsupported object shapes as unsafe.
|
|
81
|
+
*
|
|
82
|
+
* @param value - Cloned payload to inspect.
|
|
83
|
+
* @param visited - Objects already inspected while following recursive references.
|
|
84
|
+
* @returns Whether the payload contains a token, cookie, authorization, or session field.
|
|
85
|
+
*/
|
|
86
|
+
function containsSensitiveEventData(value, visited = new WeakSet()) {
|
|
87
|
+
if (value === null || typeof value !== "object")
|
|
88
|
+
return false;
|
|
89
|
+
if (visited.has(value))
|
|
90
|
+
return false;
|
|
91
|
+
visited.add(value);
|
|
92
|
+
if (Array.isArray(value)) {
|
|
93
|
+
return value.some((entry) => containsSensitiveEventData(entry, visited));
|
|
94
|
+
}
|
|
95
|
+
if (value instanceof Map) {
|
|
96
|
+
for (const [key, entry] of value) {
|
|
97
|
+
if (typeof key === "string" && isSensitiveEventKey(key))
|
|
98
|
+
return true;
|
|
99
|
+
if (containsSensitiveEventData(key, visited) || containsSensitiveEventData(entry, visited)) {
|
|
100
|
+
return true;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return false;
|
|
104
|
+
}
|
|
105
|
+
if (value instanceof Set) {
|
|
106
|
+
for (const entry of value) {
|
|
107
|
+
if (containsSensitiveEventData(entry, visited))
|
|
108
|
+
return true;
|
|
109
|
+
}
|
|
110
|
+
return false;
|
|
111
|
+
}
|
|
112
|
+
if (value instanceof ArrayBuffer || ArrayBuffer.isView(value) || value instanceof Date) {
|
|
113
|
+
return false;
|
|
114
|
+
}
|
|
115
|
+
for (const key of Reflect.ownKeys(value)) {
|
|
116
|
+
if (typeof key === "string" && isSensitiveEventKey(key))
|
|
117
|
+
return true;
|
|
118
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, key);
|
|
119
|
+
if (descriptor === undefined || !("value" in descriptor))
|
|
120
|
+
return true;
|
|
121
|
+
if (containsSensitiveEventData(descriptor.value, visited))
|
|
122
|
+
return true;
|
|
123
|
+
}
|
|
124
|
+
return false;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Validates the built-in authentication status payload.
|
|
128
|
+
*
|
|
129
|
+
* @param payload - Untrusted event payload.
|
|
130
|
+
* @returns Whether the payload contains exactly one supported status field.
|
|
131
|
+
*/
|
|
132
|
+
function isMiaixzAuthStatusEvent(payload) {
|
|
133
|
+
return (isPlainEventObject(payload) &&
|
|
134
|
+
hasExactEventKeys(payload, new Set(["status"])) &&
|
|
135
|
+
(payload.status === "authenticated" || payload.status === "anonymous"));
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Validates the built-in runtime-context payload.
|
|
139
|
+
*
|
|
140
|
+
* @param payload - Untrusted event payload.
|
|
141
|
+
* @returns Whether the payload contains only supported string-valued Context fields.
|
|
142
|
+
*/
|
|
143
|
+
function isMiaixzContextEvent(payload) {
|
|
144
|
+
return (isPlainEventObject(payload) &&
|
|
145
|
+
Object.keys(payload).every((key) => miaixzRuntimeContextKeys.has(key)) &&
|
|
146
|
+
isMiaixzRuntimeContext(payload));
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Validates the built-in appearance payload and its nested settings.
|
|
150
|
+
*
|
|
151
|
+
* @param payload - Untrusted event payload.
|
|
152
|
+
* @returns Whether the payload uses schema version one and valid appearance settings.
|
|
153
|
+
*/
|
|
154
|
+
function isMiaixzAppearanceEvent(payload) {
|
|
155
|
+
return (isPlainEventObject(payload) &&
|
|
156
|
+
hasExactEventKeys(payload, new Set(["schemaVersion", "value"])) &&
|
|
157
|
+
payload.schemaVersion === 1 &&
|
|
158
|
+
isMiaixzAppearanceSettings(payload.value));
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Validates the built-in locale-change payload.
|
|
162
|
+
*
|
|
163
|
+
* @param payload - Untrusted event payload.
|
|
164
|
+
* @returns Whether the payload contains exactly one valid BCP 47 locale.
|
|
165
|
+
*/
|
|
166
|
+
function isMiaixzLocaleEvent(payload) {
|
|
167
|
+
if (!isPlainEventObject(payload) ||
|
|
168
|
+
!hasExactEventKeys(payload, new Set(["locale"])) ||
|
|
169
|
+
typeof payload.locale !== "string") {
|
|
170
|
+
return false;
|
|
171
|
+
}
|
|
172
|
+
try {
|
|
173
|
+
return Intl.getCanonicalLocales(payload.locale).length === 1;
|
|
174
|
+
}
|
|
175
|
+
catch {
|
|
176
|
+
return false;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Validates the built-in SDK configuration payload.
|
|
181
|
+
*
|
|
182
|
+
* @param payload - Untrusted event payload.
|
|
183
|
+
* @returns Whether the payload contains only supported configuration fields and values.
|
|
184
|
+
*/
|
|
185
|
+
function isMiaixzConfigEvent(payload) {
|
|
186
|
+
return (isPlainEventObject(payload) &&
|
|
187
|
+
Object.keys(payload).every((key) => miaixzSdkConfigKeys.has(key)) &&
|
|
188
|
+
isMiaixzSdkConfig(payload));
|
|
189
|
+
}
|
|
190
|
+
const miaixzBuiltinEventValidators = Object.freeze({
|
|
191
|
+
"auth:changed": isMiaixzAuthStatusEvent,
|
|
192
|
+
"context:changed": isMiaixzContextEvent,
|
|
193
|
+
"appearance:changed": isMiaixzAppearanceEvent,
|
|
194
|
+
"locale:changed": isMiaixzLocaleEvent,
|
|
195
|
+
"config:changed": isMiaixzConfigEvent,
|
|
196
|
+
});
|
|
197
|
+
/**
|
|
198
|
+
* Creates a localized event error without including rejected payload data.
|
|
199
|
+
*
|
|
200
|
+
* @param code - Stable event error code.
|
|
201
|
+
* @param messageKey - Registered internationalization message key.
|
|
202
|
+
* @returns Localized SDK event error.
|
|
203
|
+
*/
|
|
204
|
+
function createMiaixzEventError(code, messageKey) {
|
|
205
|
+
return new MiaixzSdkError(miaixzDefaultI18n.t(messageKey), { code });
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* Creates a secure UUID for event sources and messages.
|
|
209
|
+
*
|
|
210
|
+
* @returns A cryptographically secure UUID.
|
|
211
|
+
* @throws MiaixzSdkError When the runtime cannot generate secure UUIDs.
|
|
212
|
+
*/
|
|
213
|
+
function createMiaixzEventId() {
|
|
214
|
+
if (typeof globalThis.crypto?.randomUUID !== "function") {
|
|
215
|
+
throw createMiaixzEventError("EVENT_CRYPTO_UNAVAILABLE", "sdk.error.event.cryptoUnavailable");
|
|
216
|
+
}
|
|
217
|
+
return globalThis.crypto.randomUUID();
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Validates an untrusted BroadcastChannel envelope without executing accessors.
|
|
221
|
+
*
|
|
222
|
+
* @param value - Broadcast message data to inspect.
|
|
223
|
+
* @returns Whether the value is a complete version-one event envelope.
|
|
224
|
+
*/
|
|
225
|
+
function isMiaixzEventEnvelope(value) {
|
|
226
|
+
return (isPlainEventObject(value) &&
|
|
227
|
+
hasExactEventKeys(value, miaixzEventEnvelopeKeys) &&
|
|
228
|
+
value.version === 1 &&
|
|
229
|
+
typeof value.eventId === "string" &&
|
|
230
|
+
miaixzUuidPattern.test(value.eventId) &&
|
|
231
|
+
typeof value.sourceId === "string" &&
|
|
232
|
+
miaixzUuidPattern.test(value.sourceId) &&
|
|
233
|
+
typeof value.type === "string" &&
|
|
234
|
+
value.type.length > 0);
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Produces a structured-cloned event payload before external delivery.
|
|
238
|
+
*
|
|
239
|
+
* @param payload - Payload to clone.
|
|
240
|
+
* @returns Detached payload clone safe to place in an event envelope.
|
|
241
|
+
* @throws MiaixzSdkError When structured cloning is unavailable or fails.
|
|
242
|
+
*/
|
|
243
|
+
function cloneMiaixzEventPayload(payload) {
|
|
244
|
+
if (typeof globalThis.structuredClone !== "function") {
|
|
245
|
+
throw createMiaixzEventError("EVENT_PAYLOAD_NOT_CLONEABLE", "sdk.error.event.payloadNotCloneable");
|
|
246
|
+
}
|
|
247
|
+
try {
|
|
248
|
+
return globalThis.structuredClone(payload);
|
|
249
|
+
}
|
|
250
|
+
catch {
|
|
251
|
+
throw createMiaixzEventError("EVENT_PAYLOAD_NOT_CLONEABLE", "sdk.error.event.payloadNotCloneable");
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* Applies a registered payload validator and the credential-exposure guard safely.
|
|
256
|
+
*
|
|
257
|
+
* @param validator - Event-specific runtime validator.
|
|
258
|
+
* @param payload - Cloned or externally received payload.
|
|
259
|
+
* @returns Whether the payload is valid and free of credential-shaped fields.
|
|
260
|
+
*/
|
|
261
|
+
function isValidMiaixzEventPayload(validator, payload) {
|
|
262
|
+
try {
|
|
263
|
+
return !containsSensitiveEventData(payload) && validator(payload);
|
|
264
|
+
}
|
|
265
|
+
catch {
|
|
266
|
+
return false;
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* Typed local and optional same-origin cross-tab event bus.
|
|
271
|
+
*
|
|
272
|
+
* @typeParam Events - Event-name map whose values define payload types.
|
|
273
|
+
* @public
|
|
274
|
+
*/
|
|
275
|
+
export class MiaixzEventBus {
|
|
276
|
+
#listeners = new Map();
|
|
277
|
+
#validators;
|
|
278
|
+
#channel;
|
|
279
|
+
#sourceId;
|
|
280
|
+
#receivedEventIds = new Map();
|
|
281
|
+
#messageListener;
|
|
282
|
+
#closed = false;
|
|
283
|
+
/**
|
|
284
|
+
* Creates a local-first event bus and an optional validated BroadcastChannel.
|
|
285
|
+
*
|
|
286
|
+
* @param options - Channel factory and application-defined runtime validators.
|
|
287
|
+
* @throws MiaixzSdkError When an explicit channel name or secure UUID capability is invalid.
|
|
288
|
+
*/
|
|
289
|
+
constructor(options = {}) {
|
|
290
|
+
this.#validators = Object.freeze({
|
|
291
|
+
...options.validators,
|
|
292
|
+
...miaixzBuiltinEventValidators,
|
|
293
|
+
});
|
|
294
|
+
const channelName = options.channelName ?? false;
|
|
295
|
+
if (channelName === false) {
|
|
296
|
+
this.#channel = undefined;
|
|
297
|
+
this.#sourceId = undefined;
|
|
298
|
+
this.#messageListener = undefined;
|
|
299
|
+
return;
|
|
300
|
+
}
|
|
301
|
+
if (!miaixzEventChannelPattern.test(channelName)) {
|
|
302
|
+
throw createMiaixzEventError("EVENT_CHANNEL_INVALID", "sdk.error.event.channelInvalid");
|
|
303
|
+
}
|
|
304
|
+
const hasChannelFactory = typeof options.broadcastChannelFactory === "function";
|
|
305
|
+
const hasNativeChannel = typeof globalThis.BroadcastChannel === "function";
|
|
306
|
+
if (!hasChannelFactory && !hasNativeChannel) {
|
|
307
|
+
this.#channel = undefined;
|
|
308
|
+
this.#sourceId = undefined;
|
|
309
|
+
this.#messageListener = undefined;
|
|
310
|
+
return;
|
|
311
|
+
}
|
|
312
|
+
const sourceId = createMiaixzEventId();
|
|
313
|
+
const channel = hasChannelFactory
|
|
314
|
+
? options.broadcastChannelFactory?.(channelName)
|
|
315
|
+
: new globalThis.BroadcastChannel(channelName);
|
|
316
|
+
if (channel === undefined) {
|
|
317
|
+
this.#channel = undefined;
|
|
318
|
+
this.#sourceId = undefined;
|
|
319
|
+
this.#messageListener = undefined;
|
|
320
|
+
return;
|
|
321
|
+
}
|
|
322
|
+
this.#channel = channel;
|
|
323
|
+
this.#sourceId = sourceId;
|
|
324
|
+
this.#messageListener = (event) => {
|
|
325
|
+
this.#receive(event.data);
|
|
326
|
+
};
|
|
327
|
+
this.#channel.addEventListener("message", this.#messageListener);
|
|
328
|
+
}
|
|
329
|
+
/**
|
|
330
|
+
* Registers a typed listener and returns its unsubscribe function.
|
|
331
|
+
*
|
|
332
|
+
* @param type - Event name to observe.
|
|
333
|
+
* @param listener - Callback invoked with the typed event payload.
|
|
334
|
+
* @returns Function that unregisters the listener, or a no-op after closure.
|
|
335
|
+
*/
|
|
336
|
+
on(type, listener) {
|
|
337
|
+
if (this.#closed)
|
|
338
|
+
return () => undefined;
|
|
339
|
+
const listeners = this.#listeners.get(type) ?? new Set();
|
|
340
|
+
listeners.add(listener);
|
|
341
|
+
this.#listeners.set(type, listeners);
|
|
342
|
+
return () => {
|
|
343
|
+
listeners.delete(listener);
|
|
344
|
+
if (listeners.size === 0)
|
|
345
|
+
this.#listeners.delete(type);
|
|
346
|
+
};
|
|
347
|
+
}
|
|
348
|
+
/**
|
|
349
|
+
* Registers a typed listener that is removed after its first event.
|
|
350
|
+
*
|
|
351
|
+
* @param type - Event name to observe once.
|
|
352
|
+
* @param listener - Callback invoked with the typed event payload.
|
|
353
|
+
* @returns Function that unregisters the listener before delivery.
|
|
354
|
+
*/
|
|
355
|
+
once(type, listener) {
|
|
356
|
+
if (this.#closed)
|
|
357
|
+
return () => undefined;
|
|
358
|
+
const unsubscribe = this.on(type, (payload) => {
|
|
359
|
+
unsubscribe();
|
|
360
|
+
listener(payload);
|
|
361
|
+
});
|
|
362
|
+
return unsubscribe;
|
|
363
|
+
}
|
|
364
|
+
/**
|
|
365
|
+
* Publishes an event synchronously to local listeners before optional cross-tab delivery.
|
|
366
|
+
*
|
|
367
|
+
* @param type - Event name to publish.
|
|
368
|
+
* @param payload - Typed event payload.
|
|
369
|
+
* @param options - Optional cross-tab publication behavior.
|
|
370
|
+
* @throws MiaixzSdkError When the bus is closed, a required validator is missing, cloning fails, or secure UUIDs are unavailable.
|
|
371
|
+
*/
|
|
372
|
+
emit(type, payload, options = {}) {
|
|
373
|
+
if (this.#closed) {
|
|
374
|
+
throw createMiaixzEventError("EVENT_CHANNEL_INVALID", "sdk.error.event.channelInvalid");
|
|
375
|
+
}
|
|
376
|
+
this.#dispatch(type, payload);
|
|
377
|
+
if (options.broadcast === false ||
|
|
378
|
+
this.#channel === undefined ||
|
|
379
|
+
this.#sourceId === undefined) {
|
|
380
|
+
return;
|
|
381
|
+
}
|
|
382
|
+
const validator = Object.hasOwn(this.#validators, type) ? this.#validators[type] : undefined;
|
|
383
|
+
if (validator === undefined) {
|
|
384
|
+
if (options.broadcast === true) {
|
|
385
|
+
throw createMiaixzEventError("EVENT_VALIDATOR_MISSING", "sdk.error.event.validatorMissing");
|
|
386
|
+
}
|
|
387
|
+
return;
|
|
388
|
+
}
|
|
389
|
+
const clonedPayload = cloneMiaixzEventPayload(payload);
|
|
390
|
+
if (!isValidMiaixzEventPayload(validator, clonedPayload))
|
|
391
|
+
return;
|
|
392
|
+
this.#channel.postMessage({
|
|
393
|
+
version: 1,
|
|
394
|
+
eventId: createMiaixzEventId(),
|
|
395
|
+
sourceId: this.#sourceId,
|
|
396
|
+
type,
|
|
397
|
+
payload: clonedPayload,
|
|
398
|
+
});
|
|
399
|
+
}
|
|
400
|
+
/**
|
|
401
|
+
* Removes listeners for one event type or all event types.
|
|
402
|
+
*
|
|
403
|
+
* @param type - Optional event name whose listeners should be removed.
|
|
404
|
+
*/
|
|
405
|
+
clear(type) {
|
|
406
|
+
if (type === undefined)
|
|
407
|
+
this.#listeners.clear();
|
|
408
|
+
else
|
|
409
|
+
this.#listeners.delete(type);
|
|
410
|
+
}
|
|
411
|
+
/**
|
|
412
|
+
* Removes listeners and deduplication state, then closes the underlying channel once.
|
|
413
|
+
*/
|
|
414
|
+
close() {
|
|
415
|
+
if (this.#closed)
|
|
416
|
+
return;
|
|
417
|
+
this.#closed = true;
|
|
418
|
+
this.clear();
|
|
419
|
+
this.#receivedEventIds.clear();
|
|
420
|
+
if (this.#channel && this.#messageListener) {
|
|
421
|
+
this.#channel.removeEventListener("message", this.#messageListener);
|
|
422
|
+
}
|
|
423
|
+
this.#channel?.close();
|
|
424
|
+
}
|
|
425
|
+
/**
|
|
426
|
+
* Delivers one event to current local listeners in registration order.
|
|
427
|
+
*
|
|
428
|
+
* @param type - Event name to dispatch.
|
|
429
|
+
* @param payload - Event payload to deliver.
|
|
430
|
+
*/
|
|
431
|
+
#dispatch(type, payload) {
|
|
432
|
+
for (const listener of this.#listeners.get(type) ?? [])
|
|
433
|
+
listener(payload);
|
|
434
|
+
}
|
|
435
|
+
/**
|
|
436
|
+
* Validates, deduplicates, and dispatches one untrusted channel message.
|
|
437
|
+
*
|
|
438
|
+
* @param value - Message data delivered by BroadcastChannel.
|
|
439
|
+
*/
|
|
440
|
+
#receive(value) {
|
|
441
|
+
if (this.#closed || !isMiaixzEventEnvelope(value) || value.sourceId === this.#sourceId)
|
|
442
|
+
return;
|
|
443
|
+
this.#pruneReceivedEventIds(Date.now());
|
|
444
|
+
if (this.#receivedEventIds.has(value.eventId))
|
|
445
|
+
return;
|
|
446
|
+
const validator = Object.hasOwn(this.#validators, value.type)
|
|
447
|
+
? this.#validators[value.type]
|
|
448
|
+
: undefined;
|
|
449
|
+
if (validator === undefined)
|
|
450
|
+
return;
|
|
451
|
+
if (!isValidMiaixzEventPayload(validator, value.payload))
|
|
452
|
+
return;
|
|
453
|
+
this.#receivedEventIds.set(value.eventId, Date.now());
|
|
454
|
+
while (this.#receivedEventIds.size > miaixzEventDeduplicationLimit) {
|
|
455
|
+
const oldest = this.#receivedEventIds.keys().next().value;
|
|
456
|
+
if (oldest === undefined)
|
|
457
|
+
break;
|
|
458
|
+
this.#receivedEventIds.delete(oldest);
|
|
459
|
+
}
|
|
460
|
+
this.#dispatch(value.type, value.payload);
|
|
461
|
+
}
|
|
462
|
+
/**
|
|
463
|
+
* Removes received event identifiers whose fixed five-minute TTL has elapsed.
|
|
464
|
+
*
|
|
465
|
+
* @param now - Current Unix time in milliseconds.
|
|
466
|
+
*/
|
|
467
|
+
#pruneReceivedEventIds(now) {
|
|
468
|
+
for (const [eventId, receivedAt] of this.#receivedEventIds) {
|
|
469
|
+
if (now - receivedAt < miaixzEventDeduplicationTtlMs)
|
|
470
|
+
continue;
|
|
471
|
+
this.#receivedEventIds.delete(eventId);
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
/**
|
|
476
|
+
* Creates a typed local-first event bus with optional same-origin cross-tab delivery.
|
|
477
|
+
*
|
|
478
|
+
* @typeParam Events - Event-name map whose values define payload types.
|
|
479
|
+
* @param options - Optional channel and runtime-validator configuration.
|
|
480
|
+
* @returns Configured typed event bus.
|
|
481
|
+
* @public
|
|
482
|
+
*/
|
|
483
|
+
export function createMiaixzEventBus(options) {
|
|
484
|
+
return new MiaixzEventBus(options);
|
|
485
|
+
}
|
|
486
|
+
//# sourceMappingURL=events.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"events.js","sourceRoot":"","sources":["../../src/events/events.ts"],"names":[],"mappings":"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","sourcesContent":["import { MiaixzSdkError } from \"../api/errors.js\";\nimport { isMiaixzAppearanceSettings } from \"../appearance/index.js\";\nimport { isMiaixzSdkConfig } from \"../config/index.js\";\nimport { isMiaixzRuntimeContext } from \"../context/index.js\";\nimport { miaixzDefaultI18n, type MiaixzLocale } from \"../i18n/index.js\";\nimport type {\n MiaixzAppearancePayload,\n MiaixzRuntimeContext,\n MiaixzSdkConfig,\n} from \"../types/index.js\";\n\n/**\n * Validates an untrusted event payload before it crosses a browser-context boundary.\n *\n * @public\n */\nexport type MiaixzEventValidator = (payload: unknown) => boolean;\n\n/**\n * Describes the only authentication state that may cross an SDK event channel.\n *\n * @public\n */\nexport interface MiaixzAuthStatusEvent {\n /**\n * Reports whether credentials exist without exposing a session or token.\n */\n readonly status: \"authenticated\" | \"anonymous\";\n}\n\n/**\n * Describes a locale change shared between same-origin application instances.\n *\n * @public\n */\nexport interface MiaixzLocaleChangedEvent {\n /**\n * Contains the canonical or otherwise valid BCP 47 locale to activate.\n */\n readonly locale: MiaixzLocale;\n}\n\n/**\n * Maps built-in SDK event names to their payload types.\n *\n * @public\n */\nexport interface MiaixzSdkEventMap {\n /**\n * Contains a non-sensitive authentication invalidation signal.\n */\n readonly \"auth:changed\": Readonly<MiaixzAuthStatusEvent>;\n\n /**\n * Contains the runtime-context snapshot after it changes.\n */\n readonly \"context:changed\": Readonly<MiaixzRuntimeContext>;\n\n /**\n * Contains the versioned appearance snapshot after it changes.\n */\n readonly \"appearance:changed\": Readonly<MiaixzAppearancePayload>;\n\n /**\n * Contains the active locale after it changes.\n */\n readonly \"locale:changed\": Readonly<MiaixzLocaleChangedEvent>;\n\n /**\n * Contains the SDK configuration snapshot after it changes.\n */\n readonly \"config:changed\": Readonly<MiaixzSdkConfig>;\n}\n\n/**\n * Configures local and optional same-origin cross-tab event delivery.\n *\n * @public\n */\nexport interface MiaixzEventBusOptions {\n /**\n * Selects a validated BroadcastChannel name, or disables cross-tab delivery.\n *\n * @defaultValue false\n */\n readonly channelName?: string | false;\n\n /**\n * Supplies runtime validators for application-defined event names.\n */\n readonly validators?: Readonly<Record<string, MiaixzEventValidator>>;\n\n /**\n * Creates a BroadcastChannel for tests or compatible custom browser runtimes.\n */\n readonly broadcastChannelFactory?: (name: string) => BroadcastChannel;\n}\n\n/**\n * Defines the only envelope accepted over a Miaixz BroadcastChannel.\n *\n * @public\n */\nexport interface MiaixzEventEnvelope {\n /**\n * Identifies the frozen event-envelope schema.\n */\n readonly version: 1;\n\n /**\n * Uniquely identifies one broadcast operation for deduplication.\n */\n readonly eventId: string;\n\n /**\n * Identifies the event-bus instance that originated the message.\n */\n readonly sourceId: string;\n\n /**\n * Names the typed event represented by the payload.\n */\n readonly type: string;\n\n /**\n * Contains untrusted data that must pass the registered runtime validator.\n */\n readonly payload: unknown;\n}\n\n/**\n * Configures one event publication.\n *\n * @public\n */\nexport interface MiaixzEventEmitOptions {\n /**\n * Indicates whether a locally dispatched event may also cross the configured channel.\n *\n * @defaultValue true\n */\n readonly broadcast?: boolean;\n}\n\n/**\n * Extracts string event names from an event map.\n *\n * @public\n */\nexport type MiaixzEventName<Events extends object> = Extract<keyof Events, string>;\n\n/**\n * Receives a typed event payload.\n *\n * @public\n */\nexport type MiaixzEventListener<T> = (payload: T) => void;\n\nconst miaixzEventChannelPattern = /^miaixz:v1:[a-z][a-z0-9-]{1,63}:events$/;\nconst miaixzUuidPattern =\n /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;\nconst miaixzEventEnvelopeKeys = new Set([\"version\", \"eventId\", \"sourceId\", \"type\", \"payload\"]);\nconst miaixzRuntimeContextKeys = new Set([\n \"userId\",\n \"tenantId\",\n \"organizationId\",\n \"departmentId\",\n \"spaceId\",\n \"locale\",\n \"timezone\",\n \"traceId\",\n]);\nconst miaixzSdkConfigKeys = new Set([\n \"apiBaseUrl\",\n \"environment\",\n \"release\",\n \"services\",\n \"features\",\n \"appearance\",\n \"requestTimeoutMs\",\n]);\nconst miaixzSensitiveEventKeys = new Set([\n \"authorization\",\n \"authorizationheader\",\n \"cookie\",\n \"cookies\",\n \"session\",\n]);\nconst miaixzEventDeduplicationLimit = 1_000;\nconst miaixzEventDeduplicationTtlMs = 5 * 60 * 1_000;\n\n/**\n * Determines whether a property name conventionally contains credential material.\n *\n * @param key - Property or map key to normalize.\n * @returns Whether the key names a token, cookie, authorization value, or full session.\n */\nfunction isSensitiveEventKey(key: string): boolean {\n const normalizedKey = key.replace(/[-_]/g, \"\").toLowerCase();\n return (\n miaixzSensitiveEventKeys.has(normalizedKey) ||\n normalizedKey === \"token\" ||\n normalizedKey.endsWith(\"token\")\n );\n}\n\n/**\n * Determines whether a value is a plain data object without accessors.\n *\n * @param value - Runtime value to inspect.\n * @returns Whether the value contains only enumerable own data properties.\n */\nfunction isPlainEventObject(value: unknown): value is Record<string, unknown> {\n if (typeof value !== \"object\" || value === null || Array.isArray(value)) return false;\n const prototype = Object.getPrototypeOf(value);\n if (prototype !== Object.prototype && prototype !== null) return false;\n if (Object.getOwnPropertySymbols(value).length > 0) return false;\n const descriptors = Object.getOwnPropertyDescriptors(value);\n return Object.values(descriptors).every(\n (descriptor) => descriptor.enumerable === true && \"value\" in descriptor,\n );\n}\n\n/**\n * Determines whether a plain object contains exactly the allowed enumerable keys.\n *\n * @param value - Plain object to inspect.\n * @param allowedKeys - Complete allowed-key set.\n * @returns Whether every key is allowed and the key counts match.\n */\nfunction hasExactEventKeys(\n value: Record<string, unknown>,\n allowedKeys: ReadonlySet<string>,\n): boolean {\n const keys = Object.keys(value);\n return keys.length === allowedKeys.size && keys.every((key) => allowedKeys.has(key));\n}\n\n/**\n * Determines whether an event payload contains a credential-shaped field.\n *\n * The traversal never invokes accessors and treats unsupported object shapes as unsafe.\n *\n * @param value - Cloned payload to inspect.\n * @param visited - Objects already inspected while following recursive references.\n * @returns Whether the payload contains a token, cookie, authorization, or session field.\n */\nfunction containsSensitiveEventData(value: unknown, visited = new WeakSet<object>()): boolean {\n if (value === null || typeof value !== \"object\") return false;\n if (visited.has(value)) return false;\n visited.add(value);\n if (Array.isArray(value)) {\n return value.some((entry) => containsSensitiveEventData(entry, visited));\n }\n if (value instanceof Map) {\n for (const [key, entry] of value) {\n if (typeof key === \"string\" && isSensitiveEventKey(key)) return true;\n if (containsSensitiveEventData(key, visited) || containsSensitiveEventData(entry, visited)) {\n return true;\n }\n }\n return false;\n }\n if (value instanceof Set) {\n for (const entry of value) {\n if (containsSensitiveEventData(entry, visited)) return true;\n }\n return false;\n }\n if (value instanceof ArrayBuffer || ArrayBuffer.isView(value) || value instanceof Date) {\n return false;\n }\n for (const key of Reflect.ownKeys(value)) {\n if (typeof key === \"string\" && isSensitiveEventKey(key)) return true;\n const descriptor = Object.getOwnPropertyDescriptor(value, key);\n if (descriptor === undefined || !(\"value\" in descriptor)) return true;\n if (containsSensitiveEventData(descriptor.value, visited)) return true;\n }\n return false;\n}\n\n/**\n * Validates the built-in authentication status payload.\n *\n * @param payload - Untrusted event payload.\n * @returns Whether the payload contains exactly one supported status field.\n */\nfunction isMiaixzAuthStatusEvent(payload: unknown): payload is MiaixzAuthStatusEvent {\n return (\n isPlainEventObject(payload) &&\n hasExactEventKeys(payload, new Set([\"status\"])) &&\n (payload.status === \"authenticated\" || payload.status === \"anonymous\")\n );\n}\n\n/**\n * Validates the built-in runtime-context payload.\n *\n * @param payload - Untrusted event payload.\n * @returns Whether the payload contains only supported string-valued Context fields.\n */\nfunction isMiaixzContextEvent(payload: unknown): payload is MiaixzRuntimeContext {\n return (\n isPlainEventObject(payload) &&\n Object.keys(payload).every((key) => miaixzRuntimeContextKeys.has(key)) &&\n isMiaixzRuntimeContext(payload)\n );\n}\n\n/**\n * Validates the built-in appearance payload and its nested settings.\n *\n * @param payload - Untrusted event payload.\n * @returns Whether the payload uses schema version one and valid appearance settings.\n */\nfunction isMiaixzAppearanceEvent(payload: unknown): payload is MiaixzAppearancePayload {\n return (\n isPlainEventObject(payload) &&\n hasExactEventKeys(payload, new Set([\"schemaVersion\", \"value\"])) &&\n payload.schemaVersion === 1 &&\n isMiaixzAppearanceSettings(payload.value)\n );\n}\n\n/**\n * Validates the built-in locale-change payload.\n *\n * @param payload - Untrusted event payload.\n * @returns Whether the payload contains exactly one valid BCP 47 locale.\n */\nfunction isMiaixzLocaleEvent(payload: unknown): payload is MiaixzLocaleChangedEvent {\n if (\n !isPlainEventObject(payload) ||\n !hasExactEventKeys(payload, new Set([\"locale\"])) ||\n typeof payload.locale !== \"string\"\n ) {\n return false;\n }\n try {\n return Intl.getCanonicalLocales(payload.locale).length === 1;\n } catch {\n return false;\n }\n}\n\n/**\n * Validates the built-in SDK configuration payload.\n *\n * @param payload - Untrusted event payload.\n * @returns Whether the payload contains only supported configuration fields and values.\n */\nfunction isMiaixzConfigEvent(payload: unknown): payload is MiaixzSdkConfig {\n return (\n isPlainEventObject(payload) &&\n Object.keys(payload).every((key) => miaixzSdkConfigKeys.has(key)) &&\n isMiaixzSdkConfig(payload)\n );\n}\n\nconst miaixzBuiltinEventValidators: Readonly<Record<string, MiaixzEventValidator>> = Object.freeze({\n \"auth:changed\": isMiaixzAuthStatusEvent,\n \"context:changed\": isMiaixzContextEvent,\n \"appearance:changed\": isMiaixzAppearanceEvent,\n \"locale:changed\": isMiaixzLocaleEvent,\n \"config:changed\": isMiaixzConfigEvent,\n});\n\n/**\n * Creates a localized event error without including rejected payload data.\n *\n * @param code - Stable event error code.\n * @param messageKey - Registered internationalization message key.\n * @returns Localized SDK event error.\n */\nfunction createMiaixzEventError(\n code:\n | \"EVENT_CHANNEL_INVALID\"\n | \"EVENT_VALIDATOR_MISSING\"\n | \"EVENT_PAYLOAD_NOT_CLONEABLE\"\n | \"EVENT_CRYPTO_UNAVAILABLE\",\n messageKey:\n | \"sdk.error.event.channelInvalid\"\n | \"sdk.error.event.validatorMissing\"\n | \"sdk.error.event.payloadNotCloneable\"\n | \"sdk.error.event.cryptoUnavailable\",\n): MiaixzSdkError {\n return new MiaixzSdkError(miaixzDefaultI18n.t(messageKey), { code });\n}\n\n/**\n * Creates a secure UUID for event sources and messages.\n *\n * @returns A cryptographically secure UUID.\n * @throws MiaixzSdkError When the runtime cannot generate secure UUIDs.\n */\nfunction createMiaixzEventId(): string {\n if (typeof globalThis.crypto?.randomUUID !== \"function\") {\n throw createMiaixzEventError(\"EVENT_CRYPTO_UNAVAILABLE\", \"sdk.error.event.cryptoUnavailable\");\n }\n return globalThis.crypto.randomUUID();\n}\n\n/**\n * Validates an untrusted BroadcastChannel envelope without executing accessors.\n *\n * @param value - Broadcast message data to inspect.\n * @returns Whether the value is a complete version-one event envelope.\n */\nfunction isMiaixzEventEnvelope(value: unknown): value is MiaixzEventEnvelope {\n return (\n isPlainEventObject(value) &&\n hasExactEventKeys(value, miaixzEventEnvelopeKeys) &&\n value.version === 1 &&\n typeof value.eventId === \"string\" &&\n miaixzUuidPattern.test(value.eventId) &&\n typeof value.sourceId === \"string\" &&\n miaixzUuidPattern.test(value.sourceId) &&\n typeof value.type === \"string\" &&\n value.type.length > 0\n );\n}\n\n/**\n * Produces a structured-cloned event payload before external delivery.\n *\n * @param payload - Payload to clone.\n * @returns Detached payload clone safe to place in an event envelope.\n * @throws MiaixzSdkError When structured cloning is unavailable or fails.\n */\nfunction cloneMiaixzEventPayload(payload: unknown): unknown {\n if (typeof globalThis.structuredClone !== \"function\") {\n throw createMiaixzEventError(\n \"EVENT_PAYLOAD_NOT_CLONEABLE\",\n \"sdk.error.event.payloadNotCloneable\",\n );\n }\n try {\n return globalThis.structuredClone(payload);\n } catch {\n throw createMiaixzEventError(\n \"EVENT_PAYLOAD_NOT_CLONEABLE\",\n \"sdk.error.event.payloadNotCloneable\",\n );\n }\n}\n\n/**\n * Applies a registered payload validator and the credential-exposure guard safely.\n *\n * @param validator - Event-specific runtime validator.\n * @param payload - Cloned or externally received payload.\n * @returns Whether the payload is valid and free of credential-shaped fields.\n */\nfunction isValidMiaixzEventPayload(validator: MiaixzEventValidator, payload: unknown): boolean {\n try {\n return !containsSensitiveEventData(payload) && validator(payload);\n } catch {\n return false;\n }\n}\n\n/**\n * Typed local and optional same-origin cross-tab event bus.\n *\n * @typeParam Events - Event-name map whose values define payload types.\n * @public\n */\nexport class MiaixzEventBus<Events extends object = MiaixzSdkEventMap> {\n readonly #listeners = new Map<string, Set<MiaixzEventListener<unknown>>>();\n readonly #validators: Readonly<Record<string, MiaixzEventValidator>>;\n readonly #channel: BroadcastChannel | undefined;\n readonly #sourceId: string | undefined;\n readonly #receivedEventIds = new Map<string, number>();\n readonly #messageListener: ((event: MessageEvent<unknown>) => void) | undefined;\n #closed = false;\n\n /**\n * Creates a local-first event bus and an optional validated BroadcastChannel.\n *\n * @param options - Channel factory and application-defined runtime validators.\n * @throws MiaixzSdkError When an explicit channel name or secure UUID capability is invalid.\n */\n constructor(options: MiaixzEventBusOptions = {}) {\n this.#validators = Object.freeze({\n ...options.validators,\n ...miaixzBuiltinEventValidators,\n });\n const channelName = options.channelName ?? false;\n if (channelName === false) {\n this.#channel = undefined;\n this.#sourceId = undefined;\n this.#messageListener = undefined;\n return;\n }\n if (!miaixzEventChannelPattern.test(channelName)) {\n throw createMiaixzEventError(\"EVENT_CHANNEL_INVALID\", \"sdk.error.event.channelInvalid\");\n }\n const hasChannelFactory = typeof options.broadcastChannelFactory === \"function\";\n const hasNativeChannel = typeof globalThis.BroadcastChannel === \"function\";\n if (!hasChannelFactory && !hasNativeChannel) {\n this.#channel = undefined;\n this.#sourceId = undefined;\n this.#messageListener = undefined;\n return;\n }\n const sourceId = createMiaixzEventId();\n const channel = hasChannelFactory\n ? options.broadcastChannelFactory?.(channelName)\n : new globalThis.BroadcastChannel(channelName);\n if (channel === undefined) {\n this.#channel = undefined;\n this.#sourceId = undefined;\n this.#messageListener = undefined;\n return;\n }\n this.#channel = channel;\n this.#sourceId = sourceId;\n this.#messageListener = (event: MessageEvent<unknown>): void => {\n this.#receive(event.data);\n };\n this.#channel.addEventListener(\"message\", this.#messageListener);\n }\n\n /**\n * Registers a typed listener and returns its unsubscribe function.\n *\n * @param type - Event name to observe.\n * @param listener - Callback invoked with the typed event payload.\n * @returns Function that unregisters the listener, or a no-op after closure.\n */\n on<Name extends MiaixzEventName<Events>>(\n type: Name,\n listener: MiaixzEventListener<Events[Name]>,\n ): () => void {\n if (this.#closed) return (): void => undefined;\n const listeners = this.#listeners.get(type) ?? new Set<MiaixzEventListener<unknown>>();\n listeners.add(listener as MiaixzEventListener<unknown>);\n this.#listeners.set(type, listeners);\n return (): void => {\n listeners.delete(listener as MiaixzEventListener<unknown>);\n if (listeners.size === 0) this.#listeners.delete(type);\n };\n }\n\n /**\n * Registers a typed listener that is removed after its first event.\n *\n * @param type - Event name to observe once.\n * @param listener - Callback invoked with the typed event payload.\n * @returns Function that unregisters the listener before delivery.\n */\n once<Name extends MiaixzEventName<Events>>(\n type: Name,\n listener: MiaixzEventListener<Events[Name]>,\n ): () => void {\n if (this.#closed) return (): void => undefined;\n const unsubscribe = this.on(type, (payload) => {\n unsubscribe();\n listener(payload);\n });\n return unsubscribe;\n }\n\n /**\n * Publishes an event synchronously to local listeners before optional cross-tab delivery.\n *\n * @param type - Event name to publish.\n * @param payload - Typed event payload.\n * @param options - Optional cross-tab publication behavior.\n * @throws MiaixzSdkError When the bus is closed, a required validator is missing, cloning fails, or secure UUIDs are unavailable.\n */\n emit<Name extends MiaixzEventName<Events>>(\n type: Name,\n payload: Events[Name],\n options: Readonly<MiaixzEventEmitOptions> = {},\n ): void {\n if (this.#closed) {\n throw createMiaixzEventError(\"EVENT_CHANNEL_INVALID\", \"sdk.error.event.channelInvalid\");\n }\n this.#dispatch(type, payload);\n if (\n options.broadcast === false ||\n this.#channel === undefined ||\n this.#sourceId === undefined\n ) {\n return;\n }\n const validator = Object.hasOwn(this.#validators, type) ? this.#validators[type] : undefined;\n if (validator === undefined) {\n if (options.broadcast === true) {\n throw createMiaixzEventError(\"EVENT_VALIDATOR_MISSING\", \"sdk.error.event.validatorMissing\");\n }\n return;\n }\n const clonedPayload = cloneMiaixzEventPayload(payload);\n if (!isValidMiaixzEventPayload(validator, clonedPayload)) return;\n this.#channel.postMessage({\n version: 1,\n eventId: createMiaixzEventId(),\n sourceId: this.#sourceId,\n type,\n payload: clonedPayload,\n } satisfies MiaixzEventEnvelope);\n }\n\n /**\n * Removes listeners for one event type or all event types.\n *\n * @param type - Optional event name whose listeners should be removed.\n */\n clear(type?: MiaixzEventName<Events>): void {\n if (type === undefined) this.#listeners.clear();\n else this.#listeners.delete(type);\n }\n\n /**\n * Removes listeners and deduplication state, then closes the underlying channel once.\n */\n close(): void {\n if (this.#closed) return;\n this.#closed = true;\n this.clear();\n this.#receivedEventIds.clear();\n if (this.#channel && this.#messageListener) {\n this.#channel.removeEventListener(\"message\", this.#messageListener);\n }\n this.#channel?.close();\n }\n\n /**\n * Delivers one event to current local listeners in registration order.\n *\n * @param type - Event name to dispatch.\n * @param payload - Event payload to deliver.\n */\n #dispatch(type: string, payload: unknown): void {\n for (const listener of this.#listeners.get(type) ?? []) listener(payload);\n }\n\n /**\n * Validates, deduplicates, and dispatches one untrusted channel message.\n *\n * @param value - Message data delivered by BroadcastChannel.\n */\n #receive(value: unknown): void {\n if (this.#closed || !isMiaixzEventEnvelope(value) || value.sourceId === this.#sourceId) return;\n this.#pruneReceivedEventIds(Date.now());\n if (this.#receivedEventIds.has(value.eventId)) return;\n const validator = Object.hasOwn(this.#validators, value.type)\n ? this.#validators[value.type]\n : undefined;\n if (validator === undefined) return;\n if (!isValidMiaixzEventPayload(validator, value.payload)) return;\n this.#receivedEventIds.set(value.eventId, Date.now());\n while (this.#receivedEventIds.size > miaixzEventDeduplicationLimit) {\n const oldest = this.#receivedEventIds.keys().next().value as string | undefined;\n if (oldest === undefined) break;\n this.#receivedEventIds.delete(oldest);\n }\n this.#dispatch(value.type, value.payload);\n }\n\n /**\n * Removes received event identifiers whose fixed five-minute TTL has elapsed.\n *\n * @param now - Current Unix time in milliseconds.\n */\n #pruneReceivedEventIds(now: number): void {\n for (const [eventId, receivedAt] of this.#receivedEventIds) {\n if (now - receivedAt < miaixzEventDeduplicationTtlMs) continue;\n this.#receivedEventIds.delete(eventId);\n }\n }\n}\n\n/**\n * Creates a typed local-first event bus with optional same-origin cross-tab delivery.\n *\n * @typeParam Events - Event-name map whose values define payload types.\n * @param options - Optional channel and runtime-validator configuration.\n * @returns Configured typed event bus.\n * @public\n */\nexport function createMiaixzEventBus<Events extends object = MiaixzSdkEventMap>(\n options?: MiaixzEventBusOptions,\n): MiaixzEventBus<Events> {\n return new MiaixzEventBus<Events>(options);\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/events/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/events/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC","sourcesContent":["export * from \"./events.js\";\n"]}
|