@wordrhyme/plugin 0.1.0-alpha.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/dist/admin/index.d.ts +19 -0
  2. package/dist/admin/index.js +42 -0
  3. package/dist/admin/index.js.map +1 -0
  4. package/dist/artifact.d.ts +17 -0
  5. package/dist/artifact.js +96 -0
  6. package/dist/artifact.js.map +1 -0
  7. package/dist/chunk-46KUIGB6.js +230 -0
  8. package/dist/chunk-46KUIGB6.js.map +1 -0
  9. package/dist/chunk-6GDCFR67.js +218 -0
  10. package/dist/chunk-6GDCFR67.js.map +1 -0
  11. package/dist/chunk-DY44Q4CK.js +188 -0
  12. package/dist/chunk-DY44Q4CK.js.map +1 -0
  13. package/dist/chunk-MZOLSLJ7.js +65 -0
  14. package/dist/chunk-MZOLSLJ7.js.map +1 -0
  15. package/dist/chunk-QNCOGISF.js +694 -0
  16. package/dist/chunk-QNCOGISF.js.map +1 -0
  17. package/dist/chunk-UGMYO6AU.js +37 -0
  18. package/dist/chunk-UGMYO6AU.js.map +1 -0
  19. package/dist/chunk-YQDKOEUI.js +242 -0
  20. package/dist/chunk-YQDKOEUI.js.map +1 -0
  21. package/dist/chunk-ZUBFLOKU.js +153 -0
  22. package/dist/chunk-ZUBFLOKU.js.map +1 -0
  23. package/dist/client-WXWbuuvd.d.ts +100 -0
  24. package/dist/client.d.ts +3 -0
  25. package/dist/client.js +29 -0
  26. package/dist/client.js.map +1 -0
  27. package/dist/dev-utils.d.ts +68 -0
  28. package/dist/dev-utils.js +21 -0
  29. package/dist/dev-utils.js.map +1 -0
  30. package/dist/entity-extensions-B5BbZH-m.d.ts +56 -0
  31. package/dist/globalization.d.ts +80 -0
  32. package/dist/globalization.js +43 -0
  33. package/dist/globalization.js.map +1 -0
  34. package/dist/index.d.ts +316 -0
  35. package/dist/index.js +354 -0
  36. package/dist/index.js.map +1 -0
  37. package/dist/manifest-CPSCN_Ft.d.ts +1471 -0
  38. package/dist/react.d.ts +145 -0
  39. package/dist/react.js +94 -0
  40. package/dist/react.js.map +1 -0
  41. package/dist/release-CyapqJ3z.d.ts +230 -0
  42. package/dist/server.d.ts +114 -0
  43. package/dist/server.js +194 -0
  44. package/dist/server.js.map +1 -0
  45. package/dist/time.d.ts +34 -0
  46. package/dist/time.js +31 -0
  47. package/dist/time.js.map +1 -0
  48. package/dist/trpc.d.ts +43 -0
  49. package/dist/trpc.js +11 -0
  50. package/dist/trpc.js.map +1 -0
  51. package/dist/types-8P7XyoyQ.d.ts +1599 -0
  52. package/package.json +83 -0
@@ -0,0 +1,19 @@
1
+ import * as _trpc_client from '@trpc/client';
2
+
3
+ declare function createBrowserTrpcClient<TRouter>(url: string): _trpc_client.TRPCClient<TRouter>;
4
+ type BrowserTrpcClient<TRouter> = ReturnType<typeof createBrowserTrpcClient<TRouter>>;
5
+ /**
6
+ * Factory to create a typed tRPC client for a plugin's admin UI.
7
+ * Automatically resolves the plugin's normalized ID to the correct URL prefix.
8
+ *
9
+ * Usage: `const api = createPluginTrpcClient<ShopRouter>('shop')`
10
+ */
11
+ declare function createPluginTrpcClient<TRouter>(pluginId: string): BrowserTrpcClient<TRouter>;
12
+ /**
13
+ * Factory to create a typed tRPC client for the admin host root router.
14
+ *
15
+ * Usage: `const hostApi = createHostTrpcClient<AppRouter>()`
16
+ */
17
+ declare function createHostTrpcClient<TRouter>(): _trpc_client.TRPCClient<TRouter>;
18
+
19
+ export { createHostTrpcClient, createPluginTrpcClient };
@@ -0,0 +1,42 @@
1
+ import {
2
+ CLIENT_TIME_ZONE_HEADER,
3
+ getClientTimeZone
4
+ } from "../chunk-6GDCFR67.js";
5
+
6
+ // src/admin/trpc-client.ts
7
+ import { createTRPCClient, httpBatchLink } from "@trpc/client";
8
+ function createBrowserTrpcClient(url) {
9
+ return createTRPCClient({
10
+ links: [
11
+ httpBatchLink({
12
+ url,
13
+ headers() {
14
+ const timezone = getClientTimeZone();
15
+ return timezone ? { [CLIENT_TIME_ZONE_HEADER]: timezone } : {};
16
+ },
17
+ fetch(url2, options) {
18
+ return globalThis.fetch(url2, {
19
+ ...options,
20
+ credentials: "include"
21
+ });
22
+ }
23
+ })
24
+ ]
25
+ });
26
+ }
27
+ function normalizePluginId(pluginId) {
28
+ return pluginId.replace(/^com\.wordrhyme\./, "").replace(/\./g, "-");
29
+ }
30
+ function createPluginTrpcClient(pluginId) {
31
+ const normalizedId = normalizePluginId(pluginId);
32
+ const rootClient = createBrowserTrpcClient("/trpc");
33
+ return rootClient.pluginApis[normalizedId];
34
+ }
35
+ function createHostTrpcClient() {
36
+ return createBrowserTrpcClient("/trpc");
37
+ }
38
+ export {
39
+ createHostTrpcClient,
40
+ createPluginTrpcClient
41
+ };
42
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/admin/trpc-client.ts"],"sourcesContent":["import { createTRPCClient, httpBatchLink } from '@trpc/client';\nimport { CLIENT_TIME_ZONE_HEADER, getClientTimeZone } from '../time';\n\nfunction createBrowserTrpcClient<TRouter>(url: string) {\n // @ts-expect-error -- generic factory; Router constraint is satisfied by consumer's type argument\n return createTRPCClient<TRouter>({\n links: [\n httpBatchLink({\n url,\n headers() {\n const timezone = getClientTimeZone();\n return timezone ? { [CLIENT_TIME_ZONE_HEADER]: timezone } : {};\n },\n fetch(url, options) {\n return globalThis.fetch(url as Parameters<typeof globalThis.fetch>[0], {\n ...(options as RequestInit),\n credentials: 'include',\n });\n },\n }),\n ],\n });\n}\n\ntype BrowserTrpcClient<TRouter> = ReturnType<typeof createBrowserTrpcClient<TRouter>>;\n\nfunction normalizePluginId(pluginId: string) {\n return pluginId.replace(/^com\\.wordrhyme\\./, '').replace(/\\./g, '-');\n}\n\n/**\n * Factory to create a typed tRPC client for a plugin's admin UI.\n * Automatically resolves the plugin's normalized ID to the correct URL prefix.\n *\n * Usage: `const api = createPluginTrpcClient<ShopRouter>('shop')`\n */\nexport function createPluginTrpcClient<TRouter>(pluginId: string) {\n const normalizedId = normalizePluginId(pluginId);\n const rootClient = createBrowserTrpcClient<any>('/trpc') as any;\n\n return rootClient.pluginApis[normalizedId] as BrowserTrpcClient<TRouter>;\n}\n\n/**\n * Factory to create a typed tRPC client for the admin host root router.\n *\n * Usage: `const hostApi = createHostTrpcClient<AppRouter>()`\n */\nexport function createHostTrpcClient<TRouter>() {\n return createBrowserTrpcClient<TRouter>('/trpc');\n}\n"],"mappings":";;;;;;AAAA,SAAS,kBAAkB,qBAAqB;AAGhD,SAAS,wBAAiC,KAAa;AAEnD,SAAO,iBAA0B;AAAA,IAC7B,OAAO;AAAA,MACH,cAAc;AAAA,QACV;AAAA,QACA,UAAU;AACN,gBAAM,WAAW,kBAAkB;AACnC,iBAAO,WAAW,EAAE,CAAC,uBAAuB,GAAG,SAAS,IAAI,CAAC;AAAA,QACjE;AAAA,QACA,MAAMA,MAAK,SAAS;AAChB,iBAAO,WAAW,MAAMA,MAA+C;AAAA,YACnE,GAAI;AAAA,YACJ,aAAa;AAAA,UACjB,CAAC;AAAA,QACL;AAAA,MACJ,CAAC;AAAA,IACL;AAAA,EACJ,CAAC;AACL;AAIA,SAAS,kBAAkB,UAAkB;AACzC,SAAO,SAAS,QAAQ,qBAAqB,EAAE,EAAE,QAAQ,OAAO,GAAG;AACvE;AAQO,SAAS,uBAAgC,UAAkB;AAC9D,QAAM,eAAe,kBAAkB,QAAQ;AAC/C,QAAM,aAAa,wBAA6B,OAAO;AAEvD,SAAO,WAAW,WAAW,YAAY;AAC7C;AAOO,SAAS,uBAAgC;AAC5C,SAAO,wBAAiC,OAAO;AACnD;","names":["url"]}
@@ -0,0 +1,17 @@
1
+ import { P as PluginManifest } from './manifest-CPSCN_Ft.js';
2
+ import { P as PluginRelease } from './release-CyapqJ3z.js';
3
+ import 'zod';
4
+
5
+ declare const MAX_PLUGIN_ARCHIVE_BYTES: number;
6
+ declare const MAX_PLUGIN_EXTRACTED_BYTES: number;
7
+ declare const MAX_PLUGIN_ARCHIVE_FILES = 5000;
8
+ declare function pluginArtifactSha256(data: Uint8Array): string;
9
+ interface InspectedPluginArtifact {
10
+ manifest: PluginManifest;
11
+ release: PluginRelease;
12
+ entries: Record<string, Uint8Array>;
13
+ archiveSha256: string;
14
+ }
15
+ declare function inspectPluginArtifact(data: Uint8Array): InspectedPluginArtifact;
16
+
17
+ export { type InspectedPluginArtifact, MAX_PLUGIN_ARCHIVE_BYTES, MAX_PLUGIN_ARCHIVE_FILES, MAX_PLUGIN_EXTRACTED_BYTES, inspectPluginArtifact, pluginArtifactSha256 };
@@ -0,0 +1,96 @@
1
+ import {
2
+ pluginArchivePathSchema,
3
+ pluginReleaseSchema
4
+ } from "./chunk-DY44Q4CK.js";
5
+ import {
6
+ pluginManifestSchema
7
+ } from "./chunk-QNCOGISF.js";
8
+
9
+ // src/artifact.ts
10
+ import { createHash } from "crypto";
11
+ import { strFromU8, unzipSync } from "fflate";
12
+ var MAX_PLUGIN_ARCHIVE_BYTES = 100 * 1024 * 1024;
13
+ var MAX_PLUGIN_EXTRACTED_BYTES = 250 * 1024 * 1024;
14
+ var MAX_PLUGIN_ARCHIVE_FILES = 5e3;
15
+ var RELEASE_PATH = "release.json";
16
+ var CHECKSUM_PATH = "checksums.sha256";
17
+ function verifyChecksums(entries, release) {
18
+ const raw = entries[CHECKSUM_PATH];
19
+ if (!raw) throw new Error(`plugin archive must contain ${CHECKSUM_PATH}`);
20
+ const checksums = /* @__PURE__ */ new Map();
21
+ for (const line of strFromU8(raw).split(/\r?\n/).filter(Boolean)) {
22
+ const match = /^([a-f0-9]{64}) {2}(.+)$/.exec(line);
23
+ if (!match) throw new Error(`invalid ${CHECKSUM_PATH} entry`);
24
+ const checksum = match[1];
25
+ const filePath = match[2];
26
+ pluginArchivePathSchema.parse(filePath);
27
+ if (checksums.has(filePath)) throw new Error(`duplicate ${CHECKSUM_PATH} entry: ${filePath}`);
28
+ checksums.set(filePath, checksum);
29
+ }
30
+ const expectedPaths = [...release.files.map((file) => file.path), RELEASE_PATH].sort();
31
+ if (checksums.size !== expectedPaths.length) {
32
+ throw new Error(`${CHECKSUM_PATH} does not match release contents`);
33
+ }
34
+ for (const filePath of expectedPaths) {
35
+ const content = entries[filePath];
36
+ if (!content || checksums.get(filePath) !== pluginArtifactSha256(content)) {
37
+ throw new Error(`${CHECKSUM_PATH} mismatch: ${filePath}`);
38
+ }
39
+ }
40
+ }
41
+ function pluginArtifactSha256(data) {
42
+ return createHash("sha256").update(data).digest("hex");
43
+ }
44
+ function inspectPluginArtifact(data) {
45
+ if (data.byteLength > MAX_PLUGIN_ARCHIVE_BYTES) {
46
+ throw new Error(`plugin archive exceeds ${MAX_PLUGIN_ARCHIVE_BYTES} bytes`);
47
+ }
48
+ const entries = unzipSync(data);
49
+ const paths = Object.keys(entries);
50
+ if (paths.length > MAX_PLUGIN_ARCHIVE_FILES) {
51
+ throw new Error(`plugin archive exceeds ${MAX_PLUGIN_ARCHIVE_FILES} files`);
52
+ }
53
+ let extractedBytes = 0;
54
+ for (const [filePath, content] of Object.entries(entries)) {
55
+ pluginArchivePathSchema.parse(filePath);
56
+ extractedBytes += content.byteLength;
57
+ if (extractedBytes > MAX_PLUGIN_EXTRACTED_BYTES) {
58
+ throw new Error(`plugin archive exceeds ${MAX_PLUGIN_EXTRACTED_BYTES} extracted bytes`);
59
+ }
60
+ }
61
+ const manifestData = entries["manifest.json"];
62
+ const releaseData = entries[RELEASE_PATH];
63
+ if (!manifestData || !releaseData) {
64
+ throw new Error("plugin archive must contain manifest.json and release.json");
65
+ }
66
+ const manifest = pluginManifestSchema.parse(JSON.parse(strFromU8(manifestData)));
67
+ const release = pluginReleaseSchema.parse(JSON.parse(strFromU8(releaseData)));
68
+ if (manifest.pluginId !== release.pluginId || manifest.version !== release.version) {
69
+ throw new Error("manifest and release identities do not match");
70
+ }
71
+ for (const file of release.files) {
72
+ const content = entries[file.path];
73
+ if (!content) throw new Error(`release file is missing: ${file.path}`);
74
+ if (content.byteLength !== file.size || pluginArtifactSha256(content) !== file.sha256) {
75
+ throw new Error(`release file checksum mismatch: ${file.path}`);
76
+ }
77
+ }
78
+ verifyChecksums(entries, release);
79
+ const allowedPaths = /* @__PURE__ */ new Set([
80
+ ...release.files.map((file) => file.path),
81
+ RELEASE_PATH,
82
+ CHECKSUM_PATH,
83
+ "signature.json"
84
+ ]);
85
+ const undeclared = paths.find((filePath) => !allowedPaths.has(filePath));
86
+ if (undeclared) throw new Error(`archive contains undeclared file: ${undeclared}`);
87
+ return { manifest, release, entries, archiveSha256: pluginArtifactSha256(data) };
88
+ }
89
+ export {
90
+ MAX_PLUGIN_ARCHIVE_BYTES,
91
+ MAX_PLUGIN_ARCHIVE_FILES,
92
+ MAX_PLUGIN_EXTRACTED_BYTES,
93
+ inspectPluginArtifact,
94
+ pluginArtifactSha256
95
+ };
96
+ //# sourceMappingURL=artifact.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/artifact.ts"],"sourcesContent":["import { createHash } from \"node:crypto\";\nimport { strFromU8, unzipSync } from \"fflate\";\nimport { type PluginManifest, pluginManifestSchema } from \"./manifest\";\nimport { type PluginRelease, pluginArchivePathSchema, pluginReleaseSchema } from \"./release\";\n\nexport const MAX_PLUGIN_ARCHIVE_BYTES = 100 * 1024 * 1024;\nexport const MAX_PLUGIN_EXTRACTED_BYTES = 250 * 1024 * 1024;\nexport const MAX_PLUGIN_ARCHIVE_FILES = 5_000;\n\nconst RELEASE_PATH = \"release.json\";\nconst CHECKSUM_PATH = \"checksums.sha256\";\n\nfunction verifyChecksums(entries: Record<string, Uint8Array>, release: PluginRelease): void {\n const raw = entries[CHECKSUM_PATH];\n if (!raw) throw new Error(`plugin archive must contain ${CHECKSUM_PATH}`);\n\n const checksums = new Map<string, string>();\n for (const line of strFromU8(raw).split(/\\r?\\n/).filter(Boolean)) {\n const match = /^([a-f0-9]{64}) {2}(.+)$/.exec(line);\n if (!match) throw new Error(`invalid ${CHECKSUM_PATH} entry`);\n const checksum = match[1]!;\n const filePath = match[2]!;\n pluginArchivePathSchema.parse(filePath);\n if (checksums.has(filePath)) throw new Error(`duplicate ${CHECKSUM_PATH} entry: ${filePath}`);\n checksums.set(filePath, checksum);\n }\n\n const expectedPaths = [...release.files.map((file) => file.path), RELEASE_PATH].sort();\n if (checksums.size !== expectedPaths.length) {\n throw new Error(`${CHECKSUM_PATH} does not match release contents`);\n }\n for (const filePath of expectedPaths) {\n const content = entries[filePath];\n if (!content || checksums.get(filePath) !== pluginArtifactSha256(content)) {\n throw new Error(`${CHECKSUM_PATH} mismatch: ${filePath}`);\n }\n }\n}\n\nexport function pluginArtifactSha256(data: Uint8Array): string {\n return createHash(\"sha256\").update(data).digest(\"hex\");\n}\n\nexport interface InspectedPluginArtifact {\n manifest: PluginManifest;\n release: PluginRelease;\n entries: Record<string, Uint8Array>;\n archiveSha256: string;\n}\n\nexport function inspectPluginArtifact(data: Uint8Array): InspectedPluginArtifact {\n if (data.byteLength > MAX_PLUGIN_ARCHIVE_BYTES) {\n throw new Error(`plugin archive exceeds ${MAX_PLUGIN_ARCHIVE_BYTES} bytes`);\n }\n\n const entries = unzipSync(data);\n const paths = Object.keys(entries);\n if (paths.length > MAX_PLUGIN_ARCHIVE_FILES) {\n throw new Error(`plugin archive exceeds ${MAX_PLUGIN_ARCHIVE_FILES} files`);\n }\n\n let extractedBytes = 0;\n for (const [filePath, content] of Object.entries(entries)) {\n pluginArchivePathSchema.parse(filePath);\n extractedBytes += content.byteLength;\n if (extractedBytes > MAX_PLUGIN_EXTRACTED_BYTES) {\n throw new Error(`plugin archive exceeds ${MAX_PLUGIN_EXTRACTED_BYTES} extracted bytes`);\n }\n }\n\n const manifestData = entries[\"manifest.json\"];\n const releaseData = entries[RELEASE_PATH];\n if (!manifestData || !releaseData) {\n throw new Error(\"plugin archive must contain manifest.json and release.json\");\n }\n\n const manifest = pluginManifestSchema.parse(JSON.parse(strFromU8(manifestData)));\n const release = pluginReleaseSchema.parse(JSON.parse(strFromU8(releaseData)));\n if (manifest.pluginId !== release.pluginId || manifest.version !== release.version) {\n throw new Error(\"manifest and release identities do not match\");\n }\n\n for (const file of release.files) {\n const content = entries[file.path];\n if (!content) throw new Error(`release file is missing: ${file.path}`);\n if (content.byteLength !== file.size || pluginArtifactSha256(content) !== file.sha256) {\n throw new Error(`release file checksum mismatch: ${file.path}`);\n }\n }\n verifyChecksums(entries, release);\n\n const allowedPaths = new Set([\n ...release.files.map((file) => file.path),\n RELEASE_PATH,\n CHECKSUM_PATH,\n \"signature.json\",\n ]);\n const undeclared = paths.find((filePath) => !allowedPaths.has(filePath));\n if (undeclared) throw new Error(`archive contains undeclared file: ${undeclared}`);\n\n return { manifest, release, entries, archiveSha256: pluginArtifactSha256(data) };\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,230 @@
1
+ // src/globalization.ts
2
+ import Decimal from "decimal.js";
3
+ Decimal.set({
4
+ precision: 28,
5
+ rounding: Decimal.ROUND_HALF_EVEN
6
+ });
7
+ var DEFAULT_CURRENCY = {
8
+ code: "USD",
9
+ nameI18n: { "en-US": "US Dollar", "zh-CN": "\u7F8E\u5143" },
10
+ symbol: "$",
11
+ decimalDigits: 2,
12
+ isBase: true,
13
+ currentRate: "1"
14
+ };
15
+ var RTL_LANGUAGE_CODES = /* @__PURE__ */ new Set(["ar", "he", "fa", "ur"]);
16
+ function isRecord(value) {
17
+ return Boolean(value && typeof value === "object" && !Array.isArray(value));
18
+ }
19
+ function isCurrencyInfo(value) {
20
+ return isRecord(value) && typeof value["code"] === "string";
21
+ }
22
+ function isCurrencyCatalog(value) {
23
+ return Array.isArray(value);
24
+ }
25
+ function readRate(currency) {
26
+ if (currency.isBase) return new Decimal(1);
27
+ try {
28
+ const rate = new Decimal(currency.currentRate ?? "1");
29
+ return rate.gt(0) ? rate : new Decimal(1);
30
+ } catch {
31
+ return new Decimal(1);
32
+ }
33
+ }
34
+ function minorUnitFactor(currency) {
35
+ return new Decimal(10).pow(Math.max(0, currency.decimalDigits));
36
+ }
37
+ function getTextDirection(locale) {
38
+ const languageCode = locale.split("-")[0]?.toLowerCase();
39
+ return languageCode && RTL_LANGUAGE_CODES.has(languageCode) ? "rtl" : "ltr";
40
+ }
41
+ function normalizeCurrencyInfo(currency, fallback = DEFAULT_CURRENCY) {
42
+ const code = currency.code.toUpperCase();
43
+ return {
44
+ code,
45
+ nameI18n: currency.nameI18n ?? (code === fallback.code ? fallback.nameI18n : {}),
46
+ symbol: currency.symbol ?? (code === fallback.code ? fallback.symbol : code),
47
+ decimalDigits: typeof currency.decimalDigits === "number" ? currency.decimalDigits : fallback.decimalDigits,
48
+ isBase: currency.isBase ?? code === fallback.code,
49
+ currentRate: currency.isBase ? "1" : currency.currentRate ?? null
50
+ };
51
+ }
52
+ function normalizeCurrencies(currencies) {
53
+ if (currencies.length === 0) return [DEFAULT_CURRENCY];
54
+ const normalized = currencies.map((currency) => normalizeCurrencyInfo(currency));
55
+ const hasBase = normalized.some((currency) => currency.isBase);
56
+ if (hasBase) return normalized;
57
+ return normalized.map((currency, index) => ({
58
+ ...currency,
59
+ isBase: index === 0,
60
+ currentRate: index === 0 ? "1" : currency.currentRate
61
+ }));
62
+ }
63
+ function findCurrency(currencies, currencyCode) {
64
+ if (!currencyCode) return void 0;
65
+ const normalizedCode = currencyCode.toUpperCase();
66
+ return currencies.find((currency) => currency.code.toUpperCase() === normalizedCode);
67
+ }
68
+ function getBaseCurrency(currencies) {
69
+ return currencies.find((currency) => currency.isBase) ?? currencies[0] ?? DEFAULT_CURRENCY;
70
+ }
71
+ function resolveCurrencyInfo(currency, currencies, fallback = getBaseCurrency(currencies)) {
72
+ if (isCurrencyInfo(currency)) return normalizeCurrencyInfo(currency, fallback);
73
+ return findCurrency(currencies, currency) ?? normalizeCurrencyInfo({ code: currency ?? fallback.code }, fallback);
74
+ }
75
+ function bankersRound(value, decimalDigits) {
76
+ const decimal = new Decimal(value);
77
+ const factor = new Decimal(10).pow(decimalDigits);
78
+ return decimal.times(factor).round().div(factor).toNumber();
79
+ }
80
+ function toCents(displayAmount, decimalDigits) {
81
+ const factor = new Decimal(10).pow(decimalDigits);
82
+ return new Decimal(displayAmount).times(factor).round().toNumber();
83
+ }
84
+ function fromCents(cents, decimalDigits) {
85
+ const factor = new Decimal(10).pow(decimalDigits);
86
+ return new Decimal(cents).div(factor).toNumber();
87
+ }
88
+ function convertCurrency(cents, fromCurrency, toCurrency, currenciesOrBaseCurrency) {
89
+ if (fromCurrency.code === toCurrency.code) return cents;
90
+ const catalog = isCurrencyCatalog(currenciesOrBaseCurrency) ? currenciesOrBaseCurrency : [currenciesOrBaseCurrency, fromCurrency, toCurrency];
91
+ const baseCurrency = isCurrencyCatalog(currenciesOrBaseCurrency) ? getBaseCurrency(catalog) : currenciesOrBaseCurrency;
92
+ const from = normalizeCurrencyInfo(fromCurrency, baseCurrency);
93
+ const to = normalizeCurrencyInfo(toCurrency, baseCurrency);
94
+ const sourceMinor = new Decimal(cents);
95
+ const sourceMajor = sourceMinor.div(minorUnitFactor(from));
96
+ const baseMajor = sourceMajor.div(readRate(from));
97
+ const targetMajor = baseMajor.times(readRate(to));
98
+ return targetMajor.times(minorUnitFactor(to)).round().toNumber();
99
+ }
100
+ function formatCurrencyAmount(cents, currency, locale, options = {}) {
101
+ const {
102
+ showSymbol = true,
103
+ currencyDisplay = "symbol",
104
+ notation = "standard",
105
+ signDisplay = "auto",
106
+ decimalDigits
107
+ } = options;
108
+ const digits = decimalDigits ?? currency.decimalDigits;
109
+ const displayAmount = fromCents(cents, currency.decimalDigits);
110
+ try {
111
+ const formatter = new Intl.NumberFormat(locale, showSymbol ? {
112
+ style: "currency",
113
+ currency: currency.code,
114
+ currencyDisplay,
115
+ notation,
116
+ signDisplay,
117
+ minimumFractionDigits: digits,
118
+ maximumFractionDigits: digits
119
+ } : {
120
+ style: "decimal",
121
+ notation,
122
+ signDisplay,
123
+ minimumFractionDigits: digits,
124
+ maximumFractionDigits: digits
125
+ });
126
+ return formatter.format(displayAmount);
127
+ } catch {
128
+ const symbol = showSymbol ? currency.symbol : "";
129
+ return `${symbol}${displayAmount.toFixed(digits)}`;
130
+ }
131
+ }
132
+ function formatPrice(cents, fromCurrency, state, options = {}) {
133
+ const currencies = normalizeCurrencies(state.currencies.length ? state.currencies : [state.baseCurrency]);
134
+ const baseCurrency = normalizeCurrencyInfo(state.baseCurrency, getBaseCurrency(currencies));
135
+ const sourceCurrency = resolveCurrencyInfo(fromCurrency, currencies, baseCurrency);
136
+ const targetCurrency = resolveCurrencyInfo(options.toCurrency ?? state.currency, currencies, baseCurrency);
137
+ const converted = convertCurrency(cents, sourceCurrency, targetCurrency, currencies);
138
+ return formatCurrencyAmount(
139
+ converted,
140
+ targetCurrency,
141
+ options.locale ?? state.locale,
142
+ options
143
+ );
144
+ }
145
+ function getI18nText(field, locale, fallbackLocale = "en-US") {
146
+ if (typeof field === "string") return field;
147
+ if (!field || typeof field !== "object") return "";
148
+ const languageCode = locale.split("-")[0];
149
+ const fallbackLanguageCode = fallbackLocale.split("-")[0];
150
+ const candidates = [
151
+ field[locale],
152
+ languageCode ? field[languageCode] : void 0,
153
+ field[fallbackLocale],
154
+ fallbackLanguageCode ? field[fallbackLanguageCode] : void 0,
155
+ ...Object.values(field)
156
+ ];
157
+ return candidates.find((value) => typeof value === "string" && value.length > 0) ?? "";
158
+ }
159
+ function createTranslator(messages) {
160
+ const translations = messages ?? {};
161
+ return (key, paramsOrFallback) => {
162
+ let value = translations[key];
163
+ if (!value) {
164
+ return typeof paramsOrFallback === "string" ? paramsOrFallback : key;
165
+ }
166
+ if (paramsOrFallback && typeof paramsOrFallback === "object") {
167
+ for (const [name, replacement] of Object.entries(paramsOrFallback)) {
168
+ value = value.replace(new RegExp(`\\{${name}\\}`, "g"), String(replacement));
169
+ }
170
+ }
171
+ return value;
172
+ };
173
+ }
174
+ function isFormatPriceOptions(value) {
175
+ return isRecord(value) && !("code" in value);
176
+ }
177
+ function createPriceFormatter(state, defaultFromCurrency) {
178
+ return (cents, fromCurrencyOrOptions, options) => {
179
+ const fromCurrency = isFormatPriceOptions(fromCurrencyOrOptions) ? defaultFromCurrency ?? state.baseCurrency : fromCurrencyOrOptions ?? defaultFromCurrency ?? state.baseCurrency;
180
+ const formatOptions = isFormatPriceOptions(fromCurrencyOrOptions) ? fromCurrencyOrOptions : options;
181
+ return formatPrice(cents, fromCurrency, state, formatOptions);
182
+ };
183
+ }
184
+ function withGlobalizationHelpers(state, handlers = {}) {
185
+ return {
186
+ ...state,
187
+ t: state.t ?? createTranslator(state.messages),
188
+ p: state.p ?? createPriceFormatter(state),
189
+ ...handlers
190
+ };
191
+ }
192
+ function buildGlobalizationUrl(url, updates) {
193
+ const raw = String(url);
194
+ const isAbsolute = /^[a-z][a-z\d+\-.]*:/i.test(raw);
195
+ const parsed = new URL(raw, "https://wordrhyme.local");
196
+ if (updates.locale === null) {
197
+ parsed.searchParams.delete("lang");
198
+ } else if (updates.locale) {
199
+ parsed.searchParams.set("lang", updates.locale);
200
+ }
201
+ if (updates.currency === null) {
202
+ parsed.searchParams.delete("currency");
203
+ } else if (updates.currency) {
204
+ parsed.searchParams.set("currency", updates.currency.toUpperCase());
205
+ }
206
+ return isAbsolute ? parsed.toString() : `${parsed.pathname}${parsed.search}${parsed.hash}`;
207
+ }
208
+
209
+ export {
210
+ Decimal,
211
+ DEFAULT_CURRENCY,
212
+ getTextDirection,
213
+ normalizeCurrencyInfo,
214
+ normalizeCurrencies,
215
+ findCurrency,
216
+ getBaseCurrency,
217
+ resolveCurrencyInfo,
218
+ bankersRound,
219
+ toCents,
220
+ fromCents,
221
+ convertCurrency,
222
+ formatCurrencyAmount,
223
+ formatPrice,
224
+ getI18nText,
225
+ createTranslator,
226
+ createPriceFormatter,
227
+ withGlobalizationHelpers,
228
+ buildGlobalizationUrl
229
+ };
230
+ //# sourceMappingURL=chunk-46KUIGB6.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/globalization.ts"],"sourcesContent":["import Decimal from 'decimal.js';\n\nDecimal.set({\n precision: 28,\n rounding: Decimal.ROUND_HALF_EVEN,\n});\n\nexport type TextDirection = 'ltr' | 'rtl';\n\nexport type TranslationMessages = Record<string, string>;\n\nexport type I18nTextField = string | Record<string, string | null | undefined> | null | undefined;\n\nexport interface LanguageInfo {\n locale: string;\n name: string;\n nativeName?: string | null | undefined;\n isDefault?: boolean | undefined;\n isEnabled?: boolean | undefined;\n sortOrder?: number | null | undefined;\n direction?: TextDirection | undefined;\n}\n\nexport interface CurrencyInfo {\n code: string;\n nameI18n: Record<string, string>;\n symbol: string;\n decimalDigits: number;\n isBase: boolean;\n currentRate: string | null;\n}\n\nexport type CurrencyInfoInput = {\n code: string;\n nameI18n?: Record<string, string> | null | undefined;\n symbol?: string | null | undefined;\n decimalDigits?: number | null | undefined;\n isBase?: boolean | null | undefined;\n currentRate?: string | null | undefined;\n};\n\nexport interface GlobalizationState {\n locale: string;\n direction: TextDirection;\n availableLocales: string[];\n languages?: LanguageInfo[] | undefined;\n currency: string;\n currencies: CurrencyInfo[];\n baseCurrency: CurrencyInfo;\n timezone?: string | undefined;\n fallbackLocale?: string | undefined;\n messages?: TranslationMessages | undefined;\n t?: Translator | undefined;\n p?: PriceFormatter | undefined;\n changeLocale?: ((locale: string) => void | Promise<void>) | undefined;\n changeCurrency?: ((currencyCode: string) => void | Promise<void>) | undefined;\n}\n\nexport interface FormatPriceOptions {\n showSymbol?: boolean | undefined;\n currencyDisplay?: 'symbol' | 'code' | 'name' | 'narrowSymbol' | undefined;\n notation?: 'standard' | 'compact' | undefined;\n signDisplay?: 'auto' | 'always' | 'exceptZero' | 'never' | undefined;\n decimalDigits?: number | undefined;\n locale?: string | undefined;\n toCurrency?: string | CurrencyInfo | undefined;\n}\n\nexport type Translator = (\n key: string,\n paramsOrFallback?: Record<string, string | number> | string,\n) => string;\n\nexport type PriceFormatter = (\n cents: number,\n fromCurrencyOrOptions?: string | CurrencyInfo | FormatPriceOptions,\n options?: FormatPriceOptions,\n) => string;\n\nexport const DEFAULT_CURRENCY: CurrencyInfo = {\n code: 'USD',\n nameI18n: { 'en-US': 'US Dollar', 'zh-CN': '美元' },\n symbol: '$',\n decimalDigits: 2,\n isBase: true,\n currentRate: '1',\n};\n\nconst RTL_LANGUAGE_CODES = new Set(['ar', 'he', 'fa', 'ur']);\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return Boolean(value && typeof value === 'object' && !Array.isArray(value));\n}\n\nfunction isCurrencyInfo(value: unknown): value is CurrencyInfo {\n return isRecord(value) && typeof value['code'] === 'string';\n}\n\nfunction isCurrencyCatalog(value: readonly CurrencyInfo[] | CurrencyInfo): value is readonly CurrencyInfo[] {\n return Array.isArray(value);\n}\n\nfunction readRate(currency: CurrencyInfo): Decimal {\n if (currency.isBase) return new Decimal(1);\n\n try {\n const rate = new Decimal(currency.currentRate ?? '1');\n return rate.gt(0) ? rate : new Decimal(1);\n } catch {\n return new Decimal(1);\n }\n}\n\nfunction minorUnitFactor(currency: CurrencyInfo): Decimal {\n return new Decimal(10).pow(Math.max(0, currency.decimalDigits));\n}\n\nexport function getTextDirection(locale: string): TextDirection {\n const languageCode = locale.split('-')[0]?.toLowerCase();\n return languageCode && RTL_LANGUAGE_CODES.has(languageCode) ? 'rtl' : 'ltr';\n}\n\nexport function normalizeCurrencyInfo(\n currency: CurrencyInfoInput,\n fallback: CurrencyInfo = DEFAULT_CURRENCY,\n): CurrencyInfo {\n const code = currency.code.toUpperCase();\n return {\n code,\n nameI18n: currency.nameI18n ?? (code === fallback.code ? fallback.nameI18n : {}),\n symbol: currency.symbol ?? (code === fallback.code ? fallback.symbol : code),\n decimalDigits: typeof currency.decimalDigits === 'number' ? currency.decimalDigits : fallback.decimalDigits,\n isBase: currency.isBase ?? code === fallback.code,\n currentRate: currency.isBase ? '1' : (currency.currentRate ?? null),\n };\n}\n\nexport function normalizeCurrencies(currencies: readonly CurrencyInfoInput[]): CurrencyInfo[] {\n if (currencies.length === 0) return [DEFAULT_CURRENCY];\n const normalized = currencies.map((currency) => normalizeCurrencyInfo(currency));\n const hasBase = normalized.some((currency) => currency.isBase);\n if (hasBase) return normalized;\n\n return normalized.map((currency, index) => ({\n ...currency,\n isBase: index === 0,\n currentRate: index === 0 ? '1' : currency.currentRate,\n }));\n}\n\nexport function findCurrency(\n currencies: readonly CurrencyInfo[],\n currencyCode: string | undefined,\n): CurrencyInfo | undefined {\n if (!currencyCode) return undefined;\n const normalizedCode = currencyCode.toUpperCase();\n return currencies.find((currency) => currency.code.toUpperCase() === normalizedCode);\n}\n\nexport function getBaseCurrency(currencies: readonly CurrencyInfo[]): CurrencyInfo {\n return currencies.find((currency) => currency.isBase) ?? currencies[0] ?? DEFAULT_CURRENCY;\n}\n\nexport function resolveCurrencyInfo(\n currency: string | CurrencyInfo | undefined,\n currencies: readonly CurrencyInfo[],\n fallback: CurrencyInfo = getBaseCurrency(currencies),\n): CurrencyInfo {\n if (isCurrencyInfo(currency)) return normalizeCurrencyInfo(currency, fallback);\n return findCurrency(currencies, currency) ?? normalizeCurrencyInfo({ code: currency ?? fallback.code }, fallback);\n}\n\nexport function bankersRound(value: number, decimalDigits: number): number {\n const decimal = new Decimal(value);\n const factor = new Decimal(10).pow(decimalDigits);\n return decimal.times(factor).round().div(factor).toNumber();\n}\n\nexport function toCents(displayAmount: number, decimalDigits: number): number {\n const factor = new Decimal(10).pow(decimalDigits);\n return new Decimal(displayAmount).times(factor).round().toNumber();\n}\n\nexport function fromCents(cents: number, decimalDigits: number): number {\n const factor = new Decimal(10).pow(decimalDigits);\n return new Decimal(cents).div(factor).toNumber();\n}\n\nexport function convertCurrency(\n cents: number,\n fromCurrency: CurrencyInfo,\n toCurrency: CurrencyInfo,\n currenciesOrBaseCurrency: readonly CurrencyInfo[] | CurrencyInfo,\n): number {\n if (fromCurrency.code === toCurrency.code) return cents;\n\n const catalog = isCurrencyCatalog(currenciesOrBaseCurrency)\n ? currenciesOrBaseCurrency\n : [currenciesOrBaseCurrency, fromCurrency, toCurrency];\n const baseCurrency = isCurrencyCatalog(currenciesOrBaseCurrency)\n ? getBaseCurrency(catalog)\n : currenciesOrBaseCurrency;\n\n const from = normalizeCurrencyInfo(fromCurrency, baseCurrency);\n const to = normalizeCurrencyInfo(toCurrency, baseCurrency);\n const sourceMinor = new Decimal(cents);\n const sourceMajor = sourceMinor.div(minorUnitFactor(from));\n const baseMajor = sourceMajor.div(readRate(from));\n const targetMajor = baseMajor.times(readRate(to));\n return targetMajor.times(minorUnitFactor(to)).round().toNumber();\n}\n\nexport function formatCurrencyAmount(\n cents: number,\n currency: CurrencyInfo,\n locale: string,\n options: FormatPriceOptions = {},\n): string {\n const {\n showSymbol = true,\n currencyDisplay = 'symbol',\n notation = 'standard',\n signDisplay = 'auto',\n decimalDigits,\n } = options;\n const digits = decimalDigits ?? currency.decimalDigits;\n const displayAmount = fromCents(cents, currency.decimalDigits);\n\n try {\n const formatter = new Intl.NumberFormat(locale, showSymbol ? {\n style: 'currency',\n currency: currency.code,\n currencyDisplay,\n notation,\n signDisplay,\n minimumFractionDigits: digits,\n maximumFractionDigits: digits,\n } : {\n style: 'decimal',\n notation,\n signDisplay,\n minimumFractionDigits: digits,\n maximumFractionDigits: digits,\n });\n return formatter.format(displayAmount);\n } catch {\n const symbol = showSymbol ? currency.symbol : '';\n return `${symbol}${displayAmount.toFixed(digits)}`;\n }\n}\n\nexport function formatPrice(\n cents: number,\n fromCurrency: string | CurrencyInfo,\n state: GlobalizationState,\n options: FormatPriceOptions = {},\n): string {\n const currencies = normalizeCurrencies(state.currencies.length ? state.currencies : [state.baseCurrency]);\n const baseCurrency = normalizeCurrencyInfo(state.baseCurrency, getBaseCurrency(currencies));\n const sourceCurrency = resolveCurrencyInfo(fromCurrency, currencies, baseCurrency);\n const targetCurrency = resolveCurrencyInfo(options.toCurrency ?? state.currency, currencies, baseCurrency);\n const converted = convertCurrency(cents, sourceCurrency, targetCurrency, currencies);\n\n return formatCurrencyAmount(\n converted,\n targetCurrency,\n options.locale ?? state.locale,\n options,\n );\n}\n\nexport function getI18nText(\n field: I18nTextField,\n locale: string,\n fallbackLocale = 'en-US',\n): string {\n if (typeof field === 'string') return field;\n if (!field || typeof field !== 'object') return '';\n\n const languageCode = locale.split('-')[0];\n const fallbackLanguageCode = fallbackLocale.split('-')[0];\n const candidates = [\n field[locale],\n languageCode ? field[languageCode] : undefined,\n field[fallbackLocale],\n fallbackLanguageCode ? field[fallbackLanguageCode] : undefined,\n ...Object.values(field),\n ];\n\n return candidates.find((value): value is string => typeof value === 'string' && value.length > 0) ?? '';\n}\n\nexport function createTranslator(messages: TranslationMessages | undefined): Translator {\n const translations = messages ?? {};\n return (key, paramsOrFallback) => {\n let value = translations[key];\n if (!value) {\n return typeof paramsOrFallback === 'string' ? paramsOrFallback : key;\n }\n if (paramsOrFallback && typeof paramsOrFallback === 'object') {\n for (const [name, replacement] of Object.entries(paramsOrFallback)) {\n value = value.replace(new RegExp(`\\\\{${name}\\\\}`, 'g'), String(replacement));\n }\n }\n return value;\n };\n}\n\nfunction isFormatPriceOptions(value: unknown): value is FormatPriceOptions {\n return isRecord(value) && !('code' in value);\n}\n\nexport function createPriceFormatter(\n state: GlobalizationState,\n defaultFromCurrency?: string | CurrencyInfo,\n): PriceFormatter {\n return (cents, fromCurrencyOrOptions, options) => {\n const fromCurrency = isFormatPriceOptions(fromCurrencyOrOptions)\n ? defaultFromCurrency ?? state.baseCurrency\n : fromCurrencyOrOptions ?? defaultFromCurrency ?? state.baseCurrency;\n const formatOptions = isFormatPriceOptions(fromCurrencyOrOptions)\n ? fromCurrencyOrOptions\n : options;\n\n return formatPrice(cents, fromCurrency, state, formatOptions);\n };\n}\n\nexport function withGlobalizationHelpers(\n state: GlobalizationState,\n handlers: Pick<GlobalizationState, 'changeLocale' | 'changeCurrency'> = {},\n): GlobalizationState {\n return {\n ...state,\n t: state.t ?? createTranslator(state.messages),\n p: state.p ?? createPriceFormatter(state),\n ...handlers,\n };\n}\n\nexport function buildGlobalizationUrl(\n url: string | URL,\n updates: { locale?: string | null | undefined; currency?: string | null | undefined },\n): string {\n const raw = String(url);\n const isAbsolute = /^[a-z][a-z\\d+\\-.]*:/i.test(raw);\n const parsed = new URL(raw, 'https://wordrhyme.local');\n\n if (updates.locale === null) {\n parsed.searchParams.delete('lang');\n } else if (updates.locale) {\n parsed.searchParams.set('lang', updates.locale);\n }\n\n if (updates.currency === null) {\n parsed.searchParams.delete('currency');\n } else if (updates.currency) {\n parsed.searchParams.set('currency', updates.currency.toUpperCase());\n }\n\n return isAbsolute ? parsed.toString() : `${parsed.pathname}${parsed.search}${parsed.hash}`;\n}\n\nexport { Decimal };\n"],"mappings":"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","names":[]}