@wordrhyme/plugin 0.1.0-alpha.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/admin/index.d.ts +19 -0
- package/dist/admin/index.js +42 -0
- package/dist/admin/index.js.map +1 -0
- package/dist/artifact.d.ts +17 -0
- package/dist/artifact.js +97 -0
- package/dist/artifact.js.map +1 -0
- package/dist/chunk-3IM3FPTJ.js +1470 -0
- package/dist/chunk-3IM3FPTJ.js.map +1 -0
- package/dist/chunk-6GDCFR67.js +218 -0
- package/dist/chunk-6GDCFR67.js.map +1 -0
- package/dist/chunk-7EM22QYB.js +333 -0
- package/dist/chunk-7EM22QYB.js.map +1 -0
- package/dist/chunk-BI7E5CVM.js +26 -0
- package/dist/chunk-BI7E5CVM.js.map +1 -0
- package/dist/chunk-BVOTSKM2.js +254 -0
- package/dist/chunk-BVOTSKM2.js.map +1 -0
- package/dist/chunk-DY44Q4CK.js +188 -0
- package/dist/chunk-DY44Q4CK.js.map +1 -0
- package/dist/chunk-MZOLSLJ7.js +65 -0
- package/dist/chunk-MZOLSLJ7.js.map +1 -0
- package/dist/chunk-O4AYK3YP.js +156 -0
- package/dist/chunk-O4AYK3YP.js.map +1 -0
- package/dist/chunk-UGMYO6AU.js +37 -0
- package/dist/chunk-UGMYO6AU.js.map +1 -0
- package/dist/client-poND5ovI.d.ts +679 -0
- package/dist/client.d.ts +9 -0
- package/dist/client.js +68 -0
- package/dist/client.js.map +1 -0
- package/dist/dev-utils.d.ts +105 -0
- package/dist/dev-utils.js +35 -0
- package/dist/dev-utils.js.map +1 -0
- package/dist/entity-extensions-CnhKoT4k.d.ts +56 -0
- package/dist/globalization.d.ts +85 -0
- package/dist/globalization.js +53 -0
- package/dist/globalization.js.map +1 -0
- package/dist/index.d.ts +724 -0
- package/dist/index.js +931 -0
- package/dist/index.js.map +1 -0
- package/dist/locale.d.ts +15 -0
- package/dist/locale.js +13 -0
- package/dist/locale.js.map +1 -0
- package/dist/manifest-CTFX-h0w.d.ts +2053 -0
- package/dist/react.d.ts +227 -0
- package/dist/react.js +226 -0
- package/dist/react.js.map +1 -0
- package/dist/release-BDJeO54k.d.ts +230 -0
- package/dist/server.d.ts +115 -0
- package/dist/server.js +194 -0
- package/dist/server.js.map +1 -0
- package/dist/time.d.ts +34 -0
- package/dist/time.js +31 -0
- package/dist/time.js.map +1 -0
- package/dist/trpc.d.ts +44 -0
- package/dist/trpc.js +11 -0
- package/dist/trpc.js.map +1 -0
- package/dist/types-BJo3_91V.d.ts +1997 -0
- package/package.json +92 -0
|
@@ -0,0 +1,333 @@
|
|
|
1
|
+
// src/dev-utils.ts
|
|
2
|
+
var DEV_ADMIN_ENTRY_CHUNK_PATHS = [
|
|
3
|
+
"/static/js/admin.js",
|
|
4
|
+
"/static/js/src_admin_index_tsx.js"
|
|
5
|
+
];
|
|
6
|
+
var DEV_WEB_ENTRY_CHUNK_PATHS = [
|
|
7
|
+
"/static/js/web.js",
|
|
8
|
+
"/static/js/client.js",
|
|
9
|
+
"/static/js/src_web_index_tsx.js",
|
|
10
|
+
"/static/js/src_web_client_tsx.js"
|
|
11
|
+
];
|
|
12
|
+
var DEV_REMOTE_FAILURE_TTL_MS = 3e4;
|
|
13
|
+
var ACTIVE_DEV_REMOTE_PROBE_ATTEMPTS = 20;
|
|
14
|
+
var ACTIVE_DEV_REMOTE_RETRY_DELAY_MS = 250;
|
|
15
|
+
var DEV_PROBE_CACHE_STORAGE_KEY = "wr.plugin-dev-probe-cache";
|
|
16
|
+
var DEV_REMOTE_OPT_IN_QUERY_KEY = "pluginDevRemote";
|
|
17
|
+
var DEV_REMOTE_OPT_IN_STORAGE_KEY = "wr.plugin-dev-remote";
|
|
18
|
+
var devProbeCache = /* @__PURE__ */ new Map();
|
|
19
|
+
function pluginReactOptions() {
|
|
20
|
+
return {
|
|
21
|
+
swcReactOptions: {
|
|
22
|
+
development: false
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
function pluginReactShared(requiredVersion = ">=18.0.0 <20.0.0") {
|
|
27
|
+
const shared = {
|
|
28
|
+
singleton: true,
|
|
29
|
+
eager: true,
|
|
30
|
+
import: false,
|
|
31
|
+
requiredVersion
|
|
32
|
+
};
|
|
33
|
+
return {
|
|
34
|
+
react: { ...shared },
|
|
35
|
+
"react-dom": { ...shared },
|
|
36
|
+
"react/jsx-runtime": { ...shared }
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
function pluginPublicWebShared(reactRequiredVersion = ">=18.0.0 <20.0.0") {
|
|
40
|
+
const hostShared = {
|
|
41
|
+
singleton: true,
|
|
42
|
+
eager: true,
|
|
43
|
+
import: false,
|
|
44
|
+
requiredVersion: "*"
|
|
45
|
+
};
|
|
46
|
+
return {
|
|
47
|
+
...pluginReactShared(reactRequiredVersion),
|
|
48
|
+
"@wordrhyme/plugin/react": { ...hostShared },
|
|
49
|
+
"@trpc/client": { ...hostShared },
|
|
50
|
+
"@trpc/react-query": { ...hostShared },
|
|
51
|
+
"@tanstack/react-query": { ...hostShared }
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
function getPluginDevPort(pluginId) {
|
|
55
|
+
const BASE_PORT = 3010;
|
|
56
|
+
const PORT_RANGE = 100;
|
|
57
|
+
const hash = pluginId.split("").reduce((acc, char) => {
|
|
58
|
+
return acc + char.charCodeAt(0);
|
|
59
|
+
}, 0);
|
|
60
|
+
return BASE_PORT + hash % PORT_RANGE;
|
|
61
|
+
}
|
|
62
|
+
function getPluginDevRemoteEntry(pluginId) {
|
|
63
|
+
const port = getPluginDevPort(pluginId);
|
|
64
|
+
return `http://localhost:${port}/remoteEntry.js`;
|
|
65
|
+
}
|
|
66
|
+
function getPluginWebDevPort(pluginId) {
|
|
67
|
+
return getPluginDevPort(pluginId) + 100;
|
|
68
|
+
}
|
|
69
|
+
function getPluginWebDevRemoteEntry(pluginId) {
|
|
70
|
+
return `http://localhost:${getPluginWebDevPort(pluginId)}/remoteEntry.js`;
|
|
71
|
+
}
|
|
72
|
+
function getPluginMfName(pluginId) {
|
|
73
|
+
const shortId = pluginId.replace(/^com\.wordrhyme\./, "");
|
|
74
|
+
const normalized = shortId.replace(/[^a-zA-Z0-9_$]/g, "_");
|
|
75
|
+
return `plugin_${normalized}`;
|
|
76
|
+
}
|
|
77
|
+
function runtime() {
|
|
78
|
+
return globalThis;
|
|
79
|
+
}
|
|
80
|
+
function readQueryValue(search, key) {
|
|
81
|
+
const query = search?.replace(/^\?/, "");
|
|
82
|
+
if (!query) return null;
|
|
83
|
+
for (const part of query.split("&")) {
|
|
84
|
+
const [rawKey, rawValue = ""] = part.split("=");
|
|
85
|
+
if (!rawKey) continue;
|
|
86
|
+
try {
|
|
87
|
+
if (decodeURIComponent(rawKey.replace(/\+/g, " ")) === key) {
|
|
88
|
+
return decodeURIComponent(rawValue.replace(/\+/g, " "));
|
|
89
|
+
}
|
|
90
|
+
} catch {
|
|
91
|
+
if (rawKey === key) return rawValue;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
function matchesPluginOptIn(rawValue, pluginId) {
|
|
97
|
+
const value = rawValue?.trim();
|
|
98
|
+
if (!value) return false;
|
|
99
|
+
return value === "1" || value.toLowerCase() === "true" || value.toLowerCase() === "all" || value === pluginId;
|
|
100
|
+
}
|
|
101
|
+
function isPluginListed(pluginId, rawValue) {
|
|
102
|
+
return parsePluginList(rawValue).includes(pluginId);
|
|
103
|
+
}
|
|
104
|
+
function parsePluginList(rawValue) {
|
|
105
|
+
if (typeof rawValue !== "string") return [];
|
|
106
|
+
return Array.from(new Set(
|
|
107
|
+
rawValue.split(",").map((value) => value.trim()).filter(Boolean)
|
|
108
|
+
));
|
|
109
|
+
}
|
|
110
|
+
function getPluginDevRuntimeConfig() {
|
|
111
|
+
const value = runtime().__WORDRHYME_PLUGIN_DEV__;
|
|
112
|
+
if (!value || typeof value !== "object") {
|
|
113
|
+
return { enabled: false, remotePlugins: [] };
|
|
114
|
+
}
|
|
115
|
+
const config = value;
|
|
116
|
+
const remotePlugins = Array.isArray(config.remotePlugins) ? Array.from(new Set(config.remotePlugins.filter((pluginId) => typeof pluginId === "string" && pluginId.trim().length > 0).map((pluginId) => pluginId.trim()))) : parsePluginList(config.remotePlugins);
|
|
117
|
+
return {
|
|
118
|
+
enabled: config.enabled === true,
|
|
119
|
+
remotePlugins
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
function isPluginDevRemoteSelected(pluginId) {
|
|
123
|
+
const config = getPluginDevRuntimeConfig();
|
|
124
|
+
return config.enabled && config.remotePlugins.includes(pluginId);
|
|
125
|
+
}
|
|
126
|
+
function isPluginDevRemoteEnabled(pluginId) {
|
|
127
|
+
const host = runtime();
|
|
128
|
+
if (matchesPluginOptIn(readQueryValue(host.location?.search, DEV_REMOTE_OPT_IN_QUERY_KEY), pluginId)) {
|
|
129
|
+
return true;
|
|
130
|
+
}
|
|
131
|
+
try {
|
|
132
|
+
if (matchesPluginOptIn(host.sessionStorage?.getItem(DEV_REMOTE_OPT_IN_STORAGE_KEY) ?? null, pluginId)) {
|
|
133
|
+
return true;
|
|
134
|
+
}
|
|
135
|
+
} catch {
|
|
136
|
+
}
|
|
137
|
+
try {
|
|
138
|
+
if (matchesPluginOptIn(host.localStorage?.getItem(DEV_REMOTE_OPT_IN_STORAGE_KEY) ?? null, pluginId)) {
|
|
139
|
+
return true;
|
|
140
|
+
}
|
|
141
|
+
} catch {
|
|
142
|
+
}
|
|
143
|
+
return false;
|
|
144
|
+
}
|
|
145
|
+
function readStoredProbeCache() {
|
|
146
|
+
try {
|
|
147
|
+
const raw = runtime().sessionStorage?.getItem(DEV_PROBE_CACHE_STORAGE_KEY);
|
|
148
|
+
if (!raw) return {};
|
|
149
|
+
const parsed = JSON.parse(raw);
|
|
150
|
+
return parsed && typeof parsed === "object" ? parsed : {};
|
|
151
|
+
} catch {
|
|
152
|
+
return {};
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
function writeStoredProbeCache(cache) {
|
|
156
|
+
try {
|
|
157
|
+
runtime().sessionStorage?.setItem(DEV_PROBE_CACHE_STORAGE_KEY, JSON.stringify(cache));
|
|
158
|
+
} catch {
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
function probeCacheKey(surface, pluginId, devRemoteEntry) {
|
|
162
|
+
return `${surface}:${pluginId}:${devRemoteEntry}`;
|
|
163
|
+
}
|
|
164
|
+
function getCachedProbeResult(key, now) {
|
|
165
|
+
const cached = devProbeCache.get(key);
|
|
166
|
+
if (cached) {
|
|
167
|
+
if (cached.expiresAt <= now) {
|
|
168
|
+
devProbeCache.delete(key);
|
|
169
|
+
} else {
|
|
170
|
+
return cached.result;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
const storedCache = readStoredProbeCache();
|
|
174
|
+
const stored = storedCache[key];
|
|
175
|
+
if (!stored) return null;
|
|
176
|
+
if (stored.expiresAt <= now) {
|
|
177
|
+
delete storedCache[key];
|
|
178
|
+
writeStoredProbeCache(storedCache);
|
|
179
|
+
devProbeCache.delete(key);
|
|
180
|
+
return null;
|
|
181
|
+
}
|
|
182
|
+
devProbeCache.set(key, stored);
|
|
183
|
+
return stored.result;
|
|
184
|
+
}
|
|
185
|
+
function cacheProbeResult(key, result, ttlMs) {
|
|
186
|
+
const entry = {
|
|
187
|
+
result,
|
|
188
|
+
expiresAt: Date.now() + ttlMs
|
|
189
|
+
};
|
|
190
|
+
devProbeCache.set(key, entry);
|
|
191
|
+
const storedCache = readStoredProbeCache();
|
|
192
|
+
storedCache[key] = entry;
|
|
193
|
+
writeStoredProbeCache(storedCache);
|
|
194
|
+
}
|
|
195
|
+
function createTimeoutSignal(timeoutMs) {
|
|
196
|
+
const host = runtime();
|
|
197
|
+
const timeout = host.AbortSignal?.timeout;
|
|
198
|
+
if (timeout) {
|
|
199
|
+
return { signal: timeout(timeoutMs), cleanup: () => {
|
|
200
|
+
} };
|
|
201
|
+
}
|
|
202
|
+
const Controller = host.AbortController;
|
|
203
|
+
if (!Controller || !host.setTimeout || !host.clearTimeout) {
|
|
204
|
+
return { cleanup: () => {
|
|
205
|
+
} };
|
|
206
|
+
}
|
|
207
|
+
const probeController = new Controller();
|
|
208
|
+
const timeoutId = host.setTimeout(() => probeController.abort(), timeoutMs);
|
|
209
|
+
return {
|
|
210
|
+
signal: probeController.signal,
|
|
211
|
+
cleanup: () => host.clearTimeout?.(timeoutId)
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
function waitForRetry(delayMs) {
|
|
215
|
+
const schedule = runtime().setTimeout;
|
|
216
|
+
if (!schedule || delayMs <= 0) return Promise.resolve();
|
|
217
|
+
return new Promise((resolve) => {
|
|
218
|
+
schedule(() => resolve(), delayMs);
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
function resolveChunkUrl(path, devRemoteEntry) {
|
|
222
|
+
const originMatch = devRemoteEntry.match(/^(https?:\/\/[^/?#]+)(?:[/?#]|$)/);
|
|
223
|
+
if (originMatch?.[1] && path.startsWith("/")) {
|
|
224
|
+
return `${originMatch[1]}${path}`;
|
|
225
|
+
}
|
|
226
|
+
const base = devRemoteEntry.replace(/[?#].*$/, "").replace(/\/[^/]*$/, "/");
|
|
227
|
+
return `${base}${path.replace(/^\//, "")}`;
|
|
228
|
+
}
|
|
229
|
+
async function requestOk(url, signal) {
|
|
230
|
+
const fetcher = runtime().fetch;
|
|
231
|
+
if (!fetcher) return false;
|
|
232
|
+
const response = await fetcher(url, {
|
|
233
|
+
method: "HEAD",
|
|
234
|
+
...signal ? { signal } : {}
|
|
235
|
+
});
|
|
236
|
+
if (response.ok) return true;
|
|
237
|
+
if (response.status !== 405) return false;
|
|
238
|
+
const fallbackResponse = await fetcher(url, {
|
|
239
|
+
method: "GET",
|
|
240
|
+
...signal ? { signal } : {}
|
|
241
|
+
});
|
|
242
|
+
return fallbackResponse.ok;
|
|
243
|
+
}
|
|
244
|
+
async function canLoadDevRemote(devRemoteEntry, surface, signal, entryChunkPaths) {
|
|
245
|
+
if (!await requestOk(devRemoteEntry, signal)) {
|
|
246
|
+
return false;
|
|
247
|
+
}
|
|
248
|
+
const chunkPaths = entryChunkPaths ?? (surface === "web" ? DEV_WEB_ENTRY_CHUNK_PATHS : DEV_ADMIN_ENTRY_CHUNK_PATHS);
|
|
249
|
+
for (const path of chunkPaths) {
|
|
250
|
+
if (await requestOk(resolveChunkUrl(path, devRemoteEntry), signal)) {
|
|
251
|
+
return true;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
return false;
|
|
255
|
+
}
|
|
256
|
+
function defaultDevRemoteEntry(pluginId, surface) {
|
|
257
|
+
return surface === "web" ? getPluginWebDevRemoteEntry(pluginId) : getPluginDevRemoteEntry(pluginId);
|
|
258
|
+
}
|
|
259
|
+
async function resolvePluginRemoteEntry({
|
|
260
|
+
pluginId,
|
|
261
|
+
manifestRemoteEntry,
|
|
262
|
+
devRemoteEntry,
|
|
263
|
+
surface = "admin",
|
|
264
|
+
isDev = false,
|
|
265
|
+
devRemoteEnabled = false,
|
|
266
|
+
probeTimeoutMs = 1500,
|
|
267
|
+
probeAttempts,
|
|
268
|
+
probeRetryDelayMs = ACTIVE_DEV_REMOTE_RETRY_DELAY_MS,
|
|
269
|
+
entryChunkPaths
|
|
270
|
+
}) {
|
|
271
|
+
const manifestResult = {
|
|
272
|
+
remoteEntry: manifestRemoteEntry,
|
|
273
|
+
source: "manifest"
|
|
274
|
+
};
|
|
275
|
+
if (!isDev || !devRemoteEnabled && !isPluginDevRemoteEnabled(pluginId)) {
|
|
276
|
+
return manifestResult;
|
|
277
|
+
}
|
|
278
|
+
const devUrl = devRemoteEntry?.trim() || defaultDevRemoteEntry(pluginId, surface);
|
|
279
|
+
const cacheKey = probeCacheKey(surface, pluginId, devUrl);
|
|
280
|
+
const cachedResult = getCachedProbeResult(cacheKey, Date.now());
|
|
281
|
+
if (cachedResult && !(devRemoteEnabled && cachedResult.source === "manifest")) {
|
|
282
|
+
return cachedResult;
|
|
283
|
+
}
|
|
284
|
+
const attempts = Math.max(
|
|
285
|
+
1,
|
|
286
|
+
Math.trunc(probeAttempts ?? (devRemoteEnabled ? ACTIVE_DEV_REMOTE_PROBE_ATTEMPTS : 1))
|
|
287
|
+
);
|
|
288
|
+
for (let attempt = 0; attempt < attempts; attempt += 1) {
|
|
289
|
+
const { signal, cleanup } = createTimeoutSignal(probeTimeoutMs);
|
|
290
|
+
try {
|
|
291
|
+
if (await canLoadDevRemote(devUrl, surface, signal, entryChunkPaths)) {
|
|
292
|
+
return {
|
|
293
|
+
remoteEntry: devUrl,
|
|
294
|
+
source: "dev"
|
|
295
|
+
};
|
|
296
|
+
}
|
|
297
|
+
} catch {
|
|
298
|
+
} finally {
|
|
299
|
+
cleanup();
|
|
300
|
+
}
|
|
301
|
+
if (attempt + 1 < attempts) {
|
|
302
|
+
await waitForRetry(probeRetryDelayMs);
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
console.info(`[Plugin] ${pluginId}: ${surface} dev server \u4E0D\u5728\u7EBF\uFF0C\u4F7F\u7528\u9884\u6784\u5EFA\u9759\u6001\u6587\u4EF6`);
|
|
306
|
+
if (!devRemoteEnabled) {
|
|
307
|
+
cacheProbeResult(cacheKey, manifestResult, DEV_REMOTE_FAILURE_TTL_MS);
|
|
308
|
+
}
|
|
309
|
+
return manifestResult;
|
|
310
|
+
}
|
|
311
|
+
function appendRemoteEntryCacheBust(remoteEntry, value) {
|
|
312
|
+
const separator = remoteEntry.includes("?") ? "&" : "?";
|
|
313
|
+
return `${remoteEntry}${separator}t=${encodeURIComponent(String(value))}`;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
export {
|
|
317
|
+
pluginReactOptions,
|
|
318
|
+
pluginReactShared,
|
|
319
|
+
pluginPublicWebShared,
|
|
320
|
+
getPluginDevPort,
|
|
321
|
+
getPluginDevRemoteEntry,
|
|
322
|
+
getPluginWebDevPort,
|
|
323
|
+
getPluginWebDevRemoteEntry,
|
|
324
|
+
getPluginMfName,
|
|
325
|
+
isPluginListed,
|
|
326
|
+
parsePluginList,
|
|
327
|
+
getPluginDevRuntimeConfig,
|
|
328
|
+
isPluginDevRemoteSelected,
|
|
329
|
+
isPluginDevRemoteEnabled,
|
|
330
|
+
resolvePluginRemoteEntry,
|
|
331
|
+
appendRemoteEntryCacheBust
|
|
332
|
+
};
|
|
333
|
+
//# sourceMappingURL=chunk-7EM22QYB.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/dev-utils.ts"],"sourcesContent":["/**\n * Plugin Development Utilities\n * \n * Shared utilities for plugin development, including automatic port assignment.\n */\n\nexport type PluginRemoteSurface = 'admin' | 'web';\n\nexport interface PluginDevRuntimeConfig {\n enabled: boolean;\n remotePlugins: string[];\n}\n\nexport interface PluginReactOptions {\n swcReactOptions: {\n development: boolean;\n };\n}\n\nexport interface PluginReactSharedOptions {\n singleton: true;\n eager: true;\n import: false;\n requiredVersion: string;\n}\n\nexport type PluginPublicWebSharedName =\n | 'react'\n | 'react-dom'\n | 'react/jsx-runtime'\n | '@wordrhyme/plugin/react'\n | '@trpc/client'\n | '@trpc/react-query'\n | '@tanstack/react-query';\n\nexport interface ResolvedPluginRemoteEntry {\n remoteEntry: string;\n source: 'dev' | 'manifest';\n}\n\nexport interface ResolvePluginRemoteEntryOptions {\n pluginId: string;\n manifestRemoteEntry: string;\n devRemoteEntry?: string | undefined;\n surface?: PluginRemoteSurface | undefined;\n isDev?: boolean | undefined;\n devRemoteEnabled?: boolean | undefined;\n probeTimeoutMs?: number | undefined;\n probeAttempts?: number | undefined;\n probeRetryDelayMs?: number | undefined;\n entryChunkPaths?: readonly string[] | undefined;\n}\n\ninterface DevProbeCacheEntry {\n result: ResolvedPluginRemoteEntry;\n expiresAt: number;\n}\n\ninterface StorageLike {\n getItem(key: string): string | null;\n setItem(key: string, value: string): void;\n}\n\ninterface RuntimeLike {\n __WORDRHYME_PLUGIN_DEV__?: unknown;\n location?: { search?: string | undefined } | undefined;\n sessionStorage?: StorageLike | undefined;\n localStorage?: StorageLike | undefined;\n fetch?: ((url: string, init: { method: 'HEAD' | 'GET'; signal?: unknown }) => Promise<{ ok: boolean; status: number }>) | undefined;\n AbortSignal?: { timeout?: (ms: number) => unknown } | undefined;\n AbortController?: (new () => { signal: unknown; abort: () => void }) | undefined;\n setTimeout?: ((handler: () => void, timeoutMs: number) => unknown) | undefined;\n clearTimeout?: ((handle: unknown) => void) | undefined;\n}\n\nconst DEV_ADMIN_ENTRY_CHUNK_PATHS = [\n '/static/js/admin.js',\n '/static/js/src_admin_index_tsx.js',\n] as const;\n\nconst DEV_WEB_ENTRY_CHUNK_PATHS = [\n '/static/js/web.js',\n '/static/js/client.js',\n '/static/js/src_web_index_tsx.js',\n '/static/js/src_web_client_tsx.js',\n] as const;\n\nconst DEV_REMOTE_FAILURE_TTL_MS = 30_000;\nconst ACTIVE_DEV_REMOTE_PROBE_ATTEMPTS = 20;\nconst ACTIVE_DEV_REMOTE_RETRY_DELAY_MS = 250;\nconst DEV_PROBE_CACHE_STORAGE_KEY = 'wr.plugin-dev-probe-cache';\nconst DEV_REMOTE_OPT_IN_QUERY_KEY = 'pluginDevRemote';\nconst DEV_REMOTE_OPT_IN_STORAGE_KEY = 'wr.plugin-dev-remote';\nconst devProbeCache = new Map<string, DevProbeCacheEntry>();\n\n/**\n * Keep plugin remotes on the production-compatible JSX runtime even while the\n * remote dev server and React Fast Refresh are active. A prebuilt Dev Host uses\n * production React, which cannot execute react-jsx-dev-runtime safely.\n */\nexport function pluginReactOptions(): PluginReactOptions {\n return {\n swcReactOptions: {\n development: false,\n },\n };\n}\n\nexport function pluginReactShared(\n requiredVersion = '>=18.0.0 <20.0.0',\n): Record<'react' | 'react-dom' | 'react/jsx-runtime', PluginReactSharedOptions> {\n const shared = {\n singleton: true,\n eager: true,\n import: false,\n requiredVersion,\n } as const;\n\n return {\n react: { ...shared },\n 'react-dom': { ...shared },\n 'react/jsx-runtime': { ...shared },\n };\n}\n\n/**\n * Module Federation shared contract for public Web React remotes.\n *\n * The public Host injects its tRPC client into @wordrhyme/plugin/react, so Web\n * remotes must consume the Host copy of the SDK and its query dependencies.\n */\nexport function pluginPublicWebShared(\n reactRequiredVersion = '>=18.0.0 <20.0.0',\n): Record<PluginPublicWebSharedName, PluginReactSharedOptions> {\n const hostShared = {\n singleton: true,\n eager: true,\n import: false,\n requiredVersion: '*',\n } as const;\n\n return {\n ...pluginReactShared(reactRequiredVersion),\n '@wordrhyme/plugin/react': { ...hostShared },\n '@trpc/client': { ...hostShared },\n '@trpc/react-query': { ...hostShared },\n '@tanstack/react-query': { ...hostShared },\n };\n}\n\n/**\n * Calculate a deterministic dev port for a plugin based on its ID.\n * \n * This allows multiple plugins to run simultaneously without port conflicts,\n * while keeping the port assignment automatic and predictable.\n * \n * Port range: 3010-3109 (100 possible ports)\n * \n * @param pluginId - Full plugin ID (e.g., \"com.wordrhyme.hello-world\")\n * @returns Port number for the plugin's dev server\n * \n * @example\n * getPluginDevPort('com.wordrhyme.hello-world') // e.g., 3042\n * getPluginDevPort('com.wordrhyme.analytics') // e.g., 3015\n */\nexport function getPluginDevPort(pluginId: string): number {\n const BASE_PORT = 3010;\n const PORT_RANGE = 100;\n\n // Simple hash based on character codes\n const hash = pluginId.split('').reduce((acc, char) => {\n return acc + char.charCodeAt(0);\n }, 0);\n\n return BASE_PORT + (hash % PORT_RANGE);\n}\n\n/**\n * Get the dev remote entry URL for a plugin.\n * \n * @param pluginId - Full plugin ID\n * @returns Full URL to the plugin's remoteEntry.js in dev mode\n */\nexport function getPluginDevRemoteEntry(pluginId: string): string {\n const port = getPluginDevPort(pluginId);\n return `http://localhost:${port}/remoteEntry.js`;\n}\n\n/**\n * Calculate a deterministic public web dev port for a plugin.\n *\n * Public web remotes run beside admin remotes, so they use the same stable\n * plugin hash with a +100 offset.\n */\nexport function getPluginWebDevPort(pluginId: string): number {\n return getPluginDevPort(pluginId) + 100;\n}\n\n/**\n * Get the public web dev remote entry URL for a plugin.\n */\nexport function getPluginWebDevRemoteEntry(pluginId: string): string {\n return `http://localhost:${getPluginWebDevPort(pluginId)}/remoteEntry.js`;\n}\n\n/**\n * Normalize plugin ID to a valid Module Federation name.\n * \n * MF names must be valid JavaScript identifiers.\n * \"com.wordrhyme.hello-world\" → \"plugin_hello_world\"\n * \n * @param pluginId - Full plugin ID\n * @returns Valid MF module name\n */\nexport function getPluginMfName(pluginId: string): string {\n // Keep the established short name for official plugins, while accepting\n // every valid reverse-domain plugin id from the public Plugin Contract.\n const shortId = pluginId.replace(/^com\\.wordrhyme\\./, '');\n const normalized = shortId.replace(/[^a-zA-Z0-9_$]/g, '_');\n return `plugin_${normalized}`;\n}\n\nfunction runtime(): RuntimeLike {\n return globalThis as unknown as RuntimeLike;\n}\n\nfunction readQueryValue(search: string | undefined, key: string): string | null {\n const query = search?.replace(/^\\?/, '');\n if (!query) return null;\n\n for (const part of query.split('&')) {\n const [rawKey, rawValue = ''] = part.split('=');\n if (!rawKey) continue;\n try {\n if (decodeURIComponent(rawKey.replace(/\\+/g, ' ')) === key) {\n return decodeURIComponent(rawValue.replace(/\\+/g, ' '));\n }\n } catch {\n if (rawKey === key) return rawValue;\n }\n }\n\n return null;\n}\n\nfunction matchesPluginOptIn(rawValue: string | null, pluginId: string): boolean {\n const value = rawValue?.trim();\n if (!value) return false;\n\n return value === '1'\n || value.toLowerCase() === 'true'\n || value.toLowerCase() === 'all'\n || value === pluginId;\n}\n\nexport function isPluginListed(pluginId: string, rawValue: unknown): boolean {\n return parsePluginList(rawValue).includes(pluginId);\n}\n\nexport function parsePluginList(rawValue: unknown): string[] {\n if (typeof rawValue !== 'string') return [];\n\n return Array.from(new Set(\n rawValue\n .split(',')\n .map((value) => value.trim())\n .filter(Boolean),\n ));\n}\n\nexport function getPluginDevRuntimeConfig(): PluginDevRuntimeConfig {\n const value = runtime().__WORDRHYME_PLUGIN_DEV__;\n if (!value || typeof value !== 'object') {\n return { enabled: false, remotePlugins: [] };\n }\n\n const config = value as { enabled?: unknown; remotePlugins?: unknown };\n const remotePlugins = Array.isArray(config.remotePlugins)\n ? Array.from(new Set(config.remotePlugins.filter((pluginId): pluginId is string => (\n typeof pluginId === 'string' && pluginId.trim().length > 0\n )).map((pluginId) => pluginId.trim())))\n : parsePluginList(config.remotePlugins);\n\n return {\n enabled: config.enabled === true,\n remotePlugins,\n };\n}\n\nexport function isPluginDevRemoteSelected(pluginId: string): boolean {\n const config = getPluginDevRuntimeConfig();\n return config.enabled && config.remotePlugins.includes(pluginId);\n}\n\nexport function isPluginDevRemoteEnabled(pluginId: string): boolean {\n const host = runtime();\n if (matchesPluginOptIn(readQueryValue(host.location?.search, DEV_REMOTE_OPT_IN_QUERY_KEY), pluginId)) {\n return true;\n }\n\n try {\n if (matchesPluginOptIn(host.sessionStorage?.getItem(DEV_REMOTE_OPT_IN_STORAGE_KEY) ?? null, pluginId)) {\n return true;\n }\n } catch {\n // Ignore storage access failures.\n }\n\n try {\n if (matchesPluginOptIn(host.localStorage?.getItem(DEV_REMOTE_OPT_IN_STORAGE_KEY) ?? null, pluginId)) {\n return true;\n }\n } catch {\n // Ignore storage access failures.\n }\n\n return false;\n}\n\nfunction readStoredProbeCache(): Record<string, DevProbeCacheEntry> {\n try {\n const raw = runtime().sessionStorage?.getItem(DEV_PROBE_CACHE_STORAGE_KEY);\n if (!raw) return {};\n const parsed = JSON.parse(raw) as Record<string, DevProbeCacheEntry> | null;\n return parsed && typeof parsed === 'object' ? parsed : {};\n } catch {\n return {};\n }\n}\n\nfunction writeStoredProbeCache(cache: Record<string, DevProbeCacheEntry>) {\n try {\n runtime().sessionStorage?.setItem(DEV_PROBE_CACHE_STORAGE_KEY, JSON.stringify(cache));\n } catch {\n // Ignore storage failures and rely on in-memory cache.\n }\n}\n\nfunction probeCacheKey(surface: PluginRemoteSurface, pluginId: string, devRemoteEntry: string): string {\n return `${surface}:${pluginId}:${devRemoteEntry}`;\n}\n\nfunction getCachedProbeResult(key: string, now: number): ResolvedPluginRemoteEntry | null {\n const cached = devProbeCache.get(key);\n if (cached) {\n if (cached.expiresAt <= now) {\n devProbeCache.delete(key);\n } else {\n return cached.result;\n }\n }\n\n const storedCache = readStoredProbeCache();\n const stored = storedCache[key];\n if (!stored) return null;\n if (stored.expiresAt <= now) {\n delete storedCache[key];\n writeStoredProbeCache(storedCache);\n devProbeCache.delete(key);\n return null;\n }\n devProbeCache.set(key, stored);\n return stored.result;\n}\n\nfunction cacheProbeResult(key: string, result: ResolvedPluginRemoteEntry, ttlMs: number) {\n const entry = {\n result,\n expiresAt: Date.now() + ttlMs,\n };\n devProbeCache.set(key, entry);\n\n const storedCache = readStoredProbeCache();\n storedCache[key] = entry;\n writeStoredProbeCache(storedCache);\n}\n\nfunction createTimeoutSignal(timeoutMs: number): { signal?: unknown; cleanup: () => void } {\n const host = runtime();\n const timeout = host.AbortSignal?.timeout;\n if (timeout) {\n return { signal: timeout(timeoutMs), cleanup: () => {} };\n }\n\n const Controller = host.AbortController;\n if (!Controller || !host.setTimeout || !host.clearTimeout) {\n return { cleanup: () => {} };\n }\n\n const probeController = new Controller();\n const timeoutId = host.setTimeout(() => probeController.abort(), timeoutMs);\n return {\n signal: probeController.signal,\n cleanup: () => host.clearTimeout?.(timeoutId),\n };\n}\n\nfunction waitForRetry(delayMs: number): Promise<void> {\n const schedule = runtime().setTimeout;\n if (!schedule || delayMs <= 0) return Promise.resolve();\n\n return new Promise((resolve) => {\n schedule(() => resolve(), delayMs);\n });\n}\n\nfunction resolveChunkUrl(path: string, devRemoteEntry: string): string {\n const originMatch = devRemoteEntry.match(/^(https?:\\/\\/[^/?#]+)(?:[/?#]|$)/);\n if (originMatch?.[1] && path.startsWith('/')) {\n return `${originMatch[1]}${path}`;\n }\n\n const base = devRemoteEntry.replace(/[?#].*$/, '').replace(/\\/[^/]*$/, '/');\n return `${base}${path.replace(/^\\//, '')}`;\n}\n\nasync function requestOk(url: string, signal: unknown): Promise<boolean> {\n const fetcher = runtime().fetch;\n if (!fetcher) return false;\n\n const response = await fetcher(url, {\n method: 'HEAD',\n ...(signal ? { signal } : {}),\n });\n\n if (response.ok) return true;\n if (response.status !== 405) return false;\n\n const fallbackResponse = await fetcher(url, {\n method: 'GET',\n ...(signal ? { signal } : {}),\n });\n return fallbackResponse.ok;\n}\n\nasync function canLoadDevRemote(\n devRemoteEntry: string,\n surface: PluginRemoteSurface,\n signal: unknown,\n entryChunkPaths?: readonly string[] | undefined,\n): Promise<boolean> {\n if (!await requestOk(devRemoteEntry, signal)) {\n return false;\n }\n\n const chunkPaths = entryChunkPaths\n ?? (surface === 'web' ? DEV_WEB_ENTRY_CHUNK_PATHS : DEV_ADMIN_ENTRY_CHUNK_PATHS);\n for (const path of chunkPaths) {\n if (await requestOk(resolveChunkUrl(path, devRemoteEntry), signal)) {\n return true;\n }\n }\n\n return false;\n}\n\nfunction defaultDevRemoteEntry(pluginId: string, surface: PluginRemoteSurface): string {\n return surface === 'web'\n ? getPluginWebDevRemoteEntry(pluginId)\n : getPluginDevRemoteEntry(pluginId);\n}\n\nexport async function resolvePluginRemoteEntry({\n pluginId,\n manifestRemoteEntry,\n devRemoteEntry,\n surface = 'admin',\n isDev = false,\n devRemoteEnabled = false,\n probeTimeoutMs = 1500,\n probeAttempts,\n probeRetryDelayMs = ACTIVE_DEV_REMOTE_RETRY_DELAY_MS,\n entryChunkPaths,\n}: ResolvePluginRemoteEntryOptions): Promise<ResolvedPluginRemoteEntry> {\n const manifestResult = {\n remoteEntry: manifestRemoteEntry,\n source: 'manifest',\n } as const;\n\n if (!isDev || (!devRemoteEnabled && !isPluginDevRemoteEnabled(pluginId))) {\n return manifestResult;\n }\n\n const devUrl = devRemoteEntry?.trim() || defaultDevRemoteEntry(pluginId, surface);\n const cacheKey = probeCacheKey(surface, pluginId, devUrl);\n const cachedResult = getCachedProbeResult(cacheKey, Date.now());\n if (cachedResult && !(devRemoteEnabled && cachedResult.source === 'manifest')) {\n return cachedResult;\n }\n\n const attempts = Math.max(\n 1,\n Math.trunc(probeAttempts ?? (devRemoteEnabled ? ACTIVE_DEV_REMOTE_PROBE_ATTEMPTS : 1)),\n );\n\n for (let attempt = 0; attempt < attempts; attempt += 1) {\n const { signal, cleanup } = createTimeoutSignal(probeTimeoutMs);\n\n try {\n if (await canLoadDevRemote(devUrl, surface, signal, entryChunkPaths)) {\n return {\n remoteEntry: devUrl,\n source: 'dev',\n };\n }\n } catch {\n // The selected dev process may still be starting. Retry below when explicitly active.\n } finally {\n cleanup();\n }\n\n if (attempt + 1 < attempts) {\n await waitForRetry(probeRetryDelayMs);\n }\n }\n\n console.info(`[Plugin] ${pluginId}: ${surface} dev server 不在线,使用预构建静态文件`);\n if (!devRemoteEnabled) {\n cacheProbeResult(cacheKey, manifestResult, DEV_REMOTE_FAILURE_TTL_MS);\n }\n return manifestResult;\n}\n\nexport function appendRemoteEntryCacheBust(remoteEntry: string, value: string | number): string {\n const separator = remoteEntry.includes('?') ? '&' : '?';\n return `${remoteEntry}${separator}t=${encodeURIComponent(String(value))}`;\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// src/locale.ts
|
|
2
|
+
var DEFAULT_LOCALE = "zh-CN";
|
|
3
|
+
function canonicalizeLocale(value) {
|
|
4
|
+
if (typeof value !== "string") return void 0;
|
|
5
|
+
const locale = value.trim();
|
|
6
|
+
if (!locale || locale === "*") return void 0;
|
|
7
|
+
try {
|
|
8
|
+
return Intl.getCanonicalLocales(locale)[0];
|
|
9
|
+
} catch {
|
|
10
|
+
return void 0;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
function isLocale(value) {
|
|
14
|
+
return canonicalizeLocale(value) !== void 0;
|
|
15
|
+
}
|
|
16
|
+
function normalizeLocale(value, fallback = DEFAULT_LOCALE) {
|
|
17
|
+
return canonicalizeLocale(value) ?? canonicalizeLocale(fallback) ?? DEFAULT_LOCALE;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export {
|
|
21
|
+
DEFAULT_LOCALE,
|
|
22
|
+
canonicalizeLocale,
|
|
23
|
+
isLocale,
|
|
24
|
+
normalizeLocale
|
|
25
|
+
};
|
|
26
|
+
//# sourceMappingURL=chunk-BI7E5CVM.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/locale.ts"],"sourcesContent":["/**\n * Shared locale contract for every WordRhyme runtime surface.\n *\n * Locale parsing delegates to the platform Intl implementation so every\n * caller accepts the same BCP 47 grammar and produces the same casing.\n */\n\nexport const DEFAULT_LOCALE = 'zh-CN';\n\n/** Return the canonical BCP 47 representation of a locale value. */\nexport function canonicalizeLocale(value: unknown): string | undefined {\n if (typeof value !== 'string') return undefined;\n\n const locale = value.trim();\n if (!locale || locale === '*') return undefined;\n\n try {\n return Intl.getCanonicalLocales(locale)[0];\n } catch {\n return undefined;\n }\n}\n\n/** Check whether a value is a valid BCP 47 locale. */\nexport function isLocale(value: unknown): value is string {\n return canonicalizeLocale(value) !== undefined;\n}\n\n/** Canonicalize a locale or use the shared system fallback. */\nexport function normalizeLocale(value: unknown, fallback = DEFAULT_LOCALE): string {\n return canonicalizeLocale(value) ?? canonicalizeLocale(fallback) ?? DEFAULT_LOCALE;\n}\n"],"mappings":";AAOO,IAAM,iBAAiB;AAGvB,SAAS,mBAAmB,OAAoC;AACnE,MAAI,OAAO,UAAU,SAAU,QAAO;AAEtC,QAAM,SAAS,MAAM,KAAK;AAC1B,MAAI,CAAC,UAAU,WAAW,IAAK,QAAO;AAEtC,MAAI;AACA,WAAO,KAAK,oBAAoB,MAAM,EAAE,CAAC;AAAA,EAC7C,QAAQ;AACJ,WAAO;AAAA,EACX;AACJ;AAGO,SAAS,SAAS,OAAiC;AACtD,SAAO,mBAAmB,KAAK,MAAM;AACzC;AAGO,SAAS,gBAAgB,OAAgB,WAAW,gBAAwB;AAC/E,SAAO,mBAAmB,KAAK,KAAK,mBAAmB,QAAQ,KAAK;AACxE;","names":[]}
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
import {
|
|
2
|
+
DEFAULT_LOCALE,
|
|
3
|
+
canonicalizeLocale,
|
|
4
|
+
normalizeLocale
|
|
5
|
+
} from "./chunk-BI7E5CVM.js";
|
|
6
|
+
|
|
7
|
+
// src/globalization.ts
|
|
8
|
+
import Decimal from "decimal.js";
|
|
9
|
+
Decimal.set({
|
|
10
|
+
precision: 28,
|
|
11
|
+
rounding: Decimal.ROUND_HALF_EVEN
|
|
12
|
+
});
|
|
13
|
+
var DEFAULT_CURRENCY = {
|
|
14
|
+
code: "USD",
|
|
15
|
+
nameI18n: { "en-US": "US Dollar", "zh-CN": "\u7F8E\u5143" },
|
|
16
|
+
symbol: "$",
|
|
17
|
+
decimalDigits: 2,
|
|
18
|
+
isBase: true,
|
|
19
|
+
currentRate: "1"
|
|
20
|
+
};
|
|
21
|
+
var RTL_LANGUAGE_CODES = /* @__PURE__ */ new Set(["ar", "he", "fa", "ur"]);
|
|
22
|
+
function isRecord(value) {
|
|
23
|
+
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
24
|
+
}
|
|
25
|
+
function isCurrencyInfo(value) {
|
|
26
|
+
return isRecord(value) && typeof value["code"] === "string";
|
|
27
|
+
}
|
|
28
|
+
function isCurrencyCatalog(value) {
|
|
29
|
+
return Array.isArray(value);
|
|
30
|
+
}
|
|
31
|
+
function readRate(currency) {
|
|
32
|
+
if (currency.isBase) return new Decimal(1);
|
|
33
|
+
try {
|
|
34
|
+
const rate = new Decimal(currency.currentRate ?? "1");
|
|
35
|
+
return rate.gt(0) ? rate : new Decimal(1);
|
|
36
|
+
} catch {
|
|
37
|
+
return new Decimal(1);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
function minorUnitFactor(currency) {
|
|
41
|
+
return new Decimal(10).pow(Math.max(0, currency.decimalDigits));
|
|
42
|
+
}
|
|
43
|
+
function getTextDirection(locale) {
|
|
44
|
+
const languageCode = normalizeLocale(locale, DEFAULT_LOCALE).split("-")[0]?.toLowerCase();
|
|
45
|
+
return languageCode && RTL_LANGUAGE_CODES.has(languageCode) ? "rtl" : "ltr";
|
|
46
|
+
}
|
|
47
|
+
function normalizeCurrencyInfo(currency, fallback = DEFAULT_CURRENCY) {
|
|
48
|
+
const code = currency.code.toUpperCase();
|
|
49
|
+
return {
|
|
50
|
+
code,
|
|
51
|
+
nameI18n: currency.nameI18n ?? (code === fallback.code ? fallback.nameI18n : {}),
|
|
52
|
+
symbol: currency.symbol ?? (code === fallback.code ? fallback.symbol : code),
|
|
53
|
+
decimalDigits: typeof currency.decimalDigits === "number" ? currency.decimalDigits : fallback.decimalDigits,
|
|
54
|
+
isBase: currency.isBase ?? code === fallback.code,
|
|
55
|
+
currentRate: currency.isBase ? "1" : currency.currentRate ?? null
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
function normalizeCurrencies(currencies) {
|
|
59
|
+
if (currencies.length === 0) return [DEFAULT_CURRENCY];
|
|
60
|
+
const normalized = currencies.map((currency) => normalizeCurrencyInfo(currency));
|
|
61
|
+
const hasBase = normalized.some((currency) => currency.isBase);
|
|
62
|
+
if (hasBase) return normalized;
|
|
63
|
+
return normalized.map((currency, index) => ({
|
|
64
|
+
...currency,
|
|
65
|
+
isBase: index === 0,
|
|
66
|
+
currentRate: index === 0 ? "1" : currency.currentRate
|
|
67
|
+
}));
|
|
68
|
+
}
|
|
69
|
+
function findCurrency(currencies, currencyCode) {
|
|
70
|
+
if (!currencyCode) return void 0;
|
|
71
|
+
const normalizedCode = currencyCode.toUpperCase();
|
|
72
|
+
return currencies.find((currency) => currency.code.toUpperCase() === normalizedCode);
|
|
73
|
+
}
|
|
74
|
+
function getBaseCurrency(currencies) {
|
|
75
|
+
return currencies.find((currency) => currency.isBase) ?? currencies[0] ?? DEFAULT_CURRENCY;
|
|
76
|
+
}
|
|
77
|
+
function resolveCurrencyInfo(currency, currencies, fallback = getBaseCurrency(currencies)) {
|
|
78
|
+
if (isCurrencyInfo(currency)) return normalizeCurrencyInfo(currency, fallback);
|
|
79
|
+
return findCurrency(currencies, currency) ?? normalizeCurrencyInfo({ code: currency ?? fallback.code }, fallback);
|
|
80
|
+
}
|
|
81
|
+
function bankersRound(value, decimalDigits) {
|
|
82
|
+
const decimal = new Decimal(value);
|
|
83
|
+
const factor = new Decimal(10).pow(decimalDigits);
|
|
84
|
+
return decimal.times(factor).round().div(factor).toNumber();
|
|
85
|
+
}
|
|
86
|
+
function toCents(displayAmount, decimalDigits) {
|
|
87
|
+
const factor = new Decimal(10).pow(decimalDigits);
|
|
88
|
+
return new Decimal(displayAmount).times(factor).round().toNumber();
|
|
89
|
+
}
|
|
90
|
+
function fromCents(cents, decimalDigits) {
|
|
91
|
+
const factor = new Decimal(10).pow(decimalDigits);
|
|
92
|
+
return new Decimal(cents).div(factor).toNumber();
|
|
93
|
+
}
|
|
94
|
+
function convertCurrency(cents, fromCurrency, toCurrency, currenciesOrBaseCurrency) {
|
|
95
|
+
if (fromCurrency.code === toCurrency.code) return cents;
|
|
96
|
+
const catalog = isCurrencyCatalog(currenciesOrBaseCurrency) ? currenciesOrBaseCurrency : [currenciesOrBaseCurrency, fromCurrency, toCurrency];
|
|
97
|
+
const baseCurrency = isCurrencyCatalog(currenciesOrBaseCurrency) ? getBaseCurrency(catalog) : currenciesOrBaseCurrency;
|
|
98
|
+
const from = normalizeCurrencyInfo(fromCurrency, baseCurrency);
|
|
99
|
+
const to = normalizeCurrencyInfo(toCurrency, baseCurrency);
|
|
100
|
+
const sourceMinor = new Decimal(cents);
|
|
101
|
+
const sourceMajor = sourceMinor.div(minorUnitFactor(from));
|
|
102
|
+
const baseMajor = sourceMajor.div(readRate(from));
|
|
103
|
+
const targetMajor = baseMajor.times(readRate(to));
|
|
104
|
+
return targetMajor.times(minorUnitFactor(to)).round().toNumber();
|
|
105
|
+
}
|
|
106
|
+
function formatCurrencyAmount(cents, currency, locale, options = {}) {
|
|
107
|
+
const {
|
|
108
|
+
showSymbol = true,
|
|
109
|
+
currencyDisplay = "symbol",
|
|
110
|
+
notation = "standard",
|
|
111
|
+
signDisplay = "auto",
|
|
112
|
+
decimalDigits
|
|
113
|
+
} = options;
|
|
114
|
+
const digits = decimalDigits ?? currency.decimalDigits;
|
|
115
|
+
const displayAmount = fromCents(cents, currency.decimalDigits);
|
|
116
|
+
try {
|
|
117
|
+
const formatter = new Intl.NumberFormat(locale, showSymbol ? {
|
|
118
|
+
style: "currency",
|
|
119
|
+
currency: currency.code,
|
|
120
|
+
currencyDisplay,
|
|
121
|
+
notation,
|
|
122
|
+
signDisplay,
|
|
123
|
+
minimumFractionDigits: digits,
|
|
124
|
+
maximumFractionDigits: digits
|
|
125
|
+
} : {
|
|
126
|
+
style: "decimal",
|
|
127
|
+
notation,
|
|
128
|
+
signDisplay,
|
|
129
|
+
minimumFractionDigits: digits,
|
|
130
|
+
maximumFractionDigits: digits
|
|
131
|
+
});
|
|
132
|
+
return formatter.format(displayAmount);
|
|
133
|
+
} catch {
|
|
134
|
+
const symbol = showSymbol ? currency.symbol : "";
|
|
135
|
+
return `${symbol}${displayAmount.toFixed(digits)}`;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
function formatPrice(cents, fromCurrency, state, options = {}) {
|
|
139
|
+
const currencies = normalizeCurrencies(state.currencies.length ? state.currencies : [state.baseCurrency]);
|
|
140
|
+
const baseCurrency = normalizeCurrencyInfo(state.baseCurrency, getBaseCurrency(currencies));
|
|
141
|
+
const sourceCurrency = resolveCurrencyInfo(fromCurrency, currencies, baseCurrency);
|
|
142
|
+
const targetCurrency = resolveCurrencyInfo(options.toCurrency ?? state.currency, currencies, baseCurrency);
|
|
143
|
+
const converted = convertCurrency(cents, sourceCurrency, targetCurrency, currencies);
|
|
144
|
+
return formatCurrencyAmount(
|
|
145
|
+
converted,
|
|
146
|
+
targetCurrency,
|
|
147
|
+
options.locale ?? state.locale,
|
|
148
|
+
options
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
function getI18nText(field, locale, fallbackLocale = "en-US") {
|
|
152
|
+
if (typeof field === "string") return field;
|
|
153
|
+
if (!field || typeof field !== "object") return "";
|
|
154
|
+
const normalizedLocale = normalizeLocale(locale, fallbackLocale);
|
|
155
|
+
const normalizedFallbackLocale = normalizeLocale(fallbackLocale);
|
|
156
|
+
const languageCode = normalizedLocale.split("-")[0];
|
|
157
|
+
const fallbackLanguageCode = normalizedFallbackLocale.split("-")[0];
|
|
158
|
+
const candidates = [
|
|
159
|
+
field[normalizedLocale],
|
|
160
|
+
languageCode ? field[languageCode] : void 0,
|
|
161
|
+
field[normalizedFallbackLocale],
|
|
162
|
+
fallbackLanguageCode ? field[fallbackLanguageCode] : void 0,
|
|
163
|
+
...Object.values(field)
|
|
164
|
+
];
|
|
165
|
+
return candidates.find((value) => typeof value === "string" && value.length > 0) ?? "";
|
|
166
|
+
}
|
|
167
|
+
function resolveMessageKey(messages, key, params, locale) {
|
|
168
|
+
const context = typeof params?.["context"] === "string" ? params["context"] : void 0;
|
|
169
|
+
const count = typeof params?.["count"] === "number" ? params["count"] : void 0;
|
|
170
|
+
const candidates = [];
|
|
171
|
+
if (count !== void 0) {
|
|
172
|
+
const category = new Intl.PluralRules(locale).select(count);
|
|
173
|
+
if (context) candidates.push(`${key}_${context}_${category}`, `${key}_${context}_other`);
|
|
174
|
+
candidates.push(`${key}_${category}`, `${key}_other`);
|
|
175
|
+
}
|
|
176
|
+
if (context) candidates.push(`${key}_${context}`);
|
|
177
|
+
candidates.push(key);
|
|
178
|
+
return candidates.find((candidate) => messages[candidate] !== void 0) ?? key;
|
|
179
|
+
}
|
|
180
|
+
function createTranslator(messages, locale = DEFAULT_LOCALE) {
|
|
181
|
+
const translations = messages ?? {};
|
|
182
|
+
const normalizedLocale = canonicalizeLocale(locale) ?? DEFAULT_LOCALE;
|
|
183
|
+
return (key, paramsOrFallback) => {
|
|
184
|
+
const params = paramsOrFallback && typeof paramsOrFallback === "object" ? paramsOrFallback : void 0;
|
|
185
|
+
const resolvedKey = resolveMessageKey(translations, key, params, normalizedLocale);
|
|
186
|
+
let value = translations[resolvedKey];
|
|
187
|
+
if (!value) {
|
|
188
|
+
return typeof paramsOrFallback === "string" ? paramsOrFallback : key;
|
|
189
|
+
}
|
|
190
|
+
if (params) {
|
|
191
|
+
for (const [name, replacement] of Object.entries(params)) {
|
|
192
|
+
value = value.replace(new RegExp(`\\{\\{${name}\\}\\}`, "g"), String(replacement)).replace(new RegExp(`\\{${name}\\}`, "g"), String(replacement));
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
return value;
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
function isFormatPriceOptions(value) {
|
|
199
|
+
return isRecord(value) && !("code" in value);
|
|
200
|
+
}
|
|
201
|
+
function createPriceFormatter(state, defaultFromCurrency) {
|
|
202
|
+
return (cents, fromCurrencyOrOptions, options) => {
|
|
203
|
+
const fromCurrency = isFormatPriceOptions(fromCurrencyOrOptions) ? defaultFromCurrency ?? state.baseCurrency : fromCurrencyOrOptions ?? defaultFromCurrency ?? state.baseCurrency;
|
|
204
|
+
const formatOptions = isFormatPriceOptions(fromCurrencyOrOptions) ? fromCurrencyOrOptions : options;
|
|
205
|
+
return formatPrice(cents, fromCurrency, state, formatOptions);
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
function withGlobalizationHelpers(state, handlers = {}) {
|
|
209
|
+
return {
|
|
210
|
+
...state,
|
|
211
|
+
t: state.t ?? createTranslator(state.messages, state.locale),
|
|
212
|
+
p: state.p ?? createPriceFormatter(state),
|
|
213
|
+
...handlers
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
function buildGlobalizationUrl(url, updates) {
|
|
217
|
+
const raw = String(url);
|
|
218
|
+
const isAbsolute = /^[a-z][a-z\d+\-.]*:/i.test(raw);
|
|
219
|
+
const parsed = new URL(raw, "https://wordrhyme.local");
|
|
220
|
+
if (updates.locale === null) {
|
|
221
|
+
parsed.searchParams.delete("lang");
|
|
222
|
+
} else if (updates.locale) {
|
|
223
|
+
parsed.searchParams.set("lang", updates.locale);
|
|
224
|
+
}
|
|
225
|
+
if (updates.currency === null) {
|
|
226
|
+
parsed.searchParams.delete("currency");
|
|
227
|
+
} else if (updates.currency) {
|
|
228
|
+
parsed.searchParams.set("currency", updates.currency.toUpperCase());
|
|
229
|
+
}
|
|
230
|
+
return isAbsolute ? parsed.toString() : `${parsed.pathname}${parsed.search}${parsed.hash}`;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
export {
|
|
234
|
+
Decimal,
|
|
235
|
+
DEFAULT_CURRENCY,
|
|
236
|
+
getTextDirection,
|
|
237
|
+
normalizeCurrencyInfo,
|
|
238
|
+
normalizeCurrencies,
|
|
239
|
+
findCurrency,
|
|
240
|
+
getBaseCurrency,
|
|
241
|
+
resolveCurrencyInfo,
|
|
242
|
+
bankersRound,
|
|
243
|
+
toCents,
|
|
244
|
+
fromCents,
|
|
245
|
+
convertCurrency,
|
|
246
|
+
formatCurrencyAmount,
|
|
247
|
+
formatPrice,
|
|
248
|
+
getI18nText,
|
|
249
|
+
createTranslator,
|
|
250
|
+
createPriceFormatter,
|
|
251
|
+
withGlobalizationHelpers,
|
|
252
|
+
buildGlobalizationUrl
|
|
253
|
+
};
|
|
254
|
+
//# sourceMappingURL=chunk-BVOTSKM2.js.map
|