tempest-react-sdk 0.43.0 → 0.45.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/README.md +12 -5
- package/dist/app/AppProviders.cjs +1 -1
- package/dist/app/AppProviders.js +6 -6
- package/dist/audio/sfx-pool.cjs +2 -0
- package/dist/audio/sfx-pool.cjs.map +1 -0
- package/dist/audio/sfx-pool.js +62 -0
- package/dist/audio/sfx-pool.js.map +1 -0
- package/dist/audio/use-sfx-pool.cjs +2 -0
- package/dist/audio/use-sfx-pool.cjs.map +1 -0
- package/dist/audio/use-sfx-pool.js +18 -0
- package/dist/audio/use-sfx-pool.js.map +1 -0
- package/dist/auth/create-tempest-auth.cjs +1 -1
- package/dist/auth/create-tempest-auth.cjs.map +1 -1
- package/dist/auth/create-tempest-auth.js +47 -44
- package/dist/auth/create-tempest-auth.js.map +1 -1
- package/dist/auth/lazy-with-retry.cjs +1 -1
- package/dist/auth/lazy-with-retry.cjs.map +1 -1
- package/dist/auth/lazy-with-retry.js +8 -1
- package/dist/auth/lazy-with-retry.js.map +1 -1
- package/dist/components/AppBar/AppBar.cjs +1 -1
- package/dist/components/AppBar/AppBar.cjs.map +1 -1
- package/dist/components/AppBar/AppBar.js +27 -25
- package/dist/components/AppBar/AppBar.js.map +1 -1
- package/dist/components/AppBar/use-sticky-body-warning.cjs +2 -0
- package/dist/components/AppBar/use-sticky-body-warning.cjs.map +1 -0
- package/dist/components/AppBar/use-sticky-body-warning.js +16 -0
- package/dist/components/AppBar/use-sticky-body-warning.js.map +1 -0
- package/dist/components/BarList/BarList.cjs +2 -0
- package/dist/components/BarList/BarList.cjs.map +1 -0
- package/dist/components/BarList/BarList.js +42 -0
- package/dist/components/BarList/BarList.js.map +1 -0
- package/dist/components/BarList/BarList.module.cjs +2 -0
- package/dist/components/BarList/BarList.module.cjs.map +1 -0
- package/dist/components/BarList/BarList.module.js +15 -0
- package/dist/components/BarList/BarList.module.js.map +1 -0
- package/dist/components/BarList/bar-list-model.cjs +2 -0
- package/dist/components/BarList/bar-list-model.cjs.map +1 -0
- package/dist/components/BarList/bar-list-model.js +18 -0
- package/dist/components/BarList/bar-list-model.js.map +1 -0
- package/dist/components/DataTable/DataTable.cjs +1 -1
- package/dist/components/DataTable/DataTable.cjs.map +1 -1
- package/dist/components/DataTable/DataTable.js +141 -108
- package/dist/components/DataTable/DataTable.js.map +1 -1
- package/dist/components/DataTable/DataTable.module.cjs +1 -1
- package/dist/components/DataTable/DataTable.module.cjs.map +1 -1
- package/dist/components/DataTable/DataTable.module.js +13 -10
- package/dist/components/DataTable/DataTable.module.js.map +1 -1
- package/dist/components/DataTable/LoadingRows.cjs +2 -0
- package/dist/components/DataTable/LoadingRows.cjs.map +1 -0
- package/dist/components/DataTable/LoadingRows.js +18 -0
- package/dist/components/DataTable/LoadingRows.js.map +1 -0
- package/dist/components/DataTable/use-dev-warnings.cjs +2 -0
- package/dist/components/DataTable/use-dev-warnings.cjs.map +1 -0
- package/dist/components/DataTable/use-dev-warnings.js +17 -0
- package/dist/components/DataTable/use-dev-warnings.js.map +1 -0
- package/dist/components/FilterBar/filter-apply.cjs +2 -0
- package/dist/components/FilterBar/filter-apply.cjs.map +1 -0
- package/dist/components/FilterBar/filter-apply.js +76 -0
- package/dist/components/FilterBar/filter-apply.js.map +1 -0
- package/dist/components/FilterBar/filter-query.cjs +2 -0
- package/dist/components/FilterBar/filter-query.cjs.map +1 -0
- package/dist/components/FilterBar/filter-query.js +38 -0
- package/dist/components/FilterBar/filter-query.js.map +1 -0
- package/dist/hooks/use-countdown.cjs +2 -0
- package/dist/hooks/use-countdown.cjs.map +1 -0
- package/dist/hooks/use-countdown.js +18 -0
- package/dist/hooks/use-countdown.js.map +1 -0
- package/dist/hooks/use-latest-ref.cjs +2 -0
- package/dist/hooks/use-latest-ref.cjs.map +1 -0
- package/dist/hooks/use-latest-ref.js +10 -0
- package/dist/hooks/use-latest-ref.js.map +1 -0
- package/dist/hooks/use-typewriter.cjs +2 -0
- package/dist/hooks/use-typewriter.cjs.map +1 -0
- package/dist/hooks/use-typewriter.js +28 -0
- package/dist/hooks/use-typewriter.js.map +1 -0
- package/dist/http/api-client.cjs +1 -1
- package/dist/http/api-client.cjs.map +1 -1
- package/dist/http/api-client.js +42 -43
- package/dist/http/api-client.js.map +1 -1
- package/dist/http/build-url.cjs +2 -0
- package/dist/http/build-url.cjs.map +1 -0
- package/dist/http/build-url.js +26 -0
- package/dist/http/build-url.js.map +1 -0
- package/dist/http/describe-api-error.cjs +2 -0
- package/dist/http/describe-api-error.cjs.map +1 -0
- package/dist/http/describe-api-error.js +22 -0
- package/dist/http/describe-api-error.js.map +1 -0
- package/dist/http/use-describe-api-error.cjs +2 -0
- package/dist/http/use-describe-api-error.cjs.map +1 -0
- package/dist/http/use-describe-api-error.js +15 -0
- package/dist/http/use-describe-api-error.js.map +1 -0
- package/dist/i18n/I18nProvider.cjs +1 -1
- package/dist/i18n/I18nProvider.cjs.map +1 -1
- package/dist/i18n/I18nProvider.js +4 -1
- package/dist/i18n/I18nProvider.js.map +1 -1
- package/dist/query/QueryProvider.cjs +1 -1
- package/dist/query/QueryProvider.cjs.map +1 -1
- package/dist/query/QueryProvider.js +13 -12
- package/dist/query/QueryProvider.js.map +1 -1
- package/dist/query/retry-policy.cjs +2 -0
- package/dist/query/retry-policy.cjs.map +1 -0
- package/dist/query/retry-policy.js +10 -0
- package/dist/query/retry-policy.js.map +1 -0
- package/dist/styles.css +1 -1
- package/dist/tempest-react-sdk.cjs +1 -1
- package/dist/tempest-react-sdk.d.ts +867 -10
- package/dist/tempest-react-sdk.js +276 -261
- package/dist/utils/compressed-storage.cjs +2 -0
- package/dist/utils/compressed-storage.cjs.map +1 -0
- package/dist/utils/compressed-storage.js +52 -0
- package/dist/utils/compressed-storage.js.map +1 -0
- package/dist/utils/csv.cjs +4 -0
- package/dist/utils/csv.cjs.map +1 -0
- package/dist/utils/csv.js +25 -0
- package/dist/utils/csv.js.map +1 -0
- package/dist/utils/format.cjs +1 -1
- package/dist/utils/format.cjs.map +1 -1
- package/dist/utils/format.js +11 -4
- package/dist/utils/format.js.map +1 -1
- package/dist/utils/numbers.cjs +1 -1
- package/dist/utils/numbers.cjs.map +1 -1
- package/dist/utils/numbers.js +5 -2
- package/dist/utils/numbers.js.map +1 -1
- package/dist/vite/tempest-pwa-dev-sw.cjs +1 -1
- package/dist/vite/tempest-pwa-dev-sw.cjs.map +1 -1
- package/dist/vite/tempest-pwa-dev-sw.js +10 -5
- package/dist/vite/tempest-pwa-dev-sw.js.map +1 -1
- package/dist/vite/tempest-pwa-manifest.cjs +2 -2
- package/dist/vite/tempest-pwa-manifest.cjs.map +1 -1
- package/dist/vite/tempest-pwa-manifest.js +17 -13
- package/dist/vite/tempest-pwa-manifest.js.map +1 -1
- package/dist/vite.d.ts +19 -2
- package/package.json +1 -1
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
let e=require("fflate");var t=`~tgz1:`;function n(e){let t=``,n=32768;for(let r=0;r<e.length;r+=n)t+=String.fromCharCode(...e.subarray(r,r+n));return btoa(t)}function r(e){let t=atob(e),n=new Uint8Array(t.length);for(let e=0;e<t.length;e+=1)n[e]=t.charCodeAt(e);return n}function i(r){let i=JSON.stringify(r);return`${t}${n((0,e.gzipSync)(new TextEncoder().encode(i)))}`}function a(n){if(!n.startsWith(t))return JSON.parse(n);let i=(0,e.gunzipSync)(r(n.slice(6)));return JSON.parse(new TextDecoder().decode(i))}var o={serialize:i,deserialize:a},s={get(e,t){if(typeof window>`u`)return t;try{let n=window.localStorage.getItem(e);return n===null?t:a(n)}catch{return t}},set(e,t){if(typeof window>`u`)return;let n;try{n=i(t)}catch{n=JSON.stringify(t)}try{window.localStorage.setItem(e,n)}catch{}}};exports.compressToString=i,exports.compressedStorage=s,exports.compressedStorageCodec=o,exports.decompressFromString=a;
|
|
2
|
+
//# sourceMappingURL=compressed-storage.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compressed-storage.cjs","names":[],"sources":["../../src/utils/compressed-storage.ts"],"sourcesContent":["import { gunzipSync, gzipSync } from \"fflate\";\n\n/**\n * Gzip-backed `localStorage`, for payloads that would otherwise eat the quota.\n *\n * Kept in its own module rather than folded into {@link storage} so that\n * importing the plain typed wrapper never drags `fflate` into the bundle.\n *\n * @tempest-limits empty-catch — every operation here is best-effort by\n * contract, exactly like {@link storage}. `localStorage` throws on quota\n * exhaustion, in Safari private mode, and when a cross-origin frame has storage\n * blocked; a caller persisting state has no recovery to run and no message to\n * show, so the write is dropped and the value stays in memory for the session.\n */\n\n/**\n * Prefix stamped on every value this module writes.\n *\n * It makes the format self-describing, which is what lets a read fall back to\n * plain JSON: a key that predates compression — or that a degraded write stored\n * uncompressed — is still readable, so turning compression on for an existing\n * key does not orphan the data already there. The character also cannot open a\n * JSON document, so the two cases can never be confused.\n */\nconst MARKER = \"~tgz1:\";\n\n/**\n * Base64-encode bytes without splatting the whole array into `fromCharCode`.\n *\n * `String.fromCharCode(...bytes)` overflows the argument limit somewhere around\n * a hundred thousand entries, which a compressed save reaches easily, so the\n * conversion walks the buffer in 32 KiB windows instead.\n */\nfunction bytesToBase64(bytes: Uint8Array): string {\n let binary = \"\";\n const chunkSize = 0x8000;\n for (let i = 0; i < bytes.length; i += chunkSize) {\n binary += String.fromCharCode(...bytes.subarray(i, i + chunkSize));\n }\n return btoa(binary);\n}\n\n/** Inverse of {@link bytesToBase64}. */\nfunction base64ToBytes(base64: string): Uint8Array {\n const binary = atob(base64);\n const bytes = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i += 1) {\n bytes[i] = binary.charCodeAt(i);\n }\n return bytes;\n}\n\n/**\n * Serialize a value to a gzipped, base64 string carrying the format marker.\n *\n * Base64 costs a third more characters than the raw compressed bytes, and\n * `localStorage` bills two bytes per character on top of that. Packing the\n * bytes into UTF-16 code units directly would be denser, but lone surrogates\n * survive neither every storage implementation nor a JSON round-trip, and a\n * save that decodes to garbage is far worse than one that is bigger. Even with\n * that overhead a typical JSON document lands well under a third of its\n * uncompressed size.\n *\n * @typeParam T - The value being stored.\n * @param value - Any JSON-serializable value.\n * @returns The encoded string, ready for `localStorage`.\n */\nexport function compressToString<T>(value: T): string {\n const json = JSON.stringify(value);\n const bytes = gzipSync(new TextEncoder().encode(json));\n return `${MARKER}${bytesToBase64(bytes)}`;\n}\n\n/**\n * Decode a string produced by {@link compressToString}.\n *\n * A string without the marker is parsed as plain JSON, so values written before\n * compression was enabled — or by a write that fell back after `gzipSync`\n * failed — still read back.\n *\n * @typeParam T - The expected value shape.\n * @param raw - The stored string.\n * @returns The decoded value.\n * @throws If the payload is neither valid compressed data nor valid JSON.\n */\nexport function decompressFromString<T>(raw: string): T {\n if (!raw.startsWith(MARKER)) return JSON.parse(raw) as T;\n const bytes = gunzipSync(base64ToBytes(raw.slice(MARKER.length)));\n return JSON.parse(new TextDecoder().decode(bytes)) as T;\n}\n\n/**\n * Codec pair for {@link useLocalStorage}, so a compressed key gets the hook's\n * cross-tab sync and SSR guard for free.\n *\n * @example\n * const [save, setSave] = useLocalStorage(\"save\", EMPTY_SAVE, compressedStorageCodec);\n */\nexport const compressedStorageCodec = {\n serialize: compressToString,\n deserialize: decompressFromString,\n};\n\n/**\n * Typed `localStorage` wrapper that gzips what it writes.\n *\n * Mirrors {@link storage} so the two are interchangeable at the call site; the\n * difference is only in how the value is encoded.\n */\nexport const compressedStorage = {\n /**\n * Read and decompress a key.\n *\n * @typeParam T - The expected value shape.\n * @param key - Storage key.\n * @param fallback - Returned when the key is absent, unreadable, or corrupt.\n * @returns The stored value, or `fallback`.\n */\n get<T>(key: string, fallback: T): T {\n if (typeof window === \"undefined\") return fallback;\n try {\n const raw = window.localStorage.getItem(key);\n return raw === null ? fallback : decompressFromString<T>(raw);\n } catch {\n return fallback;\n }\n },\n\n /**\n * Compress and write a key.\n *\n * When compression itself fails the value is written as plain JSON rather\n * than dropped: a slightly larger record still loads, an absent one does\n * not. Only a storage-level failure — quota, blocked storage — loses the\n * write.\n *\n * @typeParam T - The value being stored.\n * @param key - Storage key.\n * @param value - Any JSON-serializable value.\n */\n set<T>(key: string, value: T): void {\n if (typeof window === \"undefined\") return;\n let encoded: string;\n try {\n encoded = compressToString(value);\n } catch {\n encoded = JSON.stringify(value);\n }\n try {\n window.localStorage.setItem(key, encoded);\n } catch {\n /* empty */\n }\n },\n};\n"],"mappings":"wBAwBA,IAAM,EAAS,SASf,SAAS,EAAc,EAA2B,CAC9C,IAAI,EAAS,GACP,EAAY,MAClB,IAAK,IAAI,EAAI,EAAG,EAAI,EAAM,OAAQ,GAAK,EACnC,GAAU,OAAO,aAAa,GAAG,EAAM,SAAS,EAAG,EAAI,CAAS,CAAC,EAErE,OAAO,KAAK,CAAM,CACtB,CAGA,SAAS,EAAc,EAA4B,CAC/C,IAAM,EAAS,KAAK,CAAM,EACpB,EAAQ,IAAI,WAAW,EAAO,MAAM,EAC1C,IAAK,IAAI,EAAI,EAAG,EAAI,EAAO,OAAQ,GAAK,EACpC,EAAM,GAAK,EAAO,WAAW,CAAC,EAElC,OAAO,CACX,CAiBA,SAAgB,EAAoB,EAAkB,CAClD,IAAM,EAAO,KAAK,UAAU,CAAK,EAEjC,MAAO,GAAG,IAAS,GADb,EAAQ,EAAA,SAAA,CAAS,IAAI,YAAY,CAAC,CAAC,OAAO,CAAI,CACnB,CAAK,GAC1C,CAcA,SAAgB,EAAwB,EAAgB,CACpD,GAAI,CAAC,EAAI,WAAW,CAAM,EAAG,OAAO,KAAK,MAAM,CAAG,EAClD,IAAM,GAAA,EAAQ,EAAA,WAAA,CAAW,EAAc,EAAI,MAAM,CAAa,CAAC,CAAC,EAChE,OAAO,KAAK,MAAM,IAAI,YAAY,CAAC,CAAC,OAAO,CAAK,CAAC,CACrD,CASA,IAAa,EAAyB,CAClC,UAAW,EACX,YAAa,CACjB,EAQa,EAAoB,CAS7B,IAAO,EAAa,EAAgB,CAChC,GAAI,OAAO,OAAW,IAAa,OAAO,EAC1C,GAAI,CACA,IAAM,EAAM,OAAO,aAAa,QAAQ,CAAG,EAC3C,OAAO,IAAQ,KAAO,EAAW,EAAwB,CAAG,CAChE,MAAQ,CACJ,OAAO,CACX,CACJ,EAcA,IAAO,EAAa,EAAgB,CAChC,GAAI,OAAO,OAAW,IAAa,OACnC,IAAI,EACJ,GAAI,CACA,EAAU,EAAiB,CAAK,CACpC,MAAQ,CACJ,EAAU,KAAK,UAAU,CAAK,CAClC,CACA,GAAI,CACA,OAAO,aAAa,QAAQ,EAAK,CAAO,CAC5C,MAAQ,CAER,CACJ,CACJ"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { gunzipSync as e, gzipSync as t } from "fflate";
|
|
2
|
+
//#region src/utils/compressed-storage.ts
|
|
3
|
+
var n = "~tgz1:";
|
|
4
|
+
function r(e) {
|
|
5
|
+
let t = "", n = 32768;
|
|
6
|
+
for (let r = 0; r < e.length; r += n) t += String.fromCharCode(...e.subarray(r, r + n));
|
|
7
|
+
return btoa(t);
|
|
8
|
+
}
|
|
9
|
+
function i(e) {
|
|
10
|
+
let t = atob(e), n = new Uint8Array(t.length);
|
|
11
|
+
for (let e = 0; e < t.length; e += 1) n[e] = t.charCodeAt(e);
|
|
12
|
+
return n;
|
|
13
|
+
}
|
|
14
|
+
function a(e) {
|
|
15
|
+
let i = JSON.stringify(e);
|
|
16
|
+
return `${n}${r(t(new TextEncoder().encode(i)))}`;
|
|
17
|
+
}
|
|
18
|
+
function o(t) {
|
|
19
|
+
if (!t.startsWith(n)) return JSON.parse(t);
|
|
20
|
+
let r = e(i(t.slice(6)));
|
|
21
|
+
return JSON.parse(new TextDecoder().decode(r));
|
|
22
|
+
}
|
|
23
|
+
var s = {
|
|
24
|
+
serialize: a,
|
|
25
|
+
deserialize: o
|
|
26
|
+
}, c = {
|
|
27
|
+
get(e, t) {
|
|
28
|
+
if (typeof window > "u") return t;
|
|
29
|
+
try {
|
|
30
|
+
let n = window.localStorage.getItem(e);
|
|
31
|
+
return n === null ? t : o(n);
|
|
32
|
+
} catch {
|
|
33
|
+
return t;
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
set(e, t) {
|
|
37
|
+
if (typeof window > "u") return;
|
|
38
|
+
let n;
|
|
39
|
+
try {
|
|
40
|
+
n = a(t);
|
|
41
|
+
} catch {
|
|
42
|
+
n = JSON.stringify(t);
|
|
43
|
+
}
|
|
44
|
+
try {
|
|
45
|
+
window.localStorage.setItem(e, n);
|
|
46
|
+
} catch {}
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
//#endregion
|
|
50
|
+
export { a as compressToString, c as compressedStorage, s as compressedStorageCodec, o as decompressFromString };
|
|
51
|
+
|
|
52
|
+
//# sourceMappingURL=compressed-storage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compressed-storage.js","names":[],"sources":["../../src/utils/compressed-storage.ts"],"sourcesContent":["import { gunzipSync, gzipSync } from \"fflate\";\n\n/**\n * Gzip-backed `localStorage`, for payloads that would otherwise eat the quota.\n *\n * Kept in its own module rather than folded into {@link storage} so that\n * importing the plain typed wrapper never drags `fflate` into the bundle.\n *\n * @tempest-limits empty-catch — every operation here is best-effort by\n * contract, exactly like {@link storage}. `localStorage` throws on quota\n * exhaustion, in Safari private mode, and when a cross-origin frame has storage\n * blocked; a caller persisting state has no recovery to run and no message to\n * show, so the write is dropped and the value stays in memory for the session.\n */\n\n/**\n * Prefix stamped on every value this module writes.\n *\n * It makes the format self-describing, which is what lets a read fall back to\n * plain JSON: a key that predates compression — or that a degraded write stored\n * uncompressed — is still readable, so turning compression on for an existing\n * key does not orphan the data already there. The character also cannot open a\n * JSON document, so the two cases can never be confused.\n */\nconst MARKER = \"~tgz1:\";\n\n/**\n * Base64-encode bytes without splatting the whole array into `fromCharCode`.\n *\n * `String.fromCharCode(...bytes)` overflows the argument limit somewhere around\n * a hundred thousand entries, which a compressed save reaches easily, so the\n * conversion walks the buffer in 32 KiB windows instead.\n */\nfunction bytesToBase64(bytes: Uint8Array): string {\n let binary = \"\";\n const chunkSize = 0x8000;\n for (let i = 0; i < bytes.length; i += chunkSize) {\n binary += String.fromCharCode(...bytes.subarray(i, i + chunkSize));\n }\n return btoa(binary);\n}\n\n/** Inverse of {@link bytesToBase64}. */\nfunction base64ToBytes(base64: string): Uint8Array {\n const binary = atob(base64);\n const bytes = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i += 1) {\n bytes[i] = binary.charCodeAt(i);\n }\n return bytes;\n}\n\n/**\n * Serialize a value to a gzipped, base64 string carrying the format marker.\n *\n * Base64 costs a third more characters than the raw compressed bytes, and\n * `localStorage` bills two bytes per character on top of that. Packing the\n * bytes into UTF-16 code units directly would be denser, but lone surrogates\n * survive neither every storage implementation nor a JSON round-trip, and a\n * save that decodes to garbage is far worse than one that is bigger. Even with\n * that overhead a typical JSON document lands well under a third of its\n * uncompressed size.\n *\n * @typeParam T - The value being stored.\n * @param value - Any JSON-serializable value.\n * @returns The encoded string, ready for `localStorage`.\n */\nexport function compressToString<T>(value: T): string {\n const json = JSON.stringify(value);\n const bytes = gzipSync(new TextEncoder().encode(json));\n return `${MARKER}${bytesToBase64(bytes)}`;\n}\n\n/**\n * Decode a string produced by {@link compressToString}.\n *\n * A string without the marker is parsed as plain JSON, so values written before\n * compression was enabled — or by a write that fell back after `gzipSync`\n * failed — still read back.\n *\n * @typeParam T - The expected value shape.\n * @param raw - The stored string.\n * @returns The decoded value.\n * @throws If the payload is neither valid compressed data nor valid JSON.\n */\nexport function decompressFromString<T>(raw: string): T {\n if (!raw.startsWith(MARKER)) return JSON.parse(raw) as T;\n const bytes = gunzipSync(base64ToBytes(raw.slice(MARKER.length)));\n return JSON.parse(new TextDecoder().decode(bytes)) as T;\n}\n\n/**\n * Codec pair for {@link useLocalStorage}, so a compressed key gets the hook's\n * cross-tab sync and SSR guard for free.\n *\n * @example\n * const [save, setSave] = useLocalStorage(\"save\", EMPTY_SAVE, compressedStorageCodec);\n */\nexport const compressedStorageCodec = {\n serialize: compressToString,\n deserialize: decompressFromString,\n};\n\n/**\n * Typed `localStorage` wrapper that gzips what it writes.\n *\n * Mirrors {@link storage} so the two are interchangeable at the call site; the\n * difference is only in how the value is encoded.\n */\nexport const compressedStorage = {\n /**\n * Read and decompress a key.\n *\n * @typeParam T - The expected value shape.\n * @param key - Storage key.\n * @param fallback - Returned when the key is absent, unreadable, or corrupt.\n * @returns The stored value, or `fallback`.\n */\n get<T>(key: string, fallback: T): T {\n if (typeof window === \"undefined\") return fallback;\n try {\n const raw = window.localStorage.getItem(key);\n return raw === null ? fallback : decompressFromString<T>(raw);\n } catch {\n return fallback;\n }\n },\n\n /**\n * Compress and write a key.\n *\n * When compression itself fails the value is written as plain JSON rather\n * than dropped: a slightly larger record still loads, an absent one does\n * not. Only a storage-level failure — quota, blocked storage — loses the\n * write.\n *\n * @typeParam T - The value being stored.\n * @param key - Storage key.\n * @param value - Any JSON-serializable value.\n */\n set<T>(key: string, value: T): void {\n if (typeof window === \"undefined\") return;\n let encoded: string;\n try {\n encoded = compressToString(value);\n } catch {\n encoded = JSON.stringify(value);\n }\n try {\n window.localStorage.setItem(key, encoded);\n } catch {\n /* empty */\n }\n },\n};\n"],"mappings":";;AAwBA,IAAM,IAAS;AASf,SAAS,EAAc,GAA2B;CAC9C,IAAI,IAAS,IACP,IAAY;CAClB,KAAK,IAAI,IAAI,GAAG,IAAI,EAAM,QAAQ,KAAK,GACnC,KAAU,OAAO,aAAa,GAAG,EAAM,SAAS,GAAG,IAAI,CAAS,CAAC;CAErE,OAAO,KAAK,CAAM;AACtB;AAGA,SAAS,EAAc,GAA4B;CAC/C,IAAM,IAAS,KAAK,CAAM,GACpB,IAAQ,IAAI,WAAW,EAAO,MAAM;CAC1C,KAAK,IAAI,IAAI,GAAG,IAAI,EAAO,QAAQ,KAAK,GACpC,EAAM,KAAK,EAAO,WAAW,CAAC;CAElC,OAAO;AACX;AAiBA,SAAgB,EAAoB,GAAkB;CAClD,IAAM,IAAO,KAAK,UAAU,CAAK;CAEjC,OAAO,GAAG,IAAS,EADL,EAAS,IAAI,YAAY,CAAC,CAAC,OAAO,CAAI,CACnB,CAAK;AAC1C;AAcA,SAAgB,EAAwB,GAAgB;CACpD,IAAI,CAAC,EAAI,WAAW,CAAM,GAAG,OAAO,KAAK,MAAM,CAAG;CAClD,IAAM,IAAQ,EAAW,EAAc,EAAI,MAAM,CAAa,CAAC,CAAC;CAChE,OAAO,KAAK,MAAM,IAAI,YAAY,CAAC,CAAC,OAAO,CAAK,CAAC;AACrD;AASA,IAAa,IAAyB;CAClC,WAAW;CACX,aAAa;AACjB,GAQa,IAAoB;CAS7B,IAAO,GAAa,GAAgB;EAChC,IAAI,OAAO,SAAW,KAAa,OAAO;EAC1C,IAAI;GACA,IAAM,IAAM,OAAO,aAAa,QAAQ,CAAG;GAC3C,OAAO,MAAQ,OAAO,IAAW,EAAwB,CAAG;EAChE,QAAQ;GACJ,OAAO;EACX;CACJ;CAcA,IAAO,GAAa,GAAgB;EAChC,IAAI,OAAO,SAAW,KAAa;EACnC,IAAI;EACJ,IAAI;GACA,IAAU,EAAiB,CAAK;EACpC,QAAQ;GACJ,IAAU,KAAK,UAAU,CAAK;EAClC;EACA,IAAI;GACA,OAAO,aAAa,QAAQ,GAAK,CAAO;EAC5C,QAAQ,CAER;CACJ;AACJ"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
const e=require("../share/share-or-download.cjs");var t=``;function n(e,t){return e.includes(t)||e.includes(`"`)||e.includes(`
|
|
2
|
+
`)||e.includes(`\r`)?`"${e.replaceAll(`"`,`""`)}"`:e}function r(e){return e==null?``:e instanceof Date?e.toISOString():String(e)}function i(e,i,a={}){let{delimiter:o=`,`,bom:s=!0}=a,c=[i.map(e=>n(e.header,o)).join(o)];for(let t of e){let e=i.map(e=>n(r(e.csv?e.csv(t):t[e.key]),o));c.push(e.join(o))}return`${s?t:``}${c.join(`\r
|
|
3
|
+
`)}`}async function a(t,n,r=`export.csv`,a={}){let o=new Blob([i(t,n,a)],{type:`text/csv;charset=utf-8`});await e.shareOrDownloadBlob(o,r)}exports.downloadCsv=a,exports.toCsv=i;
|
|
4
|
+
//# sourceMappingURL=csv.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"csv.cjs","names":[],"sources":["../../src/utils/csv.ts"],"sourcesContent":["// CSV that survives contact with real data. Every app writes this by hand and\n// every hand-written version gets the same two things wrong: a value containing\n// the delimiter splits the row, and a value containing a quote breaks the quoting\n// it was supposed to be protected by. Both are one-liners in RFC 4180 and neither\n// is obvious until a customer's name has a comma in it.\n\nimport { shareOrDownloadBlob } from \"../share/share-or-download\";\n\n/**\n * Byte order mark, as the character `String.prototype` sees it.\n *\n * Excel on a pt-BR install reads a BOM-less UTF-8 file as Latin-1 and turns\n * every accented name into mojibake. It is three bytes to avoid a support\n * ticket, so it is on by default.\n */\nconst BOM = \"\\uFEFF\";\n\n/** One column of the exported file. */\nexport interface CsvColumn<T> {\n /** Property of the row this column reads from. Doubles as the column key. */\n key: keyof T;\n /** Column heading, written to the first line. */\n header: string;\n /**\n * Value for the file. Defaults to `String(row[key])`, with nullish becoming an\n * empty field.\n *\n * A `DataTableColumn` renders cells to `ReactNode`, which cannot be written to\n * a text file — a badge or a link would serialize as `[object Object]`. Give\n * the column this accessor and the export says what the badge said.\n */\n csv?: (row: T) => string | number | boolean | null | undefined;\n}\n\n/** Options for {@link toCsv}. */\nexport interface CsvOptions {\n /**\n * Field separator. Default `\",\"`.\n *\n * Use `\";\"` for Excel on a locale whose decimal separator is the comma —\n * which is every pt-BR install — otherwise it opens the file in one column.\n */\n delimiter?: \",\" | \";\";\n /** Prefix the output with a UTF-8 BOM. Default `true`. */\n bom?: boolean;\n}\n\n/**\n * Quote one field per RFC 4180.\n *\n * A field is quoted when it contains the delimiter, a double quote, or a line\n * break; inside a quoted field, each double quote is doubled. Fields that need\n * none of this are written bare, which keeps the common file readable.\n *\n * @param value - The already-stringified field.\n * @param delimiter - The separator in use, which decides part of the quoting.\n * @returns The field, quoted if it has to be.\n */\nfunction escapeField(value: string, delimiter: string): string {\n const mustQuote =\n value.includes(delimiter) ||\n value.includes('\"') ||\n value.includes(\"\\n\") ||\n value.includes(\"\\r\");\n if (!mustQuote) return value;\n return `\"${value.replaceAll('\"', '\"\"')}\"`;\n}\n\n/**\n * Stringify one cell, keeping the difference between \"no value\" and \"zero\".\n *\n * `null` and `undefined` become an empty field; `0` and `false` are values\n * somebody chose and are written out. Getting this backwards is how an export\n * ends up under-reporting every row that legitimately holds a zero.\n *\n * @param value - The raw cell value.\n * @returns The text to write.\n */\nfunction cellText(value: unknown): string {\n if (value === null || value === undefined) return \"\";\n if (value instanceof Date) return value.toISOString();\n return String(value);\n}\n\n/**\n * Serialize rows to CSV text, RFC 4180 style.\n *\n * Rows are separated by `\\r\\n` — the RFC's terminator and the one Excel is least\n * surprised by. A row is emitted for the header even when `rows` is empty, so the\n * person who opens the file sees which columns they asked for instead of a blank\n * document.\n *\n * @example\n * const csv = toCsv(users, [\n * { key: \"name\", header: \"Nome\" },\n * { key: \"email\", header: \"E-mail\" },\n * { key: \"plan\", header: \"Plano\", csv: (user) => user.plan.label },\n * ]);\n *\n * @param rows - The rows to export.\n * @param columns - Columns, in the order they should appear.\n * @param options - Delimiter and BOM.\n * @returns The complete file contents.\n */\nexport function toCsv<T>(\n rows: readonly T[],\n columns: readonly CsvColumn<T>[],\n options: CsvOptions = {},\n): string {\n const { delimiter = \",\", bom = true } = options;\n\n const lines = [columns.map((column) => escapeField(column.header, delimiter)).join(delimiter)];\n\n for (const row of rows) {\n const cells = columns.map((column) => {\n const raw = column.csv\n ? column.csv(row)\n : (row as Record<string, unknown>)[column.key as string];\n return escapeField(cellText(raw), delimiter);\n });\n lines.push(cells.join(delimiter));\n }\n\n return `${bom ? BOM : \"\"}${lines.join(\"\\r\\n\")}`;\n}\n\n/**\n * Build a CSV and hand it to the user.\n *\n * Goes through {@link shareOrDownloadBlob}, so on a phone it opens the native\n * share sheet and everywhere else it downloads — the same path every other\n * generated artifact in the SDK takes, instead of a fourth hand-rolled `<a\n * download>`.\n *\n * @example\n * await downloadCsv(users, COLUMNS, \"usuarios.csv\");\n *\n * @param rows - The rows to export.\n * @param columns - Columns, in the order they should appear.\n * @param fileName - File name offered to the user. Default `\"export.csv\"`.\n * @param options - Delimiter and BOM, forwarded to {@link toCsv}.\n * @returns A promise that resolves once the share or download completes.\n */\nexport async function downloadCsv<T>(\n rows: readonly T[],\n columns: readonly CsvColumn<T>[],\n fileName = \"export.csv\",\n options: CsvOptions = {},\n): Promise<void> {\n const blob = new Blob([toCsv(rows, columns, options)], {\n type: \"text/csv;charset=utf-8\",\n });\n await shareOrDownloadBlob(blob, fileName);\n}\n"],"mappings":"kDAeA,IAAM,EAAM,IA2CZ,SAAS,EAAY,EAAe,EAA2B,CAO3D,OALI,EAAM,SAAS,CAAS,GACxB,EAAM,SAAS,GAAG,GAClB,EAAM,SAAS;CAAI,GACnB,EAAM,SAAS,IAAI,EAEhB,IAAI,EAAM,WAAW,IAAK,IAAI,EAAE,GADhB,CAE3B,CAYA,SAAS,EAAS,EAAwB,CAGtC,OAFI,GAAU,KAAoC,GAC9C,aAAiB,KAAa,EAAM,YAAY,EAC7C,OAAO,CAAK,CACvB,CAsBA,SAAgB,EACZ,EACA,EACA,EAAsB,CAAC,EACjB,CACN,GAAM,CAAE,YAAY,IAAK,MAAM,IAAS,EAElC,EAAQ,CAAC,EAAQ,IAAK,GAAW,EAAY,EAAO,OAAQ,CAAS,CAAC,CAAC,CAAC,KAAK,CAAS,CAAC,EAE7F,IAAK,IAAM,KAAO,EAAM,CACpB,IAAM,EAAQ,EAAQ,IAAK,GAIhB,EAAY,EAHP,EAAO,IACb,EAAO,IAAI,CAAG,EACb,EAAgC,EAAO,IACf,EAAG,CAAS,CAC9C,EACD,EAAM,KAAK,EAAM,KAAK,CAAS,CAAC,CACpC,CAEA,MAAO,GAAG,EAAM,EAAM,KAAK,EAAM,KAAK;CAAM,GAChD,CAmBA,eAAsB,EAClB,EACA,EACA,EAAW,aACX,EAAsB,CAAC,EACV,CACb,IAAM,EAAO,IAAI,KAAK,CAAC,EAAM,EAAM,EAAS,CAAO,CAAC,EAAG,CACnD,KAAM,wBACV,CAAC,EACD,MAAM,EAAA,oBAAoB,EAAM,CAAQ,CAC5C"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { shareOrDownloadBlob as e } from "../share/share-or-download.js";
|
|
2
|
+
//#region src/utils/csv.ts
|
|
3
|
+
var t = "";
|
|
4
|
+
function n(e, t) {
|
|
5
|
+
return e.includes(t) || e.includes("\"") || e.includes("\n") || e.includes("\r") ? `"${e.replaceAll("\"", "\"\"")}"` : e;
|
|
6
|
+
}
|
|
7
|
+
function r(e) {
|
|
8
|
+
return e == null ? "" : e instanceof Date ? e.toISOString() : String(e);
|
|
9
|
+
}
|
|
10
|
+
function i(e, i, a = {}) {
|
|
11
|
+
let { delimiter: o = ",", bom: s = !0 } = a, c = [i.map((e) => n(e.header, o)).join(o)];
|
|
12
|
+
for (let t of e) {
|
|
13
|
+
let e = i.map((e) => n(r(e.csv ? e.csv(t) : t[e.key]), o));
|
|
14
|
+
c.push(e.join(o));
|
|
15
|
+
}
|
|
16
|
+
return `${s ? t : ""}${c.join("\r\n")}`;
|
|
17
|
+
}
|
|
18
|
+
async function a(t, n, r = "export.csv", a = {}) {
|
|
19
|
+
let o = new Blob([i(t, n, a)], { type: "text/csv;charset=utf-8" });
|
|
20
|
+
await e(o, r);
|
|
21
|
+
}
|
|
22
|
+
//#endregion
|
|
23
|
+
export { a as downloadCsv, i as toCsv };
|
|
24
|
+
|
|
25
|
+
//# sourceMappingURL=csv.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"csv.js","names":[],"sources":["../../src/utils/csv.ts"],"sourcesContent":["// CSV that survives contact with real data. Every app writes this by hand and\n// every hand-written version gets the same two things wrong: a value containing\n// the delimiter splits the row, and a value containing a quote breaks the quoting\n// it was supposed to be protected by. Both are one-liners in RFC 4180 and neither\n// is obvious until a customer's name has a comma in it.\n\nimport { shareOrDownloadBlob } from \"../share/share-or-download\";\n\n/**\n * Byte order mark, as the character `String.prototype` sees it.\n *\n * Excel on a pt-BR install reads a BOM-less UTF-8 file as Latin-1 and turns\n * every accented name into mojibake. It is three bytes to avoid a support\n * ticket, so it is on by default.\n */\nconst BOM = \"\\uFEFF\";\n\n/** One column of the exported file. */\nexport interface CsvColumn<T> {\n /** Property of the row this column reads from. Doubles as the column key. */\n key: keyof T;\n /** Column heading, written to the first line. */\n header: string;\n /**\n * Value for the file. Defaults to `String(row[key])`, with nullish becoming an\n * empty field.\n *\n * A `DataTableColumn` renders cells to `ReactNode`, which cannot be written to\n * a text file — a badge or a link would serialize as `[object Object]`. Give\n * the column this accessor and the export says what the badge said.\n */\n csv?: (row: T) => string | number | boolean | null | undefined;\n}\n\n/** Options for {@link toCsv}. */\nexport interface CsvOptions {\n /**\n * Field separator. Default `\",\"`.\n *\n * Use `\";\"` for Excel on a locale whose decimal separator is the comma —\n * which is every pt-BR install — otherwise it opens the file in one column.\n */\n delimiter?: \",\" | \";\";\n /** Prefix the output with a UTF-8 BOM. Default `true`. */\n bom?: boolean;\n}\n\n/**\n * Quote one field per RFC 4180.\n *\n * A field is quoted when it contains the delimiter, a double quote, or a line\n * break; inside a quoted field, each double quote is doubled. Fields that need\n * none of this are written bare, which keeps the common file readable.\n *\n * @param value - The already-stringified field.\n * @param delimiter - The separator in use, which decides part of the quoting.\n * @returns The field, quoted if it has to be.\n */\nfunction escapeField(value: string, delimiter: string): string {\n const mustQuote =\n value.includes(delimiter) ||\n value.includes('\"') ||\n value.includes(\"\\n\") ||\n value.includes(\"\\r\");\n if (!mustQuote) return value;\n return `\"${value.replaceAll('\"', '\"\"')}\"`;\n}\n\n/**\n * Stringify one cell, keeping the difference between \"no value\" and \"zero\".\n *\n * `null` and `undefined` become an empty field; `0` and `false` are values\n * somebody chose and are written out. Getting this backwards is how an export\n * ends up under-reporting every row that legitimately holds a zero.\n *\n * @param value - The raw cell value.\n * @returns The text to write.\n */\nfunction cellText(value: unknown): string {\n if (value === null || value === undefined) return \"\";\n if (value instanceof Date) return value.toISOString();\n return String(value);\n}\n\n/**\n * Serialize rows to CSV text, RFC 4180 style.\n *\n * Rows are separated by `\\r\\n` — the RFC's terminator and the one Excel is least\n * surprised by. A row is emitted for the header even when `rows` is empty, so the\n * person who opens the file sees which columns they asked for instead of a blank\n * document.\n *\n * @example\n * const csv = toCsv(users, [\n * { key: \"name\", header: \"Nome\" },\n * { key: \"email\", header: \"E-mail\" },\n * { key: \"plan\", header: \"Plano\", csv: (user) => user.plan.label },\n * ]);\n *\n * @param rows - The rows to export.\n * @param columns - Columns, in the order they should appear.\n * @param options - Delimiter and BOM.\n * @returns The complete file contents.\n */\nexport function toCsv<T>(\n rows: readonly T[],\n columns: readonly CsvColumn<T>[],\n options: CsvOptions = {},\n): string {\n const { delimiter = \",\", bom = true } = options;\n\n const lines = [columns.map((column) => escapeField(column.header, delimiter)).join(delimiter)];\n\n for (const row of rows) {\n const cells = columns.map((column) => {\n const raw = column.csv\n ? column.csv(row)\n : (row as Record<string, unknown>)[column.key as string];\n return escapeField(cellText(raw), delimiter);\n });\n lines.push(cells.join(delimiter));\n }\n\n return `${bom ? BOM : \"\"}${lines.join(\"\\r\\n\")}`;\n}\n\n/**\n * Build a CSV and hand it to the user.\n *\n * Goes through {@link shareOrDownloadBlob}, so on a phone it opens the native\n * share sheet and everywhere else it downloads — the same path every other\n * generated artifact in the SDK takes, instead of a fourth hand-rolled `<a\n * download>`.\n *\n * @example\n * await downloadCsv(users, COLUMNS, \"usuarios.csv\");\n *\n * @param rows - The rows to export.\n * @param columns - Columns, in the order they should appear.\n * @param fileName - File name offered to the user. Default `\"export.csv\"`.\n * @param options - Delimiter and BOM, forwarded to {@link toCsv}.\n * @returns A promise that resolves once the share or download completes.\n */\nexport async function downloadCsv<T>(\n rows: readonly T[],\n columns: readonly CsvColumn<T>[],\n fileName = \"export.csv\",\n options: CsvOptions = {},\n): Promise<void> {\n const blob = new Blob([toCsv(rows, columns, options)], {\n type: \"text/csv;charset=utf-8\",\n });\n await shareOrDownloadBlob(blob, fileName);\n}\n"],"mappings":";;AAeA,IAAM,IAAM;AA2CZ,SAAS,EAAY,GAAe,GAA2B;CAO3D,OALI,EAAM,SAAS,CAAS,KACxB,EAAM,SAAS,IAAG,KAClB,EAAM,SAAS,IAAI,KACnB,EAAM,SAAS,IAAI,IAEhB,IAAI,EAAM,WAAW,MAAK,MAAI,EAAE,KADhB;AAE3B;AAYA,SAAS,EAAS,GAAwB;CAGtC,OAFI,KAAU,OAAoC,KAC9C,aAAiB,OAAa,EAAM,YAAY,IAC7C,OAAO,CAAK;AACvB;AAsBA,SAAgB,EACZ,GACA,GACA,IAAsB,CAAC,GACjB;CACN,IAAM,EAAE,eAAY,KAAK,SAAM,OAAS,GAElC,IAAQ,CAAC,EAAQ,KAAK,MAAW,EAAY,EAAO,QAAQ,CAAS,CAAC,CAAC,CAAC,KAAK,CAAS,CAAC;CAE7F,KAAK,IAAM,KAAO,GAAM;EACpB,IAAM,IAAQ,EAAQ,KAAK,MAIhB,EAAY,EAHP,EAAO,MACb,EAAO,IAAI,CAAG,IACb,EAAgC,EAAO,IACf,GAAG,CAAS,CAC9C;EACD,EAAM,KAAK,EAAM,KAAK,CAAS,CAAC;CACpC;CAEA,OAAO,GAAG,IAAM,IAAM,KAAK,EAAM,KAAK,MAAM;AAChD;AAmBA,eAAsB,EAClB,GACA,GACA,IAAW,cACX,IAAsB,CAAC,GACV;CACb,IAAM,IAAO,IAAI,KAAK,CAAC,EAAM,GAAM,GAAS,CAAO,CAAC,GAAG,EACnD,MAAM,yBACV,CAAC;CACD,MAAM,EAAoB,GAAM,CAAQ;AAC5C"}
|
package/dist/utils/format.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
function e(e){return new Intl.NumberFormat(`pt-BR`,{style:`currency`,currency:`BRL`}).format(e)}function t(e){let t=typeof e==`string`?new Date(e):e;return Number.isNaN(t.getTime())?``:new Intl.DateTimeFormat(`pt-BR`).format(t)}function n(e){let t=typeof e==`string`?new Date(e):e;return Number.isNaN(t.getTime())?``:new Intl.DateTimeFormat(`pt-BR`,{dateStyle:`short`,timeStyle:`short`}).format(t)}function
|
|
1
|
+
function e(e){return new Intl.NumberFormat(`pt-BR`,{style:`currency`,currency:`BRL`}).format(e)}function t(e){let t=typeof e==`string`?new Date(e):e;return Number.isNaN(t.getTime())?``:new Intl.DateTimeFormat(`pt-BR`).format(t)}function n(e){if(typeof e==`string`&&/^\d{4}-\d{2}-\d{2}$/.test(e))return e;let t=typeof e==`string`?new Date(e):e;if(Number.isNaN(t.getTime()))return``;let n=`${t.getMonth()+1}`.padStart(2,`0`),r=`${t.getDate()}`.padStart(2,`0`);return`${t.getFullYear()}-${n}-${r}`}function r(e){let t=typeof e==`string`?new Date(e):e;return Number.isNaN(t.getTime())?``:new Intl.DateTimeFormat(`pt-BR`,{dateStyle:`short`,timeStyle:`short`}).format(t)}function i(e){let t=e.replace(/\D/g,``).slice(0,11);return t.length<=10?t.replace(/(\d{2})(\d)/,`($1) $2`).replace(/(\d{4})(\d)/,`$1-$2`):t.replace(/(\d{2})(\d)/,`($1) $2`).replace(/(\d{5})(\d)/,`$1-$2`)}function a(e){return e.replace(/\D/g,``).slice(0,11).replace(/(\d{3})(\d)/,`$1.$2`).replace(/(\d{3})(\d)/,`$1.$2`).replace(/(\d{3})(\d{1,2})$/,`$1-$2`)}function o(e){return new Intl.NumberFormat(`pt-BR`,{style:`percent`,minimumFractionDigits:1,maximumFractionDigits:1}).format(e)}exports.formatCPF=a,exports.formatCurrency=e,exports.formatDate=t,exports.formatDateForInput=n,exports.formatDateTime=r,exports.formatPercent=o,exports.formatPhone=i;
|
|
2
2
|
//# sourceMappingURL=format.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"format.cjs","names":[],"sources":["../../src/utils/format.ts"],"sourcesContent":["/**\n * Format a number as Brazilian Real currency.\n *\n * @param value - The amount in BRL.\n * @returns A locale-formatted string, e.g. \"R$ 1.234,56\".\n */\nexport function formatCurrency(value: number): string {\n return new Intl.NumberFormat(\"pt-BR\", {\n style: \"currency\",\n currency: \"BRL\",\n }).format(value);\n}\n\n/**\n * Format an ISO date or Date instance as `dd/MM/yyyy`.\n *\n * @param value - ISO string or Date.\n * @returns Formatted date string, or empty string when input is invalid.\n */\nexport function formatDate(value: string | Date): string {\n const date = typeof value === \"string\" ? new Date(value) : value;\n if (Number.isNaN(date.getTime())) return \"\";\n return new Intl.DateTimeFormat(\"pt-BR\").format(date);\n}\n\n/**\n * Format an ISO date or Date instance as `dd/MM/yyyy HH:mm`.\n *\n * @param value - ISO string or Date.\n * @returns Formatted datetime string, or empty string when input is invalid.\n */\nexport function formatDateTime(value: string | Date): string {\n const date = typeof value === \"string\" ? new Date(value) : value;\n if (Number.isNaN(date.getTime())) return \"\";\n return new Intl.DateTimeFormat(\"pt-BR\", {\n dateStyle: \"short\",\n timeStyle: \"short\",\n }).format(date);\n}\n\n/**\n * Apply the Brazilian phone mask `(XX) XXXXX-XXXX` or `(XX) XXXX-XXXX`.\n *\n * @param value - Raw digits or partially masked string.\n * @returns Masked phone string.\n */\nexport function formatPhone(value: string): string {\n const digits = value.replace(/\\D/g, \"\").slice(0, 11);\n if (digits.length <= 10) {\n return digits.replace(/(\\d{2})(\\d)/, \"($1) $2\").replace(/(\\d{4})(\\d)/, \"$1-$2\");\n }\n return digits.replace(/(\\d{2})(\\d)/, \"($1) $2\").replace(/(\\d{5})(\\d)/, \"$1-$2\");\n}\n\n/**\n * Apply the Brazilian CPF mask `XXX.XXX.XXX-XX`.\n *\n * @param value - Raw digits or partially masked string.\n * @returns Masked CPF string.\n */\nexport function formatCPF(value: string): string {\n return value\n .replace(/\\D/g, \"\")\n .slice(0, 11)\n .replace(/(\\d{3})(\\d)/, \"$1.$2\")\n .replace(/(\\d{3})(\\d)/, \"$1.$2\")\n .replace(/(\\d{3})(\\d{1,2})$/, \"$1-$2\");\n}\n\n/**\n * Format a fraction (0-1) as a percentage with one decimal.\n *\n * @param value - Fraction between 0 and 1.\n * @returns Formatted percent string, e.g. \"12,5%\".\n */\nexport function formatPercent(value: number): string {\n return new Intl.NumberFormat(\"pt-BR\", {\n style: \"percent\",\n minimumFractionDigits: 1,\n maximumFractionDigits: 1,\n }).format(value);\n}\n"],"mappings":"AAMA,SAAgB,EAAe,EAAuB,CAClD,OAAO,IAAI,KAAK,aAAa,QAAS,CAClC,MAAO,WACP,SAAU,KACd,CAAC,CAAC,CAAC,OAAO,CAAK,CACnB,CAQA,SAAgB,EAAW,EAA8B,CACrD,IAAM,EAAO,OAAO,GAAU,SAAW,IAAI,KAAK,CAAK,EAAI,EAE3D,OADI,OAAO,MAAM,EAAK,QAAQ,CAAC,EAAU,GAClC,IAAI,KAAK,eAAe,OAAO,CAAC,CAAC,OAAO,CAAI,CACvD,CAQA,SAAgB,EAAe,EAA8B,CACzD,IAAM,EAAO,OAAO,GAAU,SAAW,IAAI,KAAK,CAAK,EAAI,EAE3D,OADI,OAAO,MAAM,EAAK,QAAQ,CAAC,EAAU,GAClC,IAAI,KAAK,eAAe,QAAS,CACpC,UAAW,QACX,UAAW,OACf,CAAC,CAAC,CAAC,OAAO,CAAI,CAClB,CAQA,SAAgB,EAAY,EAAuB,CAC/C,IAAM,EAAS,EAAM,QAAQ,MAAO,EAAE,CAAC,CAAC,MAAM,EAAG,EAAE,EAInD,OAHI,EAAO,QAAU,GACV,EAAO,QAAQ,cAAe,SAAS,CAAC,CAAC,QAAQ,cAAe,OAAO,EAE3E,EAAO,QAAQ,cAAe,SAAS,CAAC,CAAC,QAAQ,cAAe,OAAO,CAClF,CAQA,SAAgB,EAAU,EAAuB,CAC7C,OAAO,EACF,QAAQ,MAAO,EAAE,CAAC,CAClB,MAAM,EAAG,EAAE,CAAC,CACZ,QAAQ,cAAe,OAAO,CAAC,CAC/B,QAAQ,cAAe,OAAO,CAAC,CAC/B,QAAQ,oBAAqB,OAAO,CAC7C,CAQA,SAAgB,EAAc,EAAuB,CACjD,OAAO,IAAI,KAAK,aAAa,QAAS,CAClC,MAAO,UACP,sBAAuB,EACvB,sBAAuB,CAC3B,CAAC,CAAC,CAAC,OAAO,CAAK,CACnB"}
|
|
1
|
+
{"version":3,"file":"format.cjs","names":[],"sources":["../../src/utils/format.ts"],"sourcesContent":["/**\n * Format a number as Brazilian Real currency.\n *\n * @param value - The amount in BRL.\n * @returns A locale-formatted string, e.g. \"R$ 1.234,56\".\n */\nexport function formatCurrency(value: number): string {\n return new Intl.NumberFormat(\"pt-BR\", {\n style: \"currency\",\n currency: \"BRL\",\n }).format(value);\n}\n\n/**\n * Format an ISO date or Date instance as `dd/MM/yyyy`.\n *\n * @param value - ISO string or Date.\n * @returns Formatted date string, or empty string when input is invalid.\n */\nexport function formatDate(value: string | Date): string {\n const date = typeof value === \"string\" ? new Date(value) : value;\n if (Number.isNaN(date.getTime())) return \"\";\n return new Intl.DateTimeFormat(\"pt-BR\").format(date);\n}\n\n/**\n * Format an ISO date or Date instance as `yyyy-MM-dd`, the value an\n * `<input type=\"date\">` accepts.\n *\n * Built from the **local** calendar parts rather than `toISOString().slice(0, 10)`,\n * which is the reflex and which is wrong: `toISOString` converts to UTC first, so\n * anything after 21:00 in UTC-3 reports the next day and the form opens on the\n * wrong date. `formatDate` cannot fill this role because a date input rejects\n * `dd/MM/yyyy` outright.\n *\n * A value that is already `yyyy-MM-dd` is returned untouched, and that shortcut\n * is load-bearing rather than an optimisation: `new Date(\"2026-05-16\")` is parsed\n * as **UTC** midnight, which in UTC-3 is the 15th at 21:00, so round-tripping the\n * exact value a backend sent would move it back a day.\n *\n * @example\n * <input type=\"date\" defaultValue={formatDateForInput(order.createdAt)} />\n *\n * @param value - ISO string or Date.\n * @returns The `yyyy-MM-dd` value, or an empty string when the input is invalid —\n * which is what a date input reads as \"no value\", unlike `\"Invalid Date\"`.\n */\nexport function formatDateForInput(value: string | Date): string {\n if (typeof value === \"string\" && /^\\d{4}-\\d{2}-\\d{2}$/.test(value)) return value;\n const date = typeof value === \"string\" ? new Date(value) : value;\n if (Number.isNaN(date.getTime())) return \"\";\n const month = `${date.getMonth() + 1}`.padStart(2, \"0\");\n const day = `${date.getDate()}`.padStart(2, \"0\");\n return `${date.getFullYear()}-${month}-${day}`;\n}\n\n/**\n * Format an ISO date or Date instance as `dd/MM/yyyy HH:mm`.\n *\n * @param value - ISO string or Date.\n * @returns Formatted datetime string, or empty string when input is invalid.\n */\nexport function formatDateTime(value: string | Date): string {\n const date = typeof value === \"string\" ? new Date(value) : value;\n if (Number.isNaN(date.getTime())) return \"\";\n return new Intl.DateTimeFormat(\"pt-BR\", {\n dateStyle: \"short\",\n timeStyle: \"short\",\n }).format(date);\n}\n\n/**\n * Apply the Brazilian phone mask `(XX) XXXXX-XXXX` or `(XX) XXXX-XXXX`.\n *\n * @param value - Raw digits or partially masked string.\n * @returns Masked phone string.\n */\nexport function formatPhone(value: string): string {\n const digits = value.replace(/\\D/g, \"\").slice(0, 11);\n if (digits.length <= 10) {\n return digits.replace(/(\\d{2})(\\d)/, \"($1) $2\").replace(/(\\d{4})(\\d)/, \"$1-$2\");\n }\n return digits.replace(/(\\d{2})(\\d)/, \"($1) $2\").replace(/(\\d{5})(\\d)/, \"$1-$2\");\n}\n\n/**\n * Apply the Brazilian CPF mask `XXX.XXX.XXX-XX`.\n *\n * @param value - Raw digits or partially masked string.\n * @returns Masked CPF string.\n */\nexport function formatCPF(value: string): string {\n return value\n .replace(/\\D/g, \"\")\n .slice(0, 11)\n .replace(/(\\d{3})(\\d)/, \"$1.$2\")\n .replace(/(\\d{3})(\\d)/, \"$1.$2\")\n .replace(/(\\d{3})(\\d{1,2})$/, \"$1-$2\");\n}\n\n/**\n * Format a fraction (0-1) as a percentage with one decimal.\n *\n * @param value - Fraction between 0 and 1.\n * @returns Formatted percent string, e.g. \"12,5%\".\n */\nexport function formatPercent(value: number): string {\n return new Intl.NumberFormat(\"pt-BR\", {\n style: \"percent\",\n minimumFractionDigits: 1,\n maximumFractionDigits: 1,\n }).format(value);\n}\n"],"mappings":"AAMA,SAAgB,EAAe,EAAuB,CAClD,OAAO,IAAI,KAAK,aAAa,QAAS,CAClC,MAAO,WACP,SAAU,KACd,CAAC,CAAC,CAAC,OAAO,CAAK,CACnB,CAQA,SAAgB,EAAW,EAA8B,CACrD,IAAM,EAAO,OAAO,GAAU,SAAW,IAAI,KAAK,CAAK,EAAI,EAE3D,OADI,OAAO,MAAM,EAAK,QAAQ,CAAC,EAAU,GAClC,IAAI,KAAK,eAAe,OAAO,CAAC,CAAC,OAAO,CAAI,CACvD,CAwBA,SAAgB,EAAmB,EAA8B,CAC7D,GAAI,OAAO,GAAU,UAAY,sBAAsB,KAAK,CAAK,EAAG,OAAO,EAC3E,IAAM,EAAO,OAAO,GAAU,SAAW,IAAI,KAAK,CAAK,EAAI,EAC3D,GAAI,OAAO,MAAM,EAAK,QAAQ,CAAC,EAAG,MAAO,GACzC,IAAM,EAAQ,GAAG,EAAK,SAAS,EAAI,IAAI,SAAS,EAAG,GAAG,EAChD,EAAM,GAAG,EAAK,QAAQ,IAAI,SAAS,EAAG,GAAG,EAC/C,MAAO,GAAG,EAAK,YAAY,EAAE,GAAG,EAAM,GAAG,GAC7C,CAQA,SAAgB,EAAe,EAA8B,CACzD,IAAM,EAAO,OAAO,GAAU,SAAW,IAAI,KAAK,CAAK,EAAI,EAE3D,OADI,OAAO,MAAM,EAAK,QAAQ,CAAC,EAAU,GAClC,IAAI,KAAK,eAAe,QAAS,CACpC,UAAW,QACX,UAAW,OACf,CAAC,CAAC,CAAC,OAAO,CAAI,CAClB,CAQA,SAAgB,EAAY,EAAuB,CAC/C,IAAM,EAAS,EAAM,QAAQ,MAAO,EAAE,CAAC,CAAC,MAAM,EAAG,EAAE,EAInD,OAHI,EAAO,QAAU,GACV,EAAO,QAAQ,cAAe,SAAS,CAAC,CAAC,QAAQ,cAAe,OAAO,EAE3E,EAAO,QAAQ,cAAe,SAAS,CAAC,CAAC,QAAQ,cAAe,OAAO,CAClF,CAQA,SAAgB,EAAU,EAAuB,CAC7C,OAAO,EACF,QAAQ,MAAO,EAAE,CAAC,CAClB,MAAM,EAAG,EAAE,CAAC,CACZ,QAAQ,cAAe,OAAO,CAAC,CAC/B,QAAQ,cAAe,OAAO,CAAC,CAC/B,QAAQ,oBAAqB,OAAO,CAC7C,CAQA,SAAgB,EAAc,EAAuB,CACjD,OAAO,IAAI,KAAK,aAAa,QAAS,CAClC,MAAO,UACP,sBAAuB,EACvB,sBAAuB,CAC3B,CAAC,CAAC,CAAC,OAAO,CAAK,CACnB"}
|
package/dist/utils/format.js
CHANGED
|
@@ -10,20 +10,27 @@ function t(e) {
|
|
|
10
10
|
return Number.isNaN(t.getTime()) ? "" : new Intl.DateTimeFormat("pt-BR").format(t);
|
|
11
11
|
}
|
|
12
12
|
function n(e) {
|
|
13
|
+
if (typeof e == "string" && /^\d{4}-\d{2}-\d{2}$/.test(e)) return e;
|
|
14
|
+
let t = typeof e == "string" ? new Date(e) : e;
|
|
15
|
+
if (Number.isNaN(t.getTime())) return "";
|
|
16
|
+
let n = `${t.getMonth() + 1}`.padStart(2, "0"), r = `${t.getDate()}`.padStart(2, "0");
|
|
17
|
+
return `${t.getFullYear()}-${n}-${r}`;
|
|
18
|
+
}
|
|
19
|
+
function r(e) {
|
|
13
20
|
let t = typeof e == "string" ? new Date(e) : e;
|
|
14
21
|
return Number.isNaN(t.getTime()) ? "" : new Intl.DateTimeFormat("pt-BR", {
|
|
15
22
|
dateStyle: "short",
|
|
16
23
|
timeStyle: "short"
|
|
17
24
|
}).format(t);
|
|
18
25
|
}
|
|
19
|
-
function
|
|
26
|
+
function i(e) {
|
|
20
27
|
let t = e.replace(/\D/g, "").slice(0, 11);
|
|
21
28
|
return t.length <= 10 ? t.replace(/(\d{2})(\d)/, "($1) $2").replace(/(\d{4})(\d)/, "$1-$2") : t.replace(/(\d{2})(\d)/, "($1) $2").replace(/(\d{5})(\d)/, "$1-$2");
|
|
22
29
|
}
|
|
23
|
-
function
|
|
30
|
+
function a(e) {
|
|
24
31
|
return e.replace(/\D/g, "").slice(0, 11).replace(/(\d{3})(\d)/, "$1.$2").replace(/(\d{3})(\d)/, "$1.$2").replace(/(\d{3})(\d{1,2})$/, "$1-$2");
|
|
25
32
|
}
|
|
26
|
-
function
|
|
33
|
+
function o(e) {
|
|
27
34
|
return new Intl.NumberFormat("pt-BR", {
|
|
28
35
|
style: "percent",
|
|
29
36
|
minimumFractionDigits: 1,
|
|
@@ -31,6 +38,6 @@ function a(e) {
|
|
|
31
38
|
}).format(e);
|
|
32
39
|
}
|
|
33
40
|
//#endregion
|
|
34
|
-
export {
|
|
41
|
+
export { a as formatCPF, e as formatCurrency, t as formatDate, n as formatDateForInput, r as formatDateTime, o as formatPercent, i as formatPhone };
|
|
35
42
|
|
|
36
43
|
//# sourceMappingURL=format.js.map
|
package/dist/utils/format.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"format.js","names":[],"sources":["../../src/utils/format.ts"],"sourcesContent":["/**\n * Format a number as Brazilian Real currency.\n *\n * @param value - The amount in BRL.\n * @returns A locale-formatted string, e.g. \"R$ 1.234,56\".\n */\nexport function formatCurrency(value: number): string {\n return new Intl.NumberFormat(\"pt-BR\", {\n style: \"currency\",\n currency: \"BRL\",\n }).format(value);\n}\n\n/**\n * Format an ISO date or Date instance as `dd/MM/yyyy`.\n *\n * @param value - ISO string or Date.\n * @returns Formatted date string, or empty string when input is invalid.\n */\nexport function formatDate(value: string | Date): string {\n const date = typeof value === \"string\" ? new Date(value) : value;\n if (Number.isNaN(date.getTime())) return \"\";\n return new Intl.DateTimeFormat(\"pt-BR\").format(date);\n}\n\n/**\n * Format an ISO date or Date instance as `dd/MM/yyyy HH:mm`.\n *\n * @param value - ISO string or Date.\n * @returns Formatted datetime string, or empty string when input is invalid.\n */\nexport function formatDateTime(value: string | Date): string {\n const date = typeof value === \"string\" ? new Date(value) : value;\n if (Number.isNaN(date.getTime())) return \"\";\n return new Intl.DateTimeFormat(\"pt-BR\", {\n dateStyle: \"short\",\n timeStyle: \"short\",\n }).format(date);\n}\n\n/**\n * Apply the Brazilian phone mask `(XX) XXXXX-XXXX` or `(XX) XXXX-XXXX`.\n *\n * @param value - Raw digits or partially masked string.\n * @returns Masked phone string.\n */\nexport function formatPhone(value: string): string {\n const digits = value.replace(/\\D/g, \"\").slice(0, 11);\n if (digits.length <= 10) {\n return digits.replace(/(\\d{2})(\\d)/, \"($1) $2\").replace(/(\\d{4})(\\d)/, \"$1-$2\");\n }\n return digits.replace(/(\\d{2})(\\d)/, \"($1) $2\").replace(/(\\d{5})(\\d)/, \"$1-$2\");\n}\n\n/**\n * Apply the Brazilian CPF mask `XXX.XXX.XXX-XX`.\n *\n * @param value - Raw digits or partially masked string.\n * @returns Masked CPF string.\n */\nexport function formatCPF(value: string): string {\n return value\n .replace(/\\D/g, \"\")\n .slice(0, 11)\n .replace(/(\\d{3})(\\d)/, \"$1.$2\")\n .replace(/(\\d{3})(\\d)/, \"$1.$2\")\n .replace(/(\\d{3})(\\d{1,2})$/, \"$1-$2\");\n}\n\n/**\n * Format a fraction (0-1) as a percentage with one decimal.\n *\n * @param value - Fraction between 0 and 1.\n * @returns Formatted percent string, e.g. \"12,5%\".\n */\nexport function formatPercent(value: number): string {\n return new Intl.NumberFormat(\"pt-BR\", {\n style: \"percent\",\n minimumFractionDigits: 1,\n maximumFractionDigits: 1,\n }).format(value);\n}\n"],"mappings":";AAMA,SAAgB,EAAe,GAAuB;CAClD,OAAO,IAAI,KAAK,aAAa,SAAS;EAClC,OAAO;EACP,UAAU;CACd,CAAC,CAAC,CAAC,OAAO,CAAK;AACnB;AAQA,SAAgB,EAAW,GAA8B;CACrD,IAAM,IAAO,OAAO,KAAU,WAAW,IAAI,KAAK,CAAK,IAAI;CAE3D,OADI,OAAO,MAAM,EAAK,QAAQ,CAAC,IAAU,KAClC,IAAI,KAAK,eAAe,OAAO,CAAC,CAAC,OAAO,CAAI;AACvD;AAQA,SAAgB,EAAe,GAA8B;CACzD,IAAM,IAAO,OAAO,KAAU,WAAW,IAAI,KAAK,CAAK,IAAI;CAE3D,OADI,OAAO,MAAM,EAAK,QAAQ,CAAC,IAAU,KAClC,IAAI,KAAK,eAAe,SAAS;EACpC,WAAW;EACX,WAAW;CACf,CAAC,CAAC,CAAC,OAAO,CAAI;AAClB;AAQA,SAAgB,EAAY,GAAuB;CAC/C,IAAM,IAAS,EAAM,QAAQ,OAAO,EAAE,CAAC,CAAC,MAAM,GAAG,EAAE;CAInD,OAHI,EAAO,UAAU,KACV,EAAO,QAAQ,eAAe,SAAS,CAAC,CAAC,QAAQ,eAAe,OAAO,IAE3E,EAAO,QAAQ,eAAe,SAAS,CAAC,CAAC,QAAQ,eAAe,OAAO;AAClF;AAQA,SAAgB,EAAU,GAAuB;CAC7C,OAAO,EACF,QAAQ,OAAO,EAAE,CAAC,CAClB,MAAM,GAAG,EAAE,CAAC,CACZ,QAAQ,eAAe,OAAO,CAAC,CAC/B,QAAQ,eAAe,OAAO,CAAC,CAC/B,QAAQ,qBAAqB,OAAO;AAC7C;AAQA,SAAgB,EAAc,GAAuB;CACjD,OAAO,IAAI,KAAK,aAAa,SAAS;EAClC,OAAO;EACP,uBAAuB;EACvB,uBAAuB;CAC3B,CAAC,CAAC,CAAC,OAAO,CAAK;AACnB"}
|
|
1
|
+
{"version":3,"file":"format.js","names":[],"sources":["../../src/utils/format.ts"],"sourcesContent":["/**\n * Format a number as Brazilian Real currency.\n *\n * @param value - The amount in BRL.\n * @returns A locale-formatted string, e.g. \"R$ 1.234,56\".\n */\nexport function formatCurrency(value: number): string {\n return new Intl.NumberFormat(\"pt-BR\", {\n style: \"currency\",\n currency: \"BRL\",\n }).format(value);\n}\n\n/**\n * Format an ISO date or Date instance as `dd/MM/yyyy`.\n *\n * @param value - ISO string or Date.\n * @returns Formatted date string, or empty string when input is invalid.\n */\nexport function formatDate(value: string | Date): string {\n const date = typeof value === \"string\" ? new Date(value) : value;\n if (Number.isNaN(date.getTime())) return \"\";\n return new Intl.DateTimeFormat(\"pt-BR\").format(date);\n}\n\n/**\n * Format an ISO date or Date instance as `yyyy-MM-dd`, the value an\n * `<input type=\"date\">` accepts.\n *\n * Built from the **local** calendar parts rather than `toISOString().slice(0, 10)`,\n * which is the reflex and which is wrong: `toISOString` converts to UTC first, so\n * anything after 21:00 in UTC-3 reports the next day and the form opens on the\n * wrong date. `formatDate` cannot fill this role because a date input rejects\n * `dd/MM/yyyy` outright.\n *\n * A value that is already `yyyy-MM-dd` is returned untouched, and that shortcut\n * is load-bearing rather than an optimisation: `new Date(\"2026-05-16\")` is parsed\n * as **UTC** midnight, which in UTC-3 is the 15th at 21:00, so round-tripping the\n * exact value a backend sent would move it back a day.\n *\n * @example\n * <input type=\"date\" defaultValue={formatDateForInput(order.createdAt)} />\n *\n * @param value - ISO string or Date.\n * @returns The `yyyy-MM-dd` value, or an empty string when the input is invalid —\n * which is what a date input reads as \"no value\", unlike `\"Invalid Date\"`.\n */\nexport function formatDateForInput(value: string | Date): string {\n if (typeof value === \"string\" && /^\\d{4}-\\d{2}-\\d{2}$/.test(value)) return value;\n const date = typeof value === \"string\" ? new Date(value) : value;\n if (Number.isNaN(date.getTime())) return \"\";\n const month = `${date.getMonth() + 1}`.padStart(2, \"0\");\n const day = `${date.getDate()}`.padStart(2, \"0\");\n return `${date.getFullYear()}-${month}-${day}`;\n}\n\n/**\n * Format an ISO date or Date instance as `dd/MM/yyyy HH:mm`.\n *\n * @param value - ISO string or Date.\n * @returns Formatted datetime string, or empty string when input is invalid.\n */\nexport function formatDateTime(value: string | Date): string {\n const date = typeof value === \"string\" ? new Date(value) : value;\n if (Number.isNaN(date.getTime())) return \"\";\n return new Intl.DateTimeFormat(\"pt-BR\", {\n dateStyle: \"short\",\n timeStyle: \"short\",\n }).format(date);\n}\n\n/**\n * Apply the Brazilian phone mask `(XX) XXXXX-XXXX` or `(XX) XXXX-XXXX`.\n *\n * @param value - Raw digits or partially masked string.\n * @returns Masked phone string.\n */\nexport function formatPhone(value: string): string {\n const digits = value.replace(/\\D/g, \"\").slice(0, 11);\n if (digits.length <= 10) {\n return digits.replace(/(\\d{2})(\\d)/, \"($1) $2\").replace(/(\\d{4})(\\d)/, \"$1-$2\");\n }\n return digits.replace(/(\\d{2})(\\d)/, \"($1) $2\").replace(/(\\d{5})(\\d)/, \"$1-$2\");\n}\n\n/**\n * Apply the Brazilian CPF mask `XXX.XXX.XXX-XX`.\n *\n * @param value - Raw digits or partially masked string.\n * @returns Masked CPF string.\n */\nexport function formatCPF(value: string): string {\n return value\n .replace(/\\D/g, \"\")\n .slice(0, 11)\n .replace(/(\\d{3})(\\d)/, \"$1.$2\")\n .replace(/(\\d{3})(\\d)/, \"$1.$2\")\n .replace(/(\\d{3})(\\d{1,2})$/, \"$1-$2\");\n}\n\n/**\n * Format a fraction (0-1) as a percentage with one decimal.\n *\n * @param value - Fraction between 0 and 1.\n * @returns Formatted percent string, e.g. \"12,5%\".\n */\nexport function formatPercent(value: number): string {\n return new Intl.NumberFormat(\"pt-BR\", {\n style: \"percent\",\n minimumFractionDigits: 1,\n maximumFractionDigits: 1,\n }).format(value);\n}\n"],"mappings":";AAMA,SAAgB,EAAe,GAAuB;CAClD,OAAO,IAAI,KAAK,aAAa,SAAS;EAClC,OAAO;EACP,UAAU;CACd,CAAC,CAAC,CAAC,OAAO,CAAK;AACnB;AAQA,SAAgB,EAAW,GAA8B;CACrD,IAAM,IAAO,OAAO,KAAU,WAAW,IAAI,KAAK,CAAK,IAAI;CAE3D,OADI,OAAO,MAAM,EAAK,QAAQ,CAAC,IAAU,KAClC,IAAI,KAAK,eAAe,OAAO,CAAC,CAAC,OAAO,CAAI;AACvD;AAwBA,SAAgB,EAAmB,GAA8B;CAC7D,IAAI,OAAO,KAAU,YAAY,sBAAsB,KAAK,CAAK,GAAG,OAAO;CAC3E,IAAM,IAAO,OAAO,KAAU,WAAW,IAAI,KAAK,CAAK,IAAI;CAC3D,IAAI,OAAO,MAAM,EAAK,QAAQ,CAAC,GAAG,OAAO;CACzC,IAAM,IAAQ,GAAG,EAAK,SAAS,IAAI,IAAI,SAAS,GAAG,GAAG,GAChD,IAAM,GAAG,EAAK,QAAQ,IAAI,SAAS,GAAG,GAAG;CAC/C,OAAO,GAAG,EAAK,YAAY,EAAE,GAAG,EAAM,GAAG;AAC7C;AAQA,SAAgB,EAAe,GAA8B;CACzD,IAAM,IAAO,OAAO,KAAU,WAAW,IAAI,KAAK,CAAK,IAAI;CAE3D,OADI,OAAO,MAAM,EAAK,QAAQ,CAAC,IAAU,KAClC,IAAI,KAAK,eAAe,SAAS;EACpC,WAAW;EACX,WAAW;CACf,CAAC,CAAC,CAAC,OAAO,CAAI;AAClB;AAQA,SAAgB,EAAY,GAAuB;CAC/C,IAAM,IAAS,EAAM,QAAQ,OAAO,EAAE,CAAC,CAAC,MAAM,GAAG,EAAE;CAInD,OAHI,EAAO,UAAU,KACV,EAAO,QAAQ,eAAe,SAAS,CAAC,CAAC,QAAQ,eAAe,OAAO,IAE3E,EAAO,QAAQ,eAAe,SAAS,CAAC,CAAC,QAAQ,eAAe,OAAO;AAClF;AAQA,SAAgB,EAAU,GAAuB;CAC7C,OAAO,EACF,QAAQ,OAAO,EAAE,CAAC,CAClB,MAAM,GAAG,EAAE,CAAC,CACZ,QAAQ,eAAe,OAAO,CAAC,CAC/B,QAAQ,eAAe,OAAO,CAAC,CAC/B,QAAQ,qBAAqB,OAAO;AAC7C;AAQA,SAAgB,EAAc,GAAuB;CACjD,OAAO,IAAI,KAAK,aAAa,SAAS;EAClC,OAAO;EACP,uBAAuB;EACvB,uBAAuB;CAC3B,CAAC,CAAC,CAAC,OAAO,CAAK;AACnB"}
|
package/dist/utils/numbers.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
function e(e,t,n){if(Number.isNaN(e))return e;if(t>n){let e=t;t=n,n=e}return e<t?t:e>n?n:e}function t(e,t=1){if(e===0)return`0 B`;let n=[`B`,`KB`,`MB`,`GB`,`TB`],r=1024,i=Math.min(Math.floor(Math.log(Math.abs(e))/Math.log(r)),n.length-1),a=e/r**i,o=i===0?0:Math.max(0,t);return`${parseFloat(a.toFixed(o)).toString()} ${n[i]}`}function n(e,t=`en-US`){return new Intl.NumberFormat(t,{notation:`compact`,maximumFractionDigits:1}).format(e)}exports.clamp=e,exports.formatBytes=t,exports.formatCompactNumber=n;
|
|
1
|
+
function e(e,t,n){if(Number.isNaN(e))return e;if(t>n){let e=t;t=n,n=e}return e<t?t:e>n?n:e}function t(e,t=1){if(e===0)return`0 B`;let n=[`B`,`KB`,`MB`,`GB`,`TB`],r=1024,i=Math.min(Math.floor(Math.log(Math.abs(e))/Math.log(r)),n.length-1),a=e/r**i,o=i===0?0:Math.max(0,t);return`${parseFloat(a.toFixed(o)).toString()} ${n[i]}`}function n(e,t){return!Number.isFinite(e)||!Number.isFinite(t)||t===0?0:e/t*100}function r(e,t=`en-US`){return new Intl.NumberFormat(t,{notation:`compact`,maximumFractionDigits:1}).format(e)}exports.clamp=e,exports.formatBytes=t,exports.formatCompactNumber=r,exports.percentOf=n;
|
|
2
2
|
//# sourceMappingURL=numbers.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"numbers.cjs","names":[],"sources":["../../src/utils/numbers.ts"],"sourcesContent":["/**\n * Clamp `value` between `min` and `max` (inclusive).\n *\n * @example\n * clamp(120, 0, 100); // 100\n * clamp(-5, 0, 100); // 0\n * clamp(42, 0, 100); // 42\n */\nexport function clamp(value: number, min: number, max: number): number {\n if (Number.isNaN(value)) return value;\n if (min > max) {\n const swap = min;\n min = max;\n max = swap;\n }\n if (value < min) return min;\n if (value > max) return max;\n return value;\n}\n\n/**\n * Format a byte count as a human-readable size string.\n *\n * Scales through B, KB, MB, GB and TB using 1024 as the base. `decimals`\n * controls the fractional digits for scaled units (default 1); `0` bytes\n * always renders as `\"0 B\"`.\n *\n * @example\n * formatBytes(0); // \"0 B\"\n * formatBytes(1536); // \"1.5 KB\"\n * formatBytes(1048576); // \"1 MB\"\n * formatBytes(1536, 2); // \"1.50 KB\"\n */\nexport function formatBytes(bytes: number, decimals: number = 1): string {\n if (bytes === 0) return \"0 B\";\n\n const units = [\"B\", \"KB\", \"MB\", \"GB\", \"TB\"];\n const base = 1024;\n const exponent = Math.min(\n Math.floor(Math.log(Math.abs(bytes)) / Math.log(base)),\n units.length - 1,\n );\n const value = bytes / base ** exponent;\n const digits = exponent === 0 ? 0 : Math.max(0, decimals);\n const formatted = parseFloat(value.toFixed(digits)).toString();\n\n return `${formatted} ${units[exponent]}`;\n}\n\n/**\n * Format a number using compact notation (e.g. `1.2K`, `3.4M`).\n *\n * Wraps `Intl.NumberFormat` with `notation: \"compact\"`. The `locale` defaults\n * to `\"en-US\"`.\n *\n * @example\n * formatCompactNumber(1234); // \"1.2K\"\n * formatCompactNumber(5600000); // \"5.6M\"\n * formatCompactNumber(1234, \"pt-BR\"); // \"1,2 mil\"\n */\nexport function formatCompactNumber(value: number, locale: string = \"en-US\"): string {\n return new Intl.NumberFormat(locale, { notation: \"compact\", maximumFractionDigits: 1 }).format(\n value,\n );\n}\n"],"mappings":"AAQA,SAAgB,EAAM,EAAe,EAAa,EAAqB,CACnE,GAAI,OAAO,MAAM,CAAK,EAAG,OAAO,EAChC,GAAI,EAAM,EAAK,CACX,IAAM,EAAO,EACb,EAAM,EACN,EAAM,CACV,CAGA,OAFI,EAAQ,EAAY,EACpB,EAAQ,EAAY,EACjB,CACX,CAeA,SAAgB,EAAY,EAAe,EAAmB,EAAW,CACrE,GAAI,IAAU,EAAG,MAAO,MAExB,IAAM,EAAQ,CAAC,IAAK,KAAM,KAAM,KAAM,IAAI,EACpC,EAAO,KACP,EAAW,KAAK,IAClB,KAAK,MAAM,KAAK,IAAI,KAAK,IAAI,CAAK,CAAC,EAAI,KAAK,IAAI,CAAI,CAAC,EACrD,EAAM,OAAS,CACnB,EACM,EAAQ,EAAQ,GAAQ,EACxB,EAAS,IAAa,EAAI,EAAI,KAAK,IAAI,EAAG,CAAQ,EAGxD,MAAO,GAFW,WAAW,EAAM,QAAQ,CAAM,CAAC,CAAC,CAAC,SAE1C,EAAU,GAAG,EAAM,IACjC,CAaA,SAAgB,EAAoB,EAAe,EAAiB,QAAiB,CACjF,OAAO,IAAI,KAAK,aAAa,EAAQ,CAAE,SAAU,UAAW,sBAAuB,CAAE,CAAC,CAAC,CAAC,OACpF,CACJ,CACJ"}
|
|
1
|
+
{"version":3,"file":"numbers.cjs","names":[],"sources":["../../src/utils/numbers.ts"],"sourcesContent":["/**\n * Clamp `value` between `min` and `max` (inclusive).\n *\n * @example\n * clamp(120, 0, 100); // 100\n * clamp(-5, 0, 100); // 0\n * clamp(42, 0, 100); // 42\n */\nexport function clamp(value: number, min: number, max: number): number {\n if (Number.isNaN(value)) return value;\n if (min > max) {\n const swap = min;\n min = max;\n max = swap;\n }\n if (value < min) return min;\n if (value > max) return max;\n return value;\n}\n\n/**\n * Format a byte count as a human-readable size string.\n *\n * Scales through B, KB, MB, GB and TB using 1024 as the base. `decimals`\n * controls the fractional digits for scaled units (default 1); `0` bytes\n * always renders as `\"0 B\"`.\n *\n * @example\n * formatBytes(0); // \"0 B\"\n * formatBytes(1536); // \"1.5 KB\"\n * formatBytes(1048576); // \"1 MB\"\n * formatBytes(1536, 2); // \"1.50 KB\"\n */\nexport function formatBytes(bytes: number, decimals: number = 1): string {\n if (bytes === 0) return \"0 B\";\n\n const units = [\"B\", \"KB\", \"MB\", \"GB\", \"TB\"];\n const base = 1024;\n const exponent = Math.min(\n Math.floor(Math.log(Math.abs(bytes)) / Math.log(base)),\n units.length - 1,\n );\n const value = bytes / base ** exponent;\n const digits = exponent === 0 ? 0 : Math.max(0, decimals);\n const formatted = parseFloat(value.toFixed(digits)).toString();\n\n return `${formatted} ${units[exponent]}`;\n}\n\n/**\n * What percentage of `total` is `part`, with an empty base yielding `0`.\n *\n * The guard is the whole point: `(part / total) * 100` produces `NaN` when the\n * base is zero and `Infinity` when only the base is missing, and both reach the\n * screen as a broken label — `NaN%` on an empty dashboard is the single most\n * common way a panel announces that it has no data yet.\n *\n * Returns a **0–100** number, which is what a bar width and a label want.\n * `formatPercent` takes a 0–1 fraction instead, so pair them as\n * `formatPercent(percentOf(a, b) / 100)` — or skip `percentOf` and pass the\n * fraction straight in.\n *\n * The result is not clamped: 120% of a target is a real number somebody wants to\n * see, and capping it would hide the interesting case.\n *\n * @example\n * percentOf(3, 4); // 75\n * percentOf(5, 0); // 0\n * percentOf(12, 8); // 150\n *\n * @param part - The measured amount.\n * @param total - The base it is measured against.\n * @returns The percentage, or `0` when the base is zero or either input is not finite.\n */\nexport function percentOf(part: number, total: number): number {\n if (!Number.isFinite(part) || !Number.isFinite(total) || total === 0) return 0;\n return (part / total) * 100;\n}\n\n/**\n * Format a number using compact notation (e.g. `1.2K`, `3.4M`).\n *\n * Wraps `Intl.NumberFormat` with `notation: \"compact\"`. The `locale` defaults\n * to `\"en-US\"`.\n *\n * @example\n * formatCompactNumber(1234); // \"1.2K\"\n * formatCompactNumber(5600000); // \"5.6M\"\n * formatCompactNumber(1234, \"pt-BR\"); // \"1,2 mil\"\n */\nexport function formatCompactNumber(value: number, locale: string = \"en-US\"): string {\n return new Intl.NumberFormat(locale, { notation: \"compact\", maximumFractionDigits: 1 }).format(\n value,\n );\n}\n"],"mappings":"AAQA,SAAgB,EAAM,EAAe,EAAa,EAAqB,CACnE,GAAI,OAAO,MAAM,CAAK,EAAG,OAAO,EAChC,GAAI,EAAM,EAAK,CACX,IAAM,EAAO,EACb,EAAM,EACN,EAAM,CACV,CAGA,OAFI,EAAQ,EAAY,EACpB,EAAQ,EAAY,EACjB,CACX,CAeA,SAAgB,EAAY,EAAe,EAAmB,EAAW,CACrE,GAAI,IAAU,EAAG,MAAO,MAExB,IAAM,EAAQ,CAAC,IAAK,KAAM,KAAM,KAAM,IAAI,EACpC,EAAO,KACP,EAAW,KAAK,IAClB,KAAK,MAAM,KAAK,IAAI,KAAK,IAAI,CAAK,CAAC,EAAI,KAAK,IAAI,CAAI,CAAC,EACrD,EAAM,OAAS,CACnB,EACM,EAAQ,EAAQ,GAAQ,EACxB,EAAS,IAAa,EAAI,EAAI,KAAK,IAAI,EAAG,CAAQ,EAGxD,MAAO,GAFW,WAAW,EAAM,QAAQ,CAAM,CAAC,CAAC,CAAC,SAE1C,EAAU,GAAG,EAAM,IACjC,CA2BA,SAAgB,EAAU,EAAc,EAAuB,CAE3D,MADI,CAAC,OAAO,SAAS,CAAI,GAAK,CAAC,OAAO,SAAS,CAAK,GAAK,IAAU,EAAU,EACrE,EAAO,EAAS,GAC5B,CAaA,SAAgB,EAAoB,EAAe,EAAiB,QAAiB,CACjF,OAAO,IAAI,KAAK,aAAa,EAAQ,CAAE,SAAU,UAAW,sBAAuB,CAAE,CAAC,CAAC,CAAC,OACpF,CACJ,CACJ"}
|
package/dist/utils/numbers.js
CHANGED
|
@@ -18,13 +18,16 @@ function t(e, t = 1) {
|
|
|
18
18
|
], r = 1024, i = Math.min(Math.floor(Math.log(Math.abs(e)) / Math.log(r)), n.length - 1), a = e / r ** i, o = i === 0 ? 0 : Math.max(0, t);
|
|
19
19
|
return `${parseFloat(a.toFixed(o)).toString()} ${n[i]}`;
|
|
20
20
|
}
|
|
21
|
-
function n(e, t
|
|
21
|
+
function n(e, t) {
|
|
22
|
+
return !Number.isFinite(e) || !Number.isFinite(t) || t === 0 ? 0 : e / t * 100;
|
|
23
|
+
}
|
|
24
|
+
function r(e, t = "en-US") {
|
|
22
25
|
return new Intl.NumberFormat(t, {
|
|
23
26
|
notation: "compact",
|
|
24
27
|
maximumFractionDigits: 1
|
|
25
28
|
}).format(e);
|
|
26
29
|
}
|
|
27
30
|
//#endregion
|
|
28
|
-
export { e as clamp, t as formatBytes,
|
|
31
|
+
export { e as clamp, t as formatBytes, r as formatCompactNumber, n as percentOf };
|
|
29
32
|
|
|
30
33
|
//# sourceMappingURL=numbers.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"numbers.js","names":[],"sources":["../../src/utils/numbers.ts"],"sourcesContent":["/**\n * Clamp `value` between `min` and `max` (inclusive).\n *\n * @example\n * clamp(120, 0, 100); // 100\n * clamp(-5, 0, 100); // 0\n * clamp(42, 0, 100); // 42\n */\nexport function clamp(value: number, min: number, max: number): number {\n if (Number.isNaN(value)) return value;\n if (min > max) {\n const swap = min;\n min = max;\n max = swap;\n }\n if (value < min) return min;\n if (value > max) return max;\n return value;\n}\n\n/**\n * Format a byte count as a human-readable size string.\n *\n * Scales through B, KB, MB, GB and TB using 1024 as the base. `decimals`\n * controls the fractional digits for scaled units (default 1); `0` bytes\n * always renders as `\"0 B\"`.\n *\n * @example\n * formatBytes(0); // \"0 B\"\n * formatBytes(1536); // \"1.5 KB\"\n * formatBytes(1048576); // \"1 MB\"\n * formatBytes(1536, 2); // \"1.50 KB\"\n */\nexport function formatBytes(bytes: number, decimals: number = 1): string {\n if (bytes === 0) return \"0 B\";\n\n const units = [\"B\", \"KB\", \"MB\", \"GB\", \"TB\"];\n const base = 1024;\n const exponent = Math.min(\n Math.floor(Math.log(Math.abs(bytes)) / Math.log(base)),\n units.length - 1,\n );\n const value = bytes / base ** exponent;\n const digits = exponent === 0 ? 0 : Math.max(0, decimals);\n const formatted = parseFloat(value.toFixed(digits)).toString();\n\n return `${formatted} ${units[exponent]}`;\n}\n\n/**\n * Format a number using compact notation (e.g. `1.2K`, `3.4M`).\n *\n * Wraps `Intl.NumberFormat` with `notation: \"compact\"`. The `locale` defaults\n * to `\"en-US\"`.\n *\n * @example\n * formatCompactNumber(1234); // \"1.2K\"\n * formatCompactNumber(5600000); // \"5.6M\"\n * formatCompactNumber(1234, \"pt-BR\"); // \"1,2 mil\"\n */\nexport function formatCompactNumber(value: number, locale: string = \"en-US\"): string {\n return new Intl.NumberFormat(locale, { notation: \"compact\", maximumFractionDigits: 1 }).format(\n value,\n );\n}\n"],"mappings":";AAQA,SAAgB,EAAM,GAAe,GAAa,GAAqB;CACnE,IAAI,OAAO,MAAM,CAAK,GAAG,OAAO;CAChC,IAAI,IAAM,GAAK;EACX,IAAM,IAAO;EAEb,AADA,IAAM,GACN,IAAM;CACV;CAGA,OAFI,IAAQ,IAAY,IACpB,IAAQ,IAAY,IACjB;AACX;AAeA,SAAgB,EAAY,GAAe,IAAmB,GAAW;CACrE,IAAI,MAAU,GAAG,OAAO;CAExB,IAAM,IAAQ;EAAC;EAAK;EAAM;EAAM;EAAM;CAAI,GACpC,IAAO,MACP,IAAW,KAAK,IAClB,KAAK,MAAM,KAAK,IAAI,KAAK,IAAI,CAAK,CAAC,IAAI,KAAK,IAAI,CAAI,CAAC,GACrD,EAAM,SAAS,CACnB,GACM,IAAQ,IAAQ,KAAQ,GACxB,IAAS,MAAa,IAAI,IAAI,KAAK,IAAI,GAAG,CAAQ;CAGxD,OAAO,GAFW,WAAW,EAAM,QAAQ,CAAM,CAAC,CAAC,CAAC,SAE1C,EAAU,GAAG,EAAM;AACjC;AAaA,SAAgB,EAAoB,GAAe,IAAiB,SAAiB;CACjF,OAAO,IAAI,KAAK,aAAa,GAAQ;EAAE,UAAU;EAAW,uBAAuB;CAAE,CAAC,CAAC,CAAC,OACpF,CACJ;AACJ"}
|
|
1
|
+
{"version":3,"file":"numbers.js","names":[],"sources":["../../src/utils/numbers.ts"],"sourcesContent":["/**\n * Clamp `value` between `min` and `max` (inclusive).\n *\n * @example\n * clamp(120, 0, 100); // 100\n * clamp(-5, 0, 100); // 0\n * clamp(42, 0, 100); // 42\n */\nexport function clamp(value: number, min: number, max: number): number {\n if (Number.isNaN(value)) return value;\n if (min > max) {\n const swap = min;\n min = max;\n max = swap;\n }\n if (value < min) return min;\n if (value > max) return max;\n return value;\n}\n\n/**\n * Format a byte count as a human-readable size string.\n *\n * Scales through B, KB, MB, GB and TB using 1024 as the base. `decimals`\n * controls the fractional digits for scaled units (default 1); `0` bytes\n * always renders as `\"0 B\"`.\n *\n * @example\n * formatBytes(0); // \"0 B\"\n * formatBytes(1536); // \"1.5 KB\"\n * formatBytes(1048576); // \"1 MB\"\n * formatBytes(1536, 2); // \"1.50 KB\"\n */\nexport function formatBytes(bytes: number, decimals: number = 1): string {\n if (bytes === 0) return \"0 B\";\n\n const units = [\"B\", \"KB\", \"MB\", \"GB\", \"TB\"];\n const base = 1024;\n const exponent = Math.min(\n Math.floor(Math.log(Math.abs(bytes)) / Math.log(base)),\n units.length - 1,\n );\n const value = bytes / base ** exponent;\n const digits = exponent === 0 ? 0 : Math.max(0, decimals);\n const formatted = parseFloat(value.toFixed(digits)).toString();\n\n return `${formatted} ${units[exponent]}`;\n}\n\n/**\n * What percentage of `total` is `part`, with an empty base yielding `0`.\n *\n * The guard is the whole point: `(part / total) * 100` produces `NaN` when the\n * base is zero and `Infinity` when only the base is missing, and both reach the\n * screen as a broken label — `NaN%` on an empty dashboard is the single most\n * common way a panel announces that it has no data yet.\n *\n * Returns a **0–100** number, which is what a bar width and a label want.\n * `formatPercent` takes a 0–1 fraction instead, so pair them as\n * `formatPercent(percentOf(a, b) / 100)` — or skip `percentOf` and pass the\n * fraction straight in.\n *\n * The result is not clamped: 120% of a target is a real number somebody wants to\n * see, and capping it would hide the interesting case.\n *\n * @example\n * percentOf(3, 4); // 75\n * percentOf(5, 0); // 0\n * percentOf(12, 8); // 150\n *\n * @param part - The measured amount.\n * @param total - The base it is measured against.\n * @returns The percentage, or `0` when the base is zero or either input is not finite.\n */\nexport function percentOf(part: number, total: number): number {\n if (!Number.isFinite(part) || !Number.isFinite(total) || total === 0) return 0;\n return (part / total) * 100;\n}\n\n/**\n * Format a number using compact notation (e.g. `1.2K`, `3.4M`).\n *\n * Wraps `Intl.NumberFormat` with `notation: \"compact\"`. The `locale` defaults\n * to `\"en-US\"`.\n *\n * @example\n * formatCompactNumber(1234); // \"1.2K\"\n * formatCompactNumber(5600000); // \"5.6M\"\n * formatCompactNumber(1234, \"pt-BR\"); // \"1,2 mil\"\n */\nexport function formatCompactNumber(value: number, locale: string = \"en-US\"): string {\n return new Intl.NumberFormat(locale, { notation: \"compact\", maximumFractionDigits: 1 }).format(\n value,\n );\n}\n"],"mappings":";AAQA,SAAgB,EAAM,GAAe,GAAa,GAAqB;CACnE,IAAI,OAAO,MAAM,CAAK,GAAG,OAAO;CAChC,IAAI,IAAM,GAAK;EACX,IAAM,IAAO;EAEb,AADA,IAAM,GACN,IAAM;CACV;CAGA,OAFI,IAAQ,IAAY,IACpB,IAAQ,IAAY,IACjB;AACX;AAeA,SAAgB,EAAY,GAAe,IAAmB,GAAW;CACrE,IAAI,MAAU,GAAG,OAAO;CAExB,IAAM,IAAQ;EAAC;EAAK;EAAM;EAAM;EAAM;CAAI,GACpC,IAAO,MACP,IAAW,KAAK,IAClB,KAAK,MAAM,KAAK,IAAI,KAAK,IAAI,CAAK,CAAC,IAAI,KAAK,IAAI,CAAI,CAAC,GACrD,EAAM,SAAS,CACnB,GACM,IAAQ,IAAQ,KAAQ,GACxB,IAAS,MAAa,IAAI,IAAI,KAAK,IAAI,GAAG,CAAQ;CAGxD,OAAO,GAFW,WAAW,EAAM,QAAQ,CAAM,CAAC,CAAC,CAAC,SAE1C,EAAU,GAAG,EAAM;AACjC;AA2BA,SAAgB,EAAU,GAAc,GAAuB;CAE3D,OADI,CAAC,OAAO,SAAS,CAAI,KAAK,CAAC,OAAO,SAAS,CAAK,KAAK,MAAU,IAAU,IACrE,IAAO,IAAS;AAC5B;AAaA,SAAgB,EAAoB,GAAe,IAAiB,SAAiB;CACjF,OAAO,IAAI,KAAK,aAAa,GAAQ;EAAE,UAAU;EAAW,uBAAuB;CAAE,CAAC,CAAC,CAAC,OACpF,CACJ;AACJ"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
let e=require("node:path");function t(t={}){let{swSrc:n=`src/sw.ts`,swUrl:r=`/sw.js`,manifestUrl:i=`/precache-manifest.json`,enabled:a=!0}=t,o=process.cwd();return{name:`tempest-pwa-dev-sw`,apply:`serve`,configResolved(e){o=e.root??process.cwd()},configureServer(t){a&&t.middlewares.use(async(t,a,s)=>{let
|
|
1
|
+
let e=require("node:path");function t(t={}){let{swSrc:n=`src/sw.ts`,swUrl:r=`/sw.js`,manifestUrl:i=`/precache-manifest.json`,enabled:a=!0}=t,o=process.cwd(),s=`/`;function c(e,t){if(e===t)return!0;let n=s.endsWith(`/`)?s:`${s}/`;return n!==`/`&&e===`${n}${t.replace(/^\//,``)}`}return{name:`tempest-pwa-dev-sw`,apply:`serve`,configResolved(e){o=e.root??process.cwd(),s=e.base??`/`},configureServer(t){a&&t.middlewares.use(async(t,a,s)=>{let l=(t.url??``).split(`?`)[0];if(c(l,r)){try{let t=await(await import(`esbuild`)).build({entryPoints:[(0,e.resolve)(o,n)],bundle:!0,format:`iife`,platform:`browser`,target:`es2020`,write:!1,absWorkingDir:o,logLevel:`silent`});a.setHeader(`Content-Type`,`application/javascript`),a.setHeader(`Service-Worker-Allowed`,`/`),a.setHeader(`Cache-Control`,`no-cache`),a.end(t.outputFiles[0].text)}catch(e){a.statusCode=500,a.end(`// SW dev build failed:\n// ${String(e)}`)}return}if(c(l,i)){a.setHeader(`Content-Type`,`application/json`),a.setHeader(`Cache-Control`,`no-cache`),a.end(JSON.stringify({version:`dev`,urls:[]}));return}s()})}}}exports.tempestPwaDevSw=t;
|
|
2
2
|
//# sourceMappingURL=tempest-pwa-dev-sw.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tempest-pwa-dev-sw.cjs","names":[],"sources":["../../src/vite/tempest-pwa-dev-sw.ts"],"sourcesContent":["import { resolve } from \"node:path\";\nimport type { Plugin } from \"vite\";\nimport type { TempestVitePlugin } from \"./tempest-pwa-manifest\";\n\n/** Options for {@link tempestPwaDevSw}. */\nexport interface TempestPwaDevSwOptions {\n /** Service-worker entry, relative to the project root. Default `src/sw.ts`. */\n swSrc?: string;\n
|
|
1
|
+
{"version":3,"file":"tempest-pwa-dev-sw.cjs","names":[],"sources":["../../src/vite/tempest-pwa-dev-sw.ts"],"sourcesContent":["import { resolve } from \"node:path\";\nimport type { Plugin } from \"vite\";\nimport type { TempestVitePlugin } from \"./tempest-pwa-manifest\";\n\n/** Options for {@link tempestPwaDevSw}. */\nexport interface TempestPwaDevSwOptions {\n /** Service-worker entry, relative to the project root. Default `src/sw.ts`. */\n swSrc?: string;\n /**\n * URL the worker is served at (must match `registerServiceWorker`).\n * Default `/sw.js`.\n *\n * Write it relative to the site root; requests are matched against both the\n * bare path and the path prefixed with the resolved Vite `base`, so a\n * project served from a subpath is handled without extra configuration.\n */\n swUrl?: string;\n /**\n * Dev URL of the precache manifest. Default `/precache-manifest.json`.\n * Matched the same way as {@link TempestPwaDevSwOptions.swUrl}.\n */\n manifestUrl?: string;\n /** Serve the worker in dev. Default `true`; set `false` to opt out. */\n enabled?: boolean;\n}\n\n/**\n * Dev-server plugin that makes the service worker available under `npm run dev`.\n *\n * The production worker is bundled at build time (`vite.sw.config.ts`), so in\n * dev there is no `/sw.js` to register. This plugin compiles `swSrc` on the fly\n * with esbuild and serves it as a classic worker, plus an empty\n * `precache-manifest.json` (there are no hashed build assets to precache in\n * dev — push and runtime caching still work). It closes the \"SW in dev\" gap\n * that otherwise only `vite-plugin-pwa`'s `devOptions` covered.\n *\n * @example\n * // vite.config.ts\n * import { createViteConfig, tempestPwaDevSw } from \"tempest-react-sdk/vite\";\n *\n * export default createViteConfig({ plugins: [tempestPwaDevSw()] });\n */\nexport function tempestPwaDevSw(options: TempestPwaDevSwOptions = {}): TempestVitePlugin {\n const {\n swSrc = \"src/sw.ts\",\n swUrl = \"/sw.js\",\n manifestUrl = \"/precache-manifest.json\",\n enabled = true,\n } = options;\n\n let root = process.cwd();\n let base = \"/\";\n\n /**\n * Whether a request path addresses `target`.\n *\n * Both the bare path and the base-prefixed one are accepted. Which of the\n * two arrives depends on where this middleware lands relative to Vite's own\n * base handling, and a project served from a subpath would otherwise never\n * match — the browser asks for `/app/sw.js` while the option says `/sw.js`.\n */\n function matches(url: string, target: string): boolean {\n if (url === target) return true;\n const prefix = base.endsWith(\"/\") ? base : `${base}/`;\n if (prefix === \"/\") return false;\n return url === `${prefix}${target.replace(/^\\//, \"\")}`;\n }\n\n const plugin: Plugin = {\n name: \"tempest-pwa-dev-sw\",\n apply: \"serve\",\n configResolved(config) {\n root = config.root ?? process.cwd();\n base = config.base ?? \"/\";\n },\n configureServer(server) {\n if (!enabled) return;\n server.middlewares.use(async (req, res, next) => {\n const url = (req.url ?? \"\").split(\"?\")[0];\n\n if (matches(url, swUrl)) {\n try {\n const esbuild = await import(\"esbuild\");\n const result = await esbuild.build({\n entryPoints: [resolve(root, swSrc)],\n bundle: true,\n format: \"iife\",\n platform: \"browser\",\n target: \"es2020\",\n write: false,\n absWorkingDir: root,\n logLevel: \"silent\",\n });\n res.setHeader(\"Content-Type\", \"application/javascript\");\n res.setHeader(\"Service-Worker-Allowed\", \"/\");\n res.setHeader(\"Cache-Control\", \"no-cache\");\n res.end(result.outputFiles[0].text);\n } catch (error) {\n res.statusCode = 500;\n res.end(`// SW dev build failed:\\n// ${String(error)}`);\n }\n return;\n }\n\n if (matches(url, manifestUrl)) {\n res.setHeader(\"Content-Type\", \"application/json\");\n res.setHeader(\"Cache-Control\", \"no-cache\");\n res.end(JSON.stringify({ version: \"dev\", urls: [] }));\n return;\n }\n\n next();\n });\n },\n };\n\n return plugin as TempestVitePlugin;\n}\n"],"mappings":"2BA0CA,SAAgB,EAAgB,EAAkC,CAAC,EAAsB,CACrF,GAAM,CACF,QAAQ,YACR,QAAQ,SACR,cAAc,0BACd,UAAU,IACV,EAEA,EAAO,QAAQ,IAAI,EACnB,EAAO,IAUX,SAAS,EAAQ,EAAa,EAAyB,CACnD,GAAI,IAAQ,EAAQ,MAAO,GAC3B,IAAM,EAAS,EAAK,SAAS,GAAG,EAAI,EAAO,GAAG,EAAK,GAEnD,OADI,IAAW,KACR,IAAQ,GAAG,IAAS,EAAO,QAAQ,MAAO,EAAE,GACvD,CAkDA,MAAO,CA/CH,KAAM,qBACN,MAAO,QACP,eAAe,EAAQ,CACnB,EAAO,EAAO,MAAQ,QAAQ,IAAI,EAClC,EAAO,EAAO,MAAQ,GAC1B,EACA,gBAAgB,EAAQ,CACf,GACL,EAAO,YAAY,IAAI,MAAO,EAAK,EAAK,IAAS,CAC7C,IAAM,GAAO,EAAI,KAAO,GAAA,CAAI,MAAM,GAAG,CAAC,CAAC,GAEvC,GAAI,EAAQ,EAAK,CAAK,EAAG,CACrB,GAAI,CAEA,IAAM,EAAS,MAAM,MADC,OAAO,WAAA,CACA,MAAM,CAC/B,YAAa,EAAA,EAAC,EAAA,QAAA,CAAQ,EAAM,CAAK,CAAC,EAClC,OAAQ,GACR,OAAQ,OACR,SAAU,UACV,OAAQ,SACR,MAAO,GACP,cAAe,EACf,SAAU,QACd,CAAC,EACD,EAAI,UAAU,eAAgB,wBAAwB,EACtD,EAAI,UAAU,yBAA0B,GAAG,EAC3C,EAAI,UAAU,gBAAiB,UAAU,EACzC,EAAI,IAAI,EAAO,YAAY,EAAE,CAAC,IAAI,CACtC,OAAS,EAAO,CACZ,EAAI,WAAa,IACjB,EAAI,IAAI,+BAA+B,OAAO,CAAK,GAAG,CAC1D,CACA,MACJ,CAEA,GAAI,EAAQ,EAAK,CAAW,EAAG,CAC3B,EAAI,UAAU,eAAgB,kBAAkB,EAChD,EAAI,UAAU,gBAAiB,UAAU,EACzC,EAAI,IAAI,KAAK,UAAU,CAAE,QAAS,MAAO,KAAM,CAAC,CAAE,CAAC,CAAC,EACpD,MACJ,CAEA,EAAK,CACT,CAAC,CACL,CAGG,CACX"}
|
|
@@ -1,17 +1,22 @@
|
|
|
1
1
|
import { resolve as e } from "node:path";
|
|
2
2
|
//#region src/vite/tempest-pwa-dev-sw.ts
|
|
3
3
|
function t(t = {}) {
|
|
4
|
-
let { swSrc: n = "src/sw.ts", swUrl: r = "/sw.js", manifestUrl: i = "/precache-manifest.json", enabled: a = !0 } = t, o = process.cwd();
|
|
4
|
+
let { swSrc: n = "src/sw.ts", swUrl: r = "/sw.js", manifestUrl: i = "/precache-manifest.json", enabled: a = !0 } = t, o = process.cwd(), s = "/";
|
|
5
|
+
function c(e, t) {
|
|
6
|
+
if (e === t) return !0;
|
|
7
|
+
let n = s.endsWith("/") ? s : `${s}/`;
|
|
8
|
+
return n !== "/" && e === `${n}${t.replace(/^\//, "")}`;
|
|
9
|
+
}
|
|
5
10
|
return {
|
|
6
11
|
name: "tempest-pwa-dev-sw",
|
|
7
12
|
apply: "serve",
|
|
8
13
|
configResolved(e) {
|
|
9
|
-
o = e.root ?? process.cwd();
|
|
14
|
+
o = e.root ?? process.cwd(), s = e.base ?? "/";
|
|
10
15
|
},
|
|
11
16
|
configureServer(t) {
|
|
12
17
|
a && t.middlewares.use(async (t, a, s) => {
|
|
13
|
-
let
|
|
14
|
-
if (c
|
|
18
|
+
let l = (t.url ?? "").split("?")[0];
|
|
19
|
+
if (c(l, r)) {
|
|
15
20
|
try {
|
|
16
21
|
let t = await (await import("esbuild")).build({
|
|
17
22
|
entryPoints: [e(o, n)],
|
|
@@ -29,7 +34,7 @@ function t(t = {}) {
|
|
|
29
34
|
}
|
|
30
35
|
return;
|
|
31
36
|
}
|
|
32
|
-
if (c
|
|
37
|
+
if (c(l, i)) {
|
|
33
38
|
a.setHeader("Content-Type", "application/json"), a.setHeader("Cache-Control", "no-cache"), a.end(JSON.stringify({
|
|
34
39
|
version: "dev",
|
|
35
40
|
urls: []
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tempest-pwa-dev-sw.js","names":[],"sources":["../../src/vite/tempest-pwa-dev-sw.ts"],"sourcesContent":["import { resolve } from \"node:path\";\nimport type { Plugin } from \"vite\";\nimport type { TempestVitePlugin } from \"./tempest-pwa-manifest\";\n\n/** Options for {@link tempestPwaDevSw}. */\nexport interface TempestPwaDevSwOptions {\n /** Service-worker entry, relative to the project root. Default `src/sw.ts`. */\n swSrc?: string;\n
|
|
1
|
+
{"version":3,"file":"tempest-pwa-dev-sw.js","names":[],"sources":["../../src/vite/tempest-pwa-dev-sw.ts"],"sourcesContent":["import { resolve } from \"node:path\";\nimport type { Plugin } from \"vite\";\nimport type { TempestVitePlugin } from \"./tempest-pwa-manifest\";\n\n/** Options for {@link tempestPwaDevSw}. */\nexport interface TempestPwaDevSwOptions {\n /** Service-worker entry, relative to the project root. Default `src/sw.ts`. */\n swSrc?: string;\n /**\n * URL the worker is served at (must match `registerServiceWorker`).\n * Default `/sw.js`.\n *\n * Write it relative to the site root; requests are matched against both the\n * bare path and the path prefixed with the resolved Vite `base`, so a\n * project served from a subpath is handled without extra configuration.\n */\n swUrl?: string;\n /**\n * Dev URL of the precache manifest. Default `/precache-manifest.json`.\n * Matched the same way as {@link TempestPwaDevSwOptions.swUrl}.\n */\n manifestUrl?: string;\n /** Serve the worker in dev. Default `true`; set `false` to opt out. */\n enabled?: boolean;\n}\n\n/**\n * Dev-server plugin that makes the service worker available under `npm run dev`.\n *\n * The production worker is bundled at build time (`vite.sw.config.ts`), so in\n * dev there is no `/sw.js` to register. This plugin compiles `swSrc` on the fly\n * with esbuild and serves it as a classic worker, plus an empty\n * `precache-manifest.json` (there are no hashed build assets to precache in\n * dev — push and runtime caching still work). It closes the \"SW in dev\" gap\n * that otherwise only `vite-plugin-pwa`'s `devOptions` covered.\n *\n * @example\n * // vite.config.ts\n * import { createViteConfig, tempestPwaDevSw } from \"tempest-react-sdk/vite\";\n *\n * export default createViteConfig({ plugins: [tempestPwaDevSw()] });\n */\nexport function tempestPwaDevSw(options: TempestPwaDevSwOptions = {}): TempestVitePlugin {\n const {\n swSrc = \"src/sw.ts\",\n swUrl = \"/sw.js\",\n manifestUrl = \"/precache-manifest.json\",\n enabled = true,\n } = options;\n\n let root = process.cwd();\n let base = \"/\";\n\n /**\n * Whether a request path addresses `target`.\n *\n * Both the bare path and the base-prefixed one are accepted. Which of the\n * two arrives depends on where this middleware lands relative to Vite's own\n * base handling, and a project served from a subpath would otherwise never\n * match — the browser asks for `/app/sw.js` while the option says `/sw.js`.\n */\n function matches(url: string, target: string): boolean {\n if (url === target) return true;\n const prefix = base.endsWith(\"/\") ? base : `${base}/`;\n if (prefix === \"/\") return false;\n return url === `${prefix}${target.replace(/^\\//, \"\")}`;\n }\n\n const plugin: Plugin = {\n name: \"tempest-pwa-dev-sw\",\n apply: \"serve\",\n configResolved(config) {\n root = config.root ?? process.cwd();\n base = config.base ?? \"/\";\n },\n configureServer(server) {\n if (!enabled) return;\n server.middlewares.use(async (req, res, next) => {\n const url = (req.url ?? \"\").split(\"?\")[0];\n\n if (matches(url, swUrl)) {\n try {\n const esbuild = await import(\"esbuild\");\n const result = await esbuild.build({\n entryPoints: [resolve(root, swSrc)],\n bundle: true,\n format: \"iife\",\n platform: \"browser\",\n target: \"es2020\",\n write: false,\n absWorkingDir: root,\n logLevel: \"silent\",\n });\n res.setHeader(\"Content-Type\", \"application/javascript\");\n res.setHeader(\"Service-Worker-Allowed\", \"/\");\n res.setHeader(\"Cache-Control\", \"no-cache\");\n res.end(result.outputFiles[0].text);\n } catch (error) {\n res.statusCode = 500;\n res.end(`// SW dev build failed:\\n// ${String(error)}`);\n }\n return;\n }\n\n if (matches(url, manifestUrl)) {\n res.setHeader(\"Content-Type\", \"application/json\");\n res.setHeader(\"Cache-Control\", \"no-cache\");\n res.end(JSON.stringify({ version: \"dev\", urls: [] }));\n return;\n }\n\n next();\n });\n },\n };\n\n return plugin as TempestVitePlugin;\n}\n"],"mappings":";;AA0CA,SAAgB,EAAgB,IAAkC,CAAC,GAAsB;CACrF,IAAM,EACF,WAAQ,aACR,WAAQ,UACR,iBAAc,2BACd,aAAU,OACV,GAEA,IAAO,QAAQ,IAAI,GACnB,IAAO;CAUX,SAAS,EAAQ,GAAa,GAAyB;EACnD,IAAI,MAAQ,GAAQ,OAAO;EAC3B,IAAM,IAAS,EAAK,SAAS,GAAG,IAAI,IAAO,GAAG,EAAK;EAEnD,OADI,MAAW,OACR,MAAQ,GAAG,IAAS,EAAO,QAAQ,OAAO,EAAE;CACvD;CAkDA,OAAO;EA/CH,MAAM;EACN,OAAO;EACP,eAAe,GAAQ;GAEnB,AADA,IAAO,EAAO,QAAQ,QAAQ,IAAI,GAClC,IAAO,EAAO,QAAQ;EAC1B;EACA,gBAAgB,GAAQ;GACf,KACL,EAAO,YAAY,IAAI,OAAO,GAAK,GAAK,MAAS;IAC7C,IAAM,KAAO,EAAI,OAAO,GAAA,CAAI,MAAM,GAAG,CAAC,CAAC;IAEvC,IAAI,EAAQ,GAAK,CAAK,GAAG;KACrB,IAAI;MAEA,IAAM,IAAS,OAAM,MADC,OAAO,WAAA,CACA,MAAM;OAC/B,aAAa,CAAC,EAAQ,GAAM,CAAK,CAAC;OAClC,QAAQ;OACR,QAAQ;OACR,UAAU;OACV,QAAQ;OACR,OAAO;OACP,eAAe;OACf,UAAU;MACd,CAAC;MAID,AAHA,EAAI,UAAU,gBAAgB,wBAAwB,GACtD,EAAI,UAAU,0BAA0B,GAAG,GAC3C,EAAI,UAAU,iBAAiB,UAAU,GACzC,EAAI,IAAI,EAAO,YAAY,EAAE,CAAC,IAAI;KACtC,SAAS,GAAO;MAEZ,AADA,EAAI,aAAa,KACjB,EAAI,IAAI,+BAA+B,OAAO,CAAK,GAAG;KAC1D;KACA;IACJ;IAEA,IAAI,EAAQ,GAAK,CAAW,GAAG;KAG3B,AAFA,EAAI,UAAU,gBAAgB,kBAAkB,GAChD,EAAI,UAAU,iBAAiB,UAAU,GACzC,EAAI,IAAI,KAAK,UAAU;MAAE,SAAS;MAAO,MAAM,CAAC;KAAE,CAAC,CAAC;KACpD;IACJ;IAEA,EAAK;GACT,CAAC;EACL;CAGG;AACX"}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
function e(e,t){return`${e.endsWith(`/`)?e:`${e}/`}${t}`.replace(/([^:]\/)\/+/g,`$1`)}function t(e){let t=5381;for(let n=0;n<e.length;n++)t=(t<<5)+t+e.charCodeAt(n)>>>0;return t.toString(16)}function
|
|
2
|
-
`));this.emitFile({type:`asset`,fileName:
|
|
1
|
+
function e(e,t){return`${e.endsWith(`/`)?e:`${e}/`}${t}`.replace(/([^:]\/)\/+/g,`$1`)}function t(e,t){let n=e.endsWith(`/`)?e:`${e}/`;return n===`/`?t.startsWith(`/`)?t:`/${t}`:t.startsWith(n)?t:`${n}${t.replace(/^\//,``)}`}function n(e){let t=5381;for(let n=0;n<e.length;n++)t=(t<<5)+t+e.charCodeAt(n)>>>0;return t.toString(16)}function r(r={}){let{fileName:i=`precache-manifest.json`,additionalUrls:a=[],exclude:o=/\.map$/,includeHtml:s=!0,appShell:c=`/index.html`}=r,l=`/`;return{name:`tempest-pwa-manifest`,apply:`build`,configResolved(e){l=e.base??`/`},generateBundle(r,u){let d=new Set(a.map(e=>t(l,e)));c&&d.add(t(l,c));for(let t of Object.keys(u))t!==i&&(o.test(t)||!s&&t.endsWith(`.html`)||d.add(e(l,t)));let f=[...d].sort(),p=n(f.join(`
|
|
2
|
+
`));this.emitFile({type:`asset`,fileName:i,source:JSON.stringify({version:p,urls:f})})}}}exports.tempestPwaManifest=r;
|
|
3
3
|
//# sourceMappingURL=tempest-pwa-manifest.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tempest-pwa-manifest.cjs","names":[],"sources":["../../src/vite/tempest-pwa-manifest.ts"],"sourcesContent":["import type { Plugin } from \"vite\";\n\n/**\n * A Vite plugin object. Typed loosely so the SDK's published declarations stay\n * free of `vite`'s internal types (which the `.d.ts` rollup can't analyze);\n * assign the result straight into a `plugins: [...]` array.\n */\nexport type TempestVitePlugin = { name: string } & Record<string, unknown>;\n\n/** Options for {@link tempestPwaManifest}. */\nexport interface TempestPwaManifestOptions {\n /** Output file name (under the build root). Default `precache-manifest.json`. */\n fileName?: string;\n /**\n * Extra URLs to precache that Vite doesn't emit into the bundle — typically\n * `public/` assets like the web manifest and icons. Default `[]`.\n */\n additionalUrls?: string[];\n /** Emitted files matching this are skipped. Default `/\\.map$/` (source maps). */\n exclude?: RegExp;\n /** Include emitted `.html` documents (the app shell). Default `true`. */\n includeHtml?: boolean;\n /**\n * App-shell document always added to the manifest, even if Vite emits it\n * after this plugin runs. Must match `installPrecache`'s `navigateFallback`\n * so offline navigations resolve. Pass `false` to disable. Default `/index.html`.\n */\n appShell?: string | false;\n}\n\nfunction joinBase(base: string, file: string): string {\n const prefix = base.endsWith(\"/\") ? base : `${base}/`;\n return `${prefix}${file}`.replace(/([^:]\\/)\\/+/g, \"$1\");\n}\n\n/** Deterministic djb2 hash → hex. Stable across rebuilds with the same assets. */\nfunction hash(input: string): string {\n let h = 5381;\n for (let i = 0; i < input.length; i++) {\n h = ((h << 5) + h + input.charCodeAt(i)) >>> 0;\n }\n return h.toString(16);\n}\n\n/**\n * Vite build plugin that emits a `precache-manifest.json` listing every built\n * asset (plus any `additionalUrls`) as root-absolute URLs, with a content-based\n * `version`. It is the dependency-free counterpart to Workbox's `__WB_MANIFEST`:\n * `installPrecache` (from `tempest-react-sdk/sw`) reads this file at the service\n * worker's `install` event to cache the app shell for offline use.\n *\n * @example\n * // vite.config.ts\n * import { createViteConfig, tempestPwaManifest } from \"tempest-react-sdk/vite\";\n *\n * export default createViteConfig({\n * plugins: [tempestPwaManifest({ additionalUrls: [\"/manifest.webmanifest\", \"/icon.svg\"] })],\n * });\n */\nexport function tempestPwaManifest(options: TempestPwaManifestOptions = {}): TempestVitePlugin {\n const {\n fileName = \"precache-manifest.json\",\n additionalUrls = [],\n exclude = /\\.map$/,\n includeHtml = true,\n appShell = \"/index.html\",\n } = options;\n\n let base = \"/\";\n\n const plugin: Plugin = {\n name: \"tempest-pwa-manifest\",\n apply: \"build\",\n configResolved(config) {\n base = config.base ?? \"/\";\n },\n generateBundle(_outputOptions, bundle) {\n const urls = new Set<string>(additionalUrls);\n // Vite may emit index.html after this hook, so guarantee the shell.\n if (appShell) urls.add(appShell);\n for (const file of Object.keys(bundle)) {\n if (file === fileName) continue;\n if (exclude.test(file)) continue;\n if (!includeHtml && file.endsWith(\".html\")) continue;\n urls.add(joinBase(base, file));\n }\n\n const list = [...urls].sort();\n const version = hash(list.join(\"\\n\"));\n this.emitFile({\n type: \"asset\",\n fileName,\n source: JSON.stringify({ version, urls: list }),\n });\n },\n };\n\n return plugin as TempestVitePlugin;\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"tempest-pwa-manifest.cjs","names":[],"sources":["../../src/vite/tempest-pwa-manifest.ts"],"sourcesContent":["import type { Plugin } from \"vite\";\n\n/**\n * A Vite plugin object. Typed loosely so the SDK's published declarations stay\n * free of `vite`'s internal types (which the `.d.ts` rollup can't analyze);\n * assign the result straight into a `plugins: [...]` array.\n */\nexport type TempestVitePlugin = { name: string } & Record<string, unknown>;\n\n/** Options for {@link tempestPwaManifest}. */\nexport interface TempestPwaManifestOptions {\n /** Output file name (under the build root). Default `precache-manifest.json`. */\n fileName?: string;\n /**\n * Extra URLs to precache that Vite doesn't emit into the bundle — typically\n * `public/` assets like the web manifest and icons. Default `[]`.\n *\n * Write these relative to the site root (`/icon.svg`); the resolved Vite\n * `base` is prefixed for you. An entry that already carries the base is\n * left alone, so spelling it out by hand is still correct.\n */\n additionalUrls?: string[];\n /** Emitted files matching this are skipped. Default `/\\.map$/` (source maps). */\n exclude?: RegExp;\n /** Include emitted `.html` documents (the app shell). Default `true`. */\n includeHtml?: boolean;\n /**\n * App-shell document always added to the manifest, even if Vite emits it\n * after this plugin runs. Must match `installPrecache`'s `navigateFallback`\n * so offline navigations resolve. Pass `false` to disable. Default `/index.html`.\n *\n * Like {@link TempestPwaManifestOptions.additionalUrls}, this is prefixed\n * with the resolved Vite `base`.\n */\n appShell?: string | false;\n}\n\nfunction joinBase(base: string, file: string): string {\n const prefix = base.endsWith(\"/\") ? base : `${base}/`;\n return `${prefix}${file}`.replace(/([^:]\\/)\\/+/g, \"$1\");\n}\n\n/**\n * Prefix a caller-supplied, root-absolute URL with the resolved Vite `base`.\n *\n * Idempotent: a URL that already starts with the base is returned untouched,\n * so configs written before this was applied — spelling the base out by hand —\n * keep producing the same manifest instead of gaining a doubled prefix.\n */\nfunction withBase(base: string, url: string): string {\n const prefix = base.endsWith(\"/\") ? base : `${base}/`;\n if (prefix === \"/\") return url.startsWith(\"/\") ? url : `/${url}`;\n if (url.startsWith(prefix)) return url;\n return `${prefix}${url.replace(/^\\//, \"\")}`;\n}\n\n/** Deterministic djb2 hash → hex. Stable across rebuilds with the same assets. */\nfunction hash(input: string): string {\n let h = 5381;\n for (let i = 0; i < input.length; i++) {\n h = ((h << 5) + h + input.charCodeAt(i)) >>> 0;\n }\n return h.toString(16);\n}\n\n/**\n * Vite build plugin that emits a `precache-manifest.json` listing every built\n * asset (plus any `additionalUrls`) as root-absolute URLs, with a content-based\n * `version`. It is the dependency-free counterpart to Workbox's `__WB_MANIFEST`:\n * `installPrecache` (from `tempest-react-sdk/sw`) reads this file at the service\n * worker's `install` event to cache the app shell for offline use.\n *\n * @example\n * // vite.config.ts\n * import { createViteConfig, tempestPwaManifest } from \"tempest-react-sdk/vite\";\n *\n * export default createViteConfig({\n * plugins: [tempestPwaManifest({ additionalUrls: [\"/manifest.webmanifest\", \"/icon.svg\"] })],\n * });\n */\nexport function tempestPwaManifest(options: TempestPwaManifestOptions = {}): TempestVitePlugin {\n const {\n fileName = \"precache-manifest.json\",\n additionalUrls = [],\n exclude = /\\.map$/,\n includeHtml = true,\n appShell = \"/index.html\",\n } = options;\n\n let base = \"/\";\n\n const plugin: Plugin = {\n name: \"tempest-pwa-manifest\",\n apply: \"build\",\n configResolved(config) {\n base = config.base ?? \"/\";\n },\n generateBundle(_outputOptions, bundle) {\n const urls = new Set<string>(additionalUrls.map((url) => withBase(base, url)));\n // Vite may emit index.html after this hook, so guarantee the shell.\n if (appShell) urls.add(withBase(base, appShell));\n for (const file of Object.keys(bundle)) {\n if (file === fileName) continue;\n if (exclude.test(file)) continue;\n if (!includeHtml && file.endsWith(\".html\")) continue;\n urls.add(joinBase(base, file));\n }\n\n const list = [...urls].sort();\n const version = hash(list.join(\"\\n\"));\n this.emitFile({\n type: \"asset\",\n fileName,\n source: JSON.stringify({ version, urls: list }),\n });\n },\n };\n\n return plugin as TempestVitePlugin;\n}\n"],"mappings":"AAqCA,SAAS,EAAS,EAAc,EAAsB,CAElD,MAAO,GADQ,EAAK,SAAS,GAAG,EAAI,EAAO,GAAG,EAAK,KAChC,IAAO,QAAQ,eAAgB,IAAI,CAC1D,CASA,SAAS,EAAS,EAAc,EAAqB,CACjD,IAAM,EAAS,EAAK,SAAS,GAAG,EAAI,EAAO,GAAG,EAAK,GAGnD,OAFI,IAAW,IAAY,EAAI,WAAW,GAAG,EAAI,EAAM,IAAI,IACvD,EAAI,WAAW,CAAM,EAAU,EAC5B,GAAG,IAAS,EAAI,QAAQ,MAAO,EAAE,GAC5C,CAGA,SAAS,EAAK,EAAuB,CACjC,IAAI,EAAI,KACR,IAAK,IAAI,EAAI,EAAG,EAAI,EAAM,OAAQ,IAC9B,GAAM,GAAK,GAAK,EAAI,EAAM,WAAW,CAAC,IAAO,EAEjD,OAAO,EAAE,SAAS,EAAE,CACxB,CAiBA,SAAgB,EAAmB,EAAqC,CAAC,EAAsB,CAC3F,GAAM,CACF,WAAW,yBACX,iBAAiB,CAAC,EAClB,UAAU,SACV,cAAc,GACd,WAAW,eACX,EAEA,EAAO,IA6BX,MAAO,CA1BH,KAAM,uBACN,MAAO,QACP,eAAe,EAAQ,CACnB,EAAO,EAAO,MAAQ,GAC1B,EACA,eAAe,EAAgB,EAAQ,CACnC,IAAM,EAAO,IAAI,IAAY,EAAe,IAAK,GAAQ,EAAS,EAAM,CAAG,CAAC,CAAC,EAEzE,GAAU,EAAK,IAAI,EAAS,EAAM,CAAQ,CAAC,EAC/C,IAAK,IAAM,KAAQ,OAAO,KAAK,CAAM,EAC7B,IAAS,IACT,EAAQ,KAAK,CAAI,GACjB,CAAC,GAAe,EAAK,SAAS,OAAO,GACzC,EAAK,IAAI,EAAS,EAAM,CAAI,CAAC,GAGjC,IAAM,EAAO,CAAC,GAAG,CAAI,CAAC,CAAC,KAAK,EACtB,EAAU,EAAK,EAAK,KAAK;CAAI,CAAC,EACpC,KAAK,SAAS,CACV,KAAM,QACN,WACA,OAAQ,KAAK,UAAU,CAAE,UAAS,KAAM,CAAK,CAAC,CAClD,CAAC,CACL,CAGG,CACX"}
|
|
@@ -2,36 +2,40 @@
|
|
|
2
2
|
function e(e, t) {
|
|
3
3
|
return `${e.endsWith("/") ? e : `${e}/`}${t}`.replace(/([^:]\/)\/+/g, "$1");
|
|
4
4
|
}
|
|
5
|
-
function t(e) {
|
|
5
|
+
function t(e, t) {
|
|
6
|
+
let n = e.endsWith("/") ? e : `${e}/`;
|
|
7
|
+
return n === "/" ? t.startsWith("/") ? t : `/${t}` : t.startsWith(n) ? t : `${n}${t.replace(/^\//, "")}`;
|
|
8
|
+
}
|
|
9
|
+
function n(e) {
|
|
6
10
|
let t = 5381;
|
|
7
11
|
for (let n = 0; n < e.length; n++) t = (t << 5) + t + e.charCodeAt(n) >>> 0;
|
|
8
12
|
return t.toString(16);
|
|
9
13
|
}
|
|
10
|
-
function
|
|
11
|
-
let { fileName:
|
|
14
|
+
function r(r = {}) {
|
|
15
|
+
let { fileName: i = "precache-manifest.json", additionalUrls: a = [], exclude: o = /\.map$/, includeHtml: s = !0, appShell: c = "/index.html" } = r, l = "/";
|
|
12
16
|
return {
|
|
13
17
|
name: "tempest-pwa-manifest",
|
|
14
18
|
apply: "build",
|
|
15
19
|
configResolved(e) {
|
|
16
|
-
|
|
20
|
+
l = e.base ?? "/";
|
|
17
21
|
},
|
|
18
|
-
generateBundle(
|
|
19
|
-
let
|
|
20
|
-
|
|
21
|
-
for (let t of Object.keys(
|
|
22
|
-
let
|
|
22
|
+
generateBundle(r, u) {
|
|
23
|
+
let d = new Set(a.map((e) => t(l, e)));
|
|
24
|
+
c && d.add(t(l, c));
|
|
25
|
+
for (let t of Object.keys(u)) t !== i && (o.test(t) || !s && t.endsWith(".html") || d.add(e(l, t)));
|
|
26
|
+
let f = [...d].sort(), p = n(f.join("\n"));
|
|
23
27
|
this.emitFile({
|
|
24
28
|
type: "asset",
|
|
25
|
-
fileName:
|
|
29
|
+
fileName: i,
|
|
26
30
|
source: JSON.stringify({
|
|
27
|
-
version:
|
|
28
|
-
urls:
|
|
31
|
+
version: p,
|
|
32
|
+
urls: f
|
|
29
33
|
})
|
|
30
34
|
});
|
|
31
35
|
}
|
|
32
36
|
};
|
|
33
37
|
}
|
|
34
38
|
//#endregion
|
|
35
|
-
export {
|
|
39
|
+
export { r as tempestPwaManifest };
|
|
36
40
|
|
|
37
41
|
//# sourceMappingURL=tempest-pwa-manifest.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tempest-pwa-manifest.js","names":[],"sources":["../../src/vite/tempest-pwa-manifest.ts"],"sourcesContent":["import type { Plugin } from \"vite\";\n\n/**\n * A Vite plugin object. Typed loosely so the SDK's published declarations stay\n * free of `vite`'s internal types (which the `.d.ts` rollup can't analyze);\n * assign the result straight into a `plugins: [...]` array.\n */\nexport type TempestVitePlugin = { name: string } & Record<string, unknown>;\n\n/** Options for {@link tempestPwaManifest}. */\nexport interface TempestPwaManifestOptions {\n /** Output file name (under the build root). Default `precache-manifest.json`. */\n fileName?: string;\n /**\n * Extra URLs to precache that Vite doesn't emit into the bundle — typically\n * `public/` assets like the web manifest and icons. Default `[]`.\n */\n additionalUrls?: string[];\n /** Emitted files matching this are skipped. Default `/\\.map$/` (source maps). */\n exclude?: RegExp;\n /** Include emitted `.html` documents (the app shell). Default `true`. */\n includeHtml?: boolean;\n /**\n * App-shell document always added to the manifest, even if Vite emits it\n * after this plugin runs. Must match `installPrecache`'s `navigateFallback`\n * so offline navigations resolve. Pass `false` to disable. Default `/index.html`.\n */\n appShell?: string | false;\n}\n\nfunction joinBase(base: string, file: string): string {\n const prefix = base.endsWith(\"/\") ? base : `${base}/`;\n return `${prefix}${file}`.replace(/([^:]\\/)\\/+/g, \"$1\");\n}\n\n/** Deterministic djb2 hash → hex. Stable across rebuilds with the same assets. */\nfunction hash(input: string): string {\n let h = 5381;\n for (let i = 0; i < input.length; i++) {\n h = ((h << 5) + h + input.charCodeAt(i)) >>> 0;\n }\n return h.toString(16);\n}\n\n/**\n * Vite build plugin that emits a `precache-manifest.json` listing every built\n * asset (plus any `additionalUrls`) as root-absolute URLs, with a content-based\n * `version`. It is the dependency-free counterpart to Workbox's `__WB_MANIFEST`:\n * `installPrecache` (from `tempest-react-sdk/sw`) reads this file at the service\n * worker's `install` event to cache the app shell for offline use.\n *\n * @example\n * // vite.config.ts\n * import { createViteConfig, tempestPwaManifest } from \"tempest-react-sdk/vite\";\n *\n * export default createViteConfig({\n * plugins: [tempestPwaManifest({ additionalUrls: [\"/manifest.webmanifest\", \"/icon.svg\"] })],\n * });\n */\nexport function tempestPwaManifest(options: TempestPwaManifestOptions = {}): TempestVitePlugin {\n const {\n fileName = \"precache-manifest.json\",\n additionalUrls = [],\n exclude = /\\.map$/,\n includeHtml = true,\n appShell = \"/index.html\",\n } = options;\n\n let base = \"/\";\n\n const plugin: Plugin = {\n name: \"tempest-pwa-manifest\",\n apply: \"build\",\n configResolved(config) {\n base = config.base ?? \"/\";\n },\n generateBundle(_outputOptions, bundle) {\n const urls = new Set<string>(additionalUrls);\n // Vite may emit index.html after this hook, so guarantee the shell.\n if (appShell) urls.add(appShell);\n for (const file of Object.keys(bundle)) {\n if (file === fileName) continue;\n if (exclude.test(file)) continue;\n if (!includeHtml && file.endsWith(\".html\")) continue;\n urls.add(joinBase(base, file));\n }\n\n const list = [...urls].sort();\n const version = hash(list.join(\"\\n\"));\n this.emitFile({\n type: \"asset\",\n fileName,\n source: JSON.stringify({ version, urls: list }),\n });\n },\n };\n\n return plugin as TempestVitePlugin;\n}\n"],"mappings":";
|
|
1
|
+
{"version":3,"file":"tempest-pwa-manifest.js","names":[],"sources":["../../src/vite/tempest-pwa-manifest.ts"],"sourcesContent":["import type { Plugin } from \"vite\";\n\n/**\n * A Vite plugin object. Typed loosely so the SDK's published declarations stay\n * free of `vite`'s internal types (which the `.d.ts` rollup can't analyze);\n * assign the result straight into a `plugins: [...]` array.\n */\nexport type TempestVitePlugin = { name: string } & Record<string, unknown>;\n\n/** Options for {@link tempestPwaManifest}. */\nexport interface TempestPwaManifestOptions {\n /** Output file name (under the build root). Default `precache-manifest.json`. */\n fileName?: string;\n /**\n * Extra URLs to precache that Vite doesn't emit into the bundle — typically\n * `public/` assets like the web manifest and icons. Default `[]`.\n *\n * Write these relative to the site root (`/icon.svg`); the resolved Vite\n * `base` is prefixed for you. An entry that already carries the base is\n * left alone, so spelling it out by hand is still correct.\n */\n additionalUrls?: string[];\n /** Emitted files matching this are skipped. Default `/\\.map$/` (source maps). */\n exclude?: RegExp;\n /** Include emitted `.html` documents (the app shell). Default `true`. */\n includeHtml?: boolean;\n /**\n * App-shell document always added to the manifest, even if Vite emits it\n * after this plugin runs. Must match `installPrecache`'s `navigateFallback`\n * so offline navigations resolve. Pass `false` to disable. Default `/index.html`.\n *\n * Like {@link TempestPwaManifestOptions.additionalUrls}, this is prefixed\n * with the resolved Vite `base`.\n */\n appShell?: string | false;\n}\n\nfunction joinBase(base: string, file: string): string {\n const prefix = base.endsWith(\"/\") ? base : `${base}/`;\n return `${prefix}${file}`.replace(/([^:]\\/)\\/+/g, \"$1\");\n}\n\n/**\n * Prefix a caller-supplied, root-absolute URL with the resolved Vite `base`.\n *\n * Idempotent: a URL that already starts with the base is returned untouched,\n * so configs written before this was applied — spelling the base out by hand —\n * keep producing the same manifest instead of gaining a doubled prefix.\n */\nfunction withBase(base: string, url: string): string {\n const prefix = base.endsWith(\"/\") ? base : `${base}/`;\n if (prefix === \"/\") return url.startsWith(\"/\") ? url : `/${url}`;\n if (url.startsWith(prefix)) return url;\n return `${prefix}${url.replace(/^\\//, \"\")}`;\n}\n\n/** Deterministic djb2 hash → hex. Stable across rebuilds with the same assets. */\nfunction hash(input: string): string {\n let h = 5381;\n for (let i = 0; i < input.length; i++) {\n h = ((h << 5) + h + input.charCodeAt(i)) >>> 0;\n }\n return h.toString(16);\n}\n\n/**\n * Vite build plugin that emits a `precache-manifest.json` listing every built\n * asset (plus any `additionalUrls`) as root-absolute URLs, with a content-based\n * `version`. It is the dependency-free counterpart to Workbox's `__WB_MANIFEST`:\n * `installPrecache` (from `tempest-react-sdk/sw`) reads this file at the service\n * worker's `install` event to cache the app shell for offline use.\n *\n * @example\n * // vite.config.ts\n * import { createViteConfig, tempestPwaManifest } from \"tempest-react-sdk/vite\";\n *\n * export default createViteConfig({\n * plugins: [tempestPwaManifest({ additionalUrls: [\"/manifest.webmanifest\", \"/icon.svg\"] })],\n * });\n */\nexport function tempestPwaManifest(options: TempestPwaManifestOptions = {}): TempestVitePlugin {\n const {\n fileName = \"precache-manifest.json\",\n additionalUrls = [],\n exclude = /\\.map$/,\n includeHtml = true,\n appShell = \"/index.html\",\n } = options;\n\n let base = \"/\";\n\n const plugin: Plugin = {\n name: \"tempest-pwa-manifest\",\n apply: \"build\",\n configResolved(config) {\n base = config.base ?? \"/\";\n },\n generateBundle(_outputOptions, bundle) {\n const urls = new Set<string>(additionalUrls.map((url) => withBase(base, url)));\n // Vite may emit index.html after this hook, so guarantee the shell.\n if (appShell) urls.add(withBase(base, appShell));\n for (const file of Object.keys(bundle)) {\n if (file === fileName) continue;\n if (exclude.test(file)) continue;\n if (!includeHtml && file.endsWith(\".html\")) continue;\n urls.add(joinBase(base, file));\n }\n\n const list = [...urls].sort();\n const version = hash(list.join(\"\\n\"));\n this.emitFile({\n type: \"asset\",\n fileName,\n source: JSON.stringify({ version, urls: list }),\n });\n },\n };\n\n return plugin as TempestVitePlugin;\n}\n"],"mappings":";AAqCA,SAAS,EAAS,GAAc,GAAsB;CAElD,OAAO,GADQ,EAAK,SAAS,GAAG,IAAI,IAAO,GAAG,EAAK,KAChC,IAAO,QAAQ,gBAAgB,IAAI;AAC1D;AASA,SAAS,EAAS,GAAc,GAAqB;CACjD,IAAM,IAAS,EAAK,SAAS,GAAG,IAAI,IAAO,GAAG,EAAK;CAGnD,OAFI,MAAW,MAAY,EAAI,WAAW,GAAG,IAAI,IAAM,IAAI,MACvD,EAAI,WAAW,CAAM,IAAU,IAC5B,GAAG,IAAS,EAAI,QAAQ,OAAO,EAAE;AAC5C;AAGA,SAAS,EAAK,GAAuB;CACjC,IAAI,IAAI;CACR,KAAK,IAAI,IAAI,GAAG,IAAI,EAAM,QAAQ,KAC9B,KAAM,KAAK,KAAK,IAAI,EAAM,WAAW,CAAC,MAAO;CAEjD,OAAO,EAAE,SAAS,EAAE;AACxB;AAiBA,SAAgB,EAAmB,IAAqC,CAAC,GAAsB;CAC3F,IAAM,EACF,cAAW,0BACX,oBAAiB,CAAC,GAClB,aAAU,UACV,iBAAc,IACd,cAAW,kBACX,GAEA,IAAO;CA6BX,OAAO;EA1BH,MAAM;EACN,OAAO;EACP,eAAe,GAAQ;GACnB,IAAO,EAAO,QAAQ;EAC1B;EACA,eAAe,GAAgB,GAAQ;GACnC,IAAM,IAAO,IAAI,IAAY,EAAe,KAAK,MAAQ,EAAS,GAAM,CAAG,CAAC,CAAC;GAE7E,AAAI,KAAU,EAAK,IAAI,EAAS,GAAM,CAAQ,CAAC;GAC/C,KAAK,IAAM,KAAQ,OAAO,KAAK,CAAM,GAC7B,MAAS,MACT,EAAQ,KAAK,CAAI,KACjB,CAAC,KAAe,EAAK,SAAS,OAAO,KACzC,EAAK,IAAI,EAAS,GAAM,CAAI,CAAC;GAGjC,IAAM,IAAO,CAAC,GAAG,CAAI,CAAC,CAAC,KAAK,GACtB,IAAU,EAAK,EAAK,KAAK,IAAI,CAAC;GACpC,KAAK,SAAS;IACV,MAAM;IACN;IACA,QAAQ,KAAK,UAAU;KAAE;KAAS,MAAM;IAAK,CAAC;GAClD,CAAC;EACL;CAGG;AACX"}
|