@stackframe/stack-shared 2.8.34 → 2.8.36
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/CHANGELOG.md +12 -0
- package/dist/config/schema.d.mts +249 -236
- package/dist/config/schema.d.ts +249 -236
- package/dist/config/schema.js +76 -54
- package/dist/config/schema.js.map +1 -1
- package/dist/esm/config/schema.js +76 -54
- package/dist/esm/config/schema.js.map +1 -1
- package/dist/esm/helpers/vault/client-side.js +46 -0
- package/dist/esm/helpers/vault/client-side.js.map +1 -0
- package/dist/esm/helpers/vault/server-side.js +92 -0
- package/dist/esm/helpers/vault/server-side.js.map +1 -0
- package/dist/esm/hooks/use-hover.js +71 -0
- package/dist/esm/hooks/use-hover.js.map +1 -0
- package/dist/esm/interface/admin-interface.js +30 -8
- package/dist/esm/interface/admin-interface.js.map +1 -1
- package/dist/esm/interface/client-interface.js +18 -4
- package/dist/esm/interface/client-interface.js.map +1 -1
- package/dist/esm/interface/server-interface.js +57 -10
- package/dist/esm/interface/server-interface.js.map +1 -1
- package/dist/esm/known-errors.js +38 -1
- package/dist/esm/known-errors.js.map +1 -1
- package/dist/esm/schema-fields.js +22 -9
- package/dist/esm/schema-fields.js.map +1 -1
- package/dist/esm/utils/bytes.js +1 -2
- package/dist/esm/utils/bytes.js.map +1 -1
- package/dist/esm/utils/crypto.js +83 -2
- package/dist/esm/utils/crypto.js.map +1 -1
- package/dist/esm/utils/currencies.js +0 -38
- package/dist/esm/utils/currencies.js.map +1 -1
- package/dist/esm/utils/currency-constants.js +42 -0
- package/dist/esm/utils/currency-constants.js.map +1 -0
- package/dist/esm/utils/dates.js +30 -0
- package/dist/esm/utils/dates.js.map +1 -1
- package/dist/esm/utils/numbers.js.map +1 -1
- package/dist/esm/utils/react.js +7 -3
- package/dist/esm/utils/react.js.map +1 -1
- package/dist/esm/utils/strings.js +3 -0
- package/dist/esm/utils/strings.js.map +1 -1
- package/dist/esm/utils/types.js.map +1 -1
- package/dist/helpers/password.d.mts +5 -5
- package/dist/helpers/password.d.ts +5 -5
- package/dist/helpers/vault/client-side.d.mts +14 -0
- package/dist/helpers/vault/client-side.d.ts +14 -0
- package/dist/helpers/vault/client-side.js +73 -0
- package/dist/helpers/vault/client-side.js.map +1 -0
- package/dist/helpers/vault/server-side.d.mts +7 -0
- package/dist/helpers/vault/server-side.d.ts +7 -0
- package/dist/helpers/vault/server-side.js +111 -0
- package/dist/helpers/vault/server-side.js.map +1 -0
- package/dist/hooks/use-hover.d.mts +6 -0
- package/dist/hooks/use-hover.d.ts +6 -0
- package/dist/hooks/use-hover.js +96 -0
- package/dist/hooks/use-hover.js.map +1 -0
- package/dist/index.d.mts +4 -4
- package/dist/index.d.ts +4 -4
- package/dist/interface/admin-interface.d.mts +22 -9
- package/dist/interface/admin-interface.d.ts +22 -9
- package/dist/interface/admin-interface.js +30 -8
- package/dist/interface/admin-interface.js.map +1 -1
- package/dist/interface/client-interface.d.mts +11 -6
- package/dist/interface/client-interface.d.ts +11 -6
- package/dist/interface/client-interface.js +18 -4
- package/dist/interface/client-interface.js.map +1 -1
- package/dist/interface/crud/current-user.d.mts +1 -1
- package/dist/interface/crud/current-user.d.ts +1 -1
- package/dist/interface/crud/project-api-keys.d.mts +2 -2
- package/dist/interface/crud/project-api-keys.d.ts +2 -2
- package/dist/interface/crud/team-member-profiles.d.mts +2 -2
- package/dist/interface/crud/team-member-profiles.d.ts +2 -2
- package/dist/interface/crud/users.d.mts +4 -4
- package/dist/interface/crud/users.d.ts +4 -4
- package/dist/interface/server-interface.d.mts +13 -2
- package/dist/interface/server-interface.d.ts +13 -2
- package/dist/interface/server-interface.js +57 -10
- package/dist/interface/server-interface.js.map +1 -1
- package/dist/known-errors.d.mts +12 -3
- package/dist/known-errors.d.ts +12 -3
- package/dist/known-errors.js +38 -1
- package/dist/known-errors.js.map +1 -1
- package/dist/schema-fields.d.mts +23 -7
- package/dist/schema-fields.d.ts +23 -7
- package/dist/schema-fields.js +25 -11
- package/dist/schema-fields.js.map +1 -1
- package/dist/utils/bytes.js +1 -2
- package/dist/utils/bytes.js.map +1 -1
- package/dist/utils/crypto.d.mts +31 -1
- package/dist/utils/crypto.d.ts +31 -1
- package/dist/utils/crypto.js +87 -2
- package/dist/utils/crypto.js.map +1 -1
- package/dist/utils/currencies.d.mts +3 -36
- package/dist/utils/currencies.d.ts +3 -36
- package/dist/utils/currencies.js +0 -39
- package/dist/utils/currencies.js.map +1 -1
- package/dist/utils/currency-constants.d.mts +37 -0
- package/dist/utils/currency-constants.d.ts +37 -0
- package/dist/utils/currency-constants.js +67 -0
- package/dist/utils/currency-constants.js.map +1 -0
- package/dist/utils/dates.d.mts +3 -1
- package/dist/utils/dates.d.ts +3 -1
- package/dist/utils/dates.js +32 -0
- package/dist/utils/dates.js.map +1 -1
- package/dist/utils/numbers.js.map +1 -1
- package/dist/utils/react.js +7 -3
- package/dist/utils/react.js.map +1 -1
- package/dist/utils/strings.js +3 -0
- package/dist/utils/strings.js.map +1 -1
- package/dist/utils/types.d.mts +5 -4
- package/dist/utils/types.d.ts +5 -4
- package/dist/utils/types.js.map +1 -1
- package/package.json +2 -1
package/dist/utils/crypto.js
CHANGED
|
@@ -20,13 +20,18 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
20
20
|
// src/utils/crypto.tsx
|
|
21
21
|
var crypto_exports = {};
|
|
22
22
|
__export(crypto_exports, {
|
|
23
|
+
decrypt: () => decrypt,
|
|
24
|
+
encrypt: () => encrypt,
|
|
23
25
|
generateRandomValues: () => generateRandomValues,
|
|
24
|
-
generateSecureRandomString: () => generateSecureRandomString
|
|
26
|
+
generateSecureRandomString: () => generateSecureRandomString,
|
|
27
|
+
hash: () => hash,
|
|
28
|
+
iteratedHash: () => iteratedHash
|
|
25
29
|
});
|
|
26
30
|
module.exports = __toCommonJS(crypto_exports);
|
|
27
31
|
var import_bytes = require("./bytes.js");
|
|
28
32
|
var import_errors = require("./errors.js");
|
|
29
33
|
var import_globals = require("./globals.js");
|
|
34
|
+
var import_results = require("./results.js");
|
|
30
35
|
function generateRandomValues(array) {
|
|
31
36
|
if (!import_globals.globalVar.crypto) {
|
|
32
37
|
throw new import_errors.StackAssertionError("Crypto API is not available in this environment. Are you using an old browser?");
|
|
@@ -43,9 +48,89 @@ function generateSecureRandomString(minBitsOfEntropy = 224) {
|
|
|
43
48
|
const str = (0, import_bytes.encodeBase32)(randomBytes);
|
|
44
49
|
return str.slice(str.length - base32CharactersCount).toLowerCase();
|
|
45
50
|
}
|
|
51
|
+
async function getDerivedSymmetricKey(purpose, secret, salt) {
|
|
52
|
+
const originalSecretKey = await crypto.subtle.importKey("raw", typeof secret === "string" ? new TextEncoder().encode(secret) : secret, "HKDF", false, ["deriveKey"]);
|
|
53
|
+
return await crypto.subtle.deriveKey(
|
|
54
|
+
{
|
|
55
|
+
name: "HKDF",
|
|
56
|
+
salt,
|
|
57
|
+
hash: "SHA-256",
|
|
58
|
+
info: new TextEncoder().encode(JSON.stringify([
|
|
59
|
+
"stack-crypto-helper-derived-symmetric-key",
|
|
60
|
+
purpose,
|
|
61
|
+
typeof secret === "string" ? "string-key" : "binary-key",
|
|
62
|
+
(0, import_bytes.encodeBase64)(salt)
|
|
63
|
+
]))
|
|
64
|
+
},
|
|
65
|
+
originalSecretKey,
|
|
66
|
+
{ name: "AES-GCM", length: 256 },
|
|
67
|
+
false,
|
|
68
|
+
["encrypt", "decrypt"]
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
async function encrypt({ purpose, secret, value }) {
|
|
72
|
+
const iv = crypto.getRandomValues(new Uint8Array(12));
|
|
73
|
+
const salt = crypto.getRandomValues(new Uint8Array(16));
|
|
74
|
+
const derivedSecretKey = await getDerivedSymmetricKey(purpose, secret, salt);
|
|
75
|
+
const cipher = await crypto.subtle.encrypt({
|
|
76
|
+
name: "AES-GCM",
|
|
77
|
+
iv
|
|
78
|
+
}, derivedSecretKey, value);
|
|
79
|
+
const version = [1, 0];
|
|
80
|
+
return new Uint8Array([...version, ...salt, ...iv, ...new Uint8Array(cipher)]);
|
|
81
|
+
}
|
|
82
|
+
async function decrypt({ purpose, secret, cipher }) {
|
|
83
|
+
const version = cipher.slice(0, 2);
|
|
84
|
+
if (version[0] !== 1 || version[1] !== 0) throw new import_errors.StackAssertionError("Invalid ciphertext version in decrypt(...); expected 0x0100", { purpose });
|
|
85
|
+
const salt = cipher.slice(2, 18);
|
|
86
|
+
const iv = cipher.slice(18, 30);
|
|
87
|
+
const cipherBytes = cipher.slice(30);
|
|
88
|
+
const derivedSecretKey = await getDerivedSymmetricKey(purpose, secret, salt);
|
|
89
|
+
try {
|
|
90
|
+
const plaintext = await crypto.subtle.decrypt({
|
|
91
|
+
name: "AES-GCM",
|
|
92
|
+
iv
|
|
93
|
+
}, derivedSecretKey, cipherBytes);
|
|
94
|
+
return import_results.Result.ok(new Uint8Array(plaintext));
|
|
95
|
+
} catch (e) {
|
|
96
|
+
if (e instanceof DOMException && e.name === "OperationError") {
|
|
97
|
+
return import_results.Result.error(new Error("Invalid ciphertext or secret when decrypting encrypted value", { cause: e }));
|
|
98
|
+
}
|
|
99
|
+
throw e;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
async function hash(options) {
|
|
103
|
+
return await iteratedHash({ ...options, iterations: 1 });
|
|
104
|
+
}
|
|
105
|
+
async function iteratedHash(options) {
|
|
106
|
+
const stringOrUint8ArrayToUint8Array = (value) => typeof value === "string" ? new TextEncoder().encode(value) : value;
|
|
107
|
+
const stringOrUint8ArrayToBase64 = (value) => (0, import_bytes.encodeBase64)(stringOrUint8ArrayToUint8Array(value));
|
|
108
|
+
const input = await crypto.subtle.importKey(
|
|
109
|
+
"raw",
|
|
110
|
+
stringOrUint8ArrayToUint8Array(options.value),
|
|
111
|
+
"PBKDF2",
|
|
112
|
+
false,
|
|
113
|
+
["deriveBits"]
|
|
114
|
+
);
|
|
115
|
+
return new Uint8Array(await crypto.subtle.deriveBits({
|
|
116
|
+
name: "PBKDF2",
|
|
117
|
+
salt: new TextEncoder().encode(JSON.stringify([
|
|
118
|
+
"stack-crypto-helper-iterated-hash",
|
|
119
|
+
options.purpose,
|
|
120
|
+
stringOrUint8ArrayToBase64(options.salt ?? ""),
|
|
121
|
+
stringOrUint8ArrayToBase64(options.extra ?? "")
|
|
122
|
+
])),
|
|
123
|
+
iterations: options.iterations,
|
|
124
|
+
hash: "SHA-256"
|
|
125
|
+
}, input, 256));
|
|
126
|
+
}
|
|
46
127
|
// Annotate the CommonJS export names for ESM import in node:
|
|
47
128
|
0 && (module.exports = {
|
|
129
|
+
decrypt,
|
|
130
|
+
encrypt,
|
|
48
131
|
generateRandomValues,
|
|
49
|
-
generateSecureRandomString
|
|
132
|
+
generateSecureRandomString,
|
|
133
|
+
hash,
|
|
134
|
+
iteratedHash
|
|
50
135
|
});
|
|
51
136
|
//# sourceMappingURL=crypto.js.map
|
package/dist/utils/crypto.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/utils/crypto.tsx"],"sourcesContent":["import { encodeBase32 } from \"./bytes\";\nimport { StackAssertionError } from \"./errors\";\nimport { globalVar } from \"./globals\";\n\nexport function generateRandomValues(array: Uint8Array): typeof array {\n if (!globalVar.crypto) {\n throw new StackAssertionError(\"Crypto API is not available in this environment. Are you using an old browser?\");\n }\n if (!globalVar.crypto.getRandomValues) {\n throw new StackAssertionError(\"crypto.getRandomValues is not available in this environment. Are you using an old browser?\");\n }\n return globalVar.crypto.getRandomValues(array);\n}\n\n/**\n * Generates a secure alphanumeric string using the system's cryptographically secure\n * random number generator.\n */\nexport function generateSecureRandomString(minBitsOfEntropy: number = 224) {\n const base32CharactersCount = Math.ceil(minBitsOfEntropy / 5);\n const bytesCount = Math.ceil(base32CharactersCount * 5 / 8);\n const randomBytes = generateRandomValues(new Uint8Array(bytesCount));\n const str = encodeBase32(randomBytes);\n return str.slice(str.length - base32CharactersCount).toLowerCase();\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,mBAA6B;AAC7B,oBAAoC;AACpC,qBAA0B;AAEnB,SAAS,qBAAqB,OAAiC;AACpE,MAAI,CAAC,yBAAU,QAAQ;AACrB,UAAM,IAAI,kCAAoB,gFAAgF;AAAA,EAChH;AACA,MAAI,CAAC,yBAAU,OAAO,iBAAiB;AACrC,UAAM,IAAI,kCAAoB,4FAA4F;AAAA,EAC5H;AACA,SAAO,yBAAU,OAAO,gBAAgB,KAAK;AAC/C;AAMO,SAAS,2BAA2B,mBAA2B,KAAK;AACzE,QAAM,wBAAwB,KAAK,KAAK,mBAAmB,CAAC;AAC5D,QAAM,aAAa,KAAK,KAAK,wBAAwB,IAAI,CAAC;AAC1D,QAAM,cAAc,qBAAqB,IAAI,WAAW,UAAU,CAAC;AACnE,QAAM,UAAM,2BAAa,WAAW;AACpC,SAAO,IAAI,MAAM,IAAI,SAAS,qBAAqB,EAAE,YAAY;AACnE;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/utils/crypto.tsx"],"sourcesContent":["import { encodeBase32, encodeBase64 } from \"./bytes\";\nimport { StackAssertionError } from \"./errors\";\nimport { globalVar } from \"./globals\";\nimport { Result } from \"./results\";\n\nexport function generateRandomValues(array: Uint8Array): typeof array {\n if (!globalVar.crypto) {\n throw new StackAssertionError(\"Crypto API is not available in this environment. Are you using an old browser?\");\n }\n if (!globalVar.crypto.getRandomValues) {\n throw new StackAssertionError(\"crypto.getRandomValues is not available in this environment. Are you using an old browser?\");\n }\n return globalVar.crypto.getRandomValues(array);\n}\n\n/**\n * Generates a secure alphanumeric string using the system's cryptographically secure\n * random number generator.\n */\nexport function generateSecureRandomString(minBitsOfEntropy: number = 224) {\n const base32CharactersCount = Math.ceil(minBitsOfEntropy / 5);\n const bytesCount = Math.ceil(base32CharactersCount * 5 / 8);\n const randomBytes = generateRandomValues(new Uint8Array(bytesCount));\n const str = encodeBase32(randomBytes);\n return str.slice(str.length - base32CharactersCount).toLowerCase();\n}\n\nasync function getDerivedSymmetricKey(purpose: string, secret: string | Uint8Array, salt: Uint8Array) {\n const originalSecretKey = await crypto.subtle.importKey(\"raw\", typeof secret === \"string\" ? new TextEncoder().encode(secret) : secret, \"HKDF\", false, [\"deriveKey\"]);\n return await crypto.subtle.deriveKey(\n {\n name: \"HKDF\",\n salt,\n hash: \"SHA-256\",\n info: new TextEncoder().encode(JSON.stringify([\n \"stack-crypto-helper-derived-symmetric-key\",\n purpose,\n typeof secret === \"string\" ? \"string-key\" : \"binary-key\",\n encodeBase64(salt),\n ])),\n },\n originalSecretKey,\n { name: \"AES-GCM\", length: 256 },\n false,\n [\"encrypt\", \"decrypt\"]\n );\n}\n\nexport async function encrypt({ purpose, secret, value }: { purpose: string, secret: string | Uint8Array, value: Uint8Array }) {\n const iv = crypto.getRandomValues(new Uint8Array(12));\n const salt = crypto.getRandomValues(new Uint8Array(16));\n const derivedSecretKey = await getDerivedSymmetricKey(purpose, secret, salt);\n\n const cipher = await crypto.subtle.encrypt({\n name: \"AES-GCM\",\n iv,\n }, derivedSecretKey, value);\n\n const version = [0x01, 0x00];\n return new Uint8Array([...version, ...salt, ...iv, ...new Uint8Array(cipher)]);\n}\n\nexport async function decrypt({ purpose, secret, cipher }: { purpose: string, secret: string | Uint8Array, cipher: Uint8Array }) {\n const version = cipher.slice(0, 2);\n if (version[0] !== 0x01 || version[1] !== 0x00) throw new StackAssertionError(\"Invalid ciphertext version in decrypt(...); expected 0x0100\", { purpose });\n const salt = cipher.slice(2, 18);\n const iv = cipher.slice(18, 30);\n const cipherBytes = cipher.slice(30);\n const derivedSecretKey = await getDerivedSymmetricKey(purpose, secret, salt);\n\n try {\n const plaintext = await crypto.subtle.decrypt({\n name: \"AES-GCM\",\n iv,\n }, derivedSecretKey, cipherBytes);\n return Result.ok(new Uint8Array(plaintext));\n } catch (e) {\n if (e instanceof DOMException && e.name === \"OperationError\") {\n return Result.error(new Error(\"Invalid ciphertext or secret when decrypting encrypted value\", { cause: e }));\n }\n throw e;\n }\n}\n\nundefined?.test(\"encrypt & decrypt\", async ({ expect }) => {\n const encryptAndDecrypt = async (encryptPurpose: string, decryptPurpose: string, encryptSecret: string | Uint8Array, decryptSecret: string | Uint8Array, value: Uint8Array) => {\n const encrypted = await encrypt({ purpose: encryptPurpose, secret: encryptSecret, value });\n const decrypted = await decrypt({ purpose: decryptPurpose, secret: decryptSecret, cipher: encrypted });\n return decrypted;\n };\n\n const exampleBytes = new TextEncoder().encode(\"hello\");\n\n const exampleKey1 = crypto.getRandomValues(new Uint8Array(32));\n const exampleKey2 = crypto.getRandomValues(new Uint8Array(32));\n\n expect(await encryptAndDecrypt(\"p\", \"p\", \"secret\", \"secret\", exampleBytes)).toEqual(Result.ok(exampleBytes));\n expect(await encryptAndDecrypt(\"p\", \"p\", exampleKey1, exampleKey1, exampleBytes)).toEqual(Result.ok(exampleBytes));\n expect(await encryptAndDecrypt(\"p\", \"p\", exampleKey1, \"secret\", exampleBytes)).toEqual(Result.error(expect.objectContaining({ message: \"Invalid ciphertext or secret when decrypting encrypted value\" })));\n expect(await encryptAndDecrypt(\"p\", \"p\", exampleKey1, exampleKey2, exampleBytes)).toEqual(Result.error(expect.objectContaining({ message: \"Invalid ciphertext or secret when decrypting encrypted value\" })));\n expect(await encryptAndDecrypt(\"p\", \"not-p\", exampleKey1, exampleKey1, exampleBytes)).toEqual(Result.error(expect.objectContaining({ message: \"Invalid ciphertext or secret when decrypting encrypted value\" })));\n});\n\nexport type HashOptions = {\n purpose: string,\n salt?: string | Uint8Array,\n extra?: string | Uint8Array,\n value: string | Uint8Array,\n};\n\nexport async function hash(options: HashOptions) {\n return await iteratedHash({ ...options, iterations: 1 });\n}\n\nexport async function iteratedHash(options: HashOptions & { iterations: number }) {\n const stringOrUint8ArrayToUint8Array = (value: string | Uint8Array) => typeof value === \"string\" ? new TextEncoder().encode(value) : value;\n const stringOrUint8ArrayToBase64 = (value: string | Uint8Array) => encodeBase64(stringOrUint8ArrayToUint8Array(value));\n const input = await crypto.subtle.importKey(\n \"raw\",\n stringOrUint8ArrayToUint8Array(options.value),\n \"PBKDF2\",\n false,\n [\"deriveBits\"]\n );\n return new Uint8Array(await crypto.subtle.deriveBits({\n name: \"PBKDF2\",\n salt: new TextEncoder().encode(JSON.stringify([\n \"stack-crypto-helper-iterated-hash\",\n options.purpose,\n stringOrUint8ArrayToBase64(options.salt ?? \"\"),\n stringOrUint8ArrayToBase64(options.extra ?? \"\"),\n ])),\n iterations: options.iterations,\n hash: \"SHA-256\",\n }, input, 256));\n}\n\nundefined?.test(\"iteratedHash\", async ({ expect }) => {\n const valueBytes = new TextEncoder().encode(\"hello\");\n const incrementBytes = new Uint8Array([0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10]);\n\n const hash = await iteratedHash({ purpose: \"purpose\", value: valueBytes, iterations: 100_000 });\n const hash2 = await iteratedHash({ purpose: \"purpose\", value: valueBytes, iterations: 100_000 });\n const hashWithDifferentPurpose = await iteratedHash({ purpose: \"different-purpose\", value: valueBytes, iterations: 100_000 });\n const hashWithEmptySalt = await iteratedHash({ purpose: \"purpose\", value: valueBytes, salt: new Uint8Array(0), iterations: 100_000 });\n const hashWithDifferentSalt = await iteratedHash({ purpose: \"purpose\", value: valueBytes, salt: incrementBytes, iterations: 100_000 });\n const hashWithEmptyExtra = await iteratedHash({ purpose: \"purpose\", value: valueBytes, extra: new Uint8Array(0), iterations: 100_000 });\n const hashWithDifferentExtra = await iteratedHash({ purpose: \"purpose\", value: valueBytes, extra: incrementBytes, iterations: 100_000 });\n const hashWithDifferentValue = await iteratedHash({ purpose: \"purpose\", value: new TextEncoder().encode(\"hello2\"), iterations: 100_000 });\n const hashWithDifferentSaltAndExtra = await iteratedHash({ purpose: \"purpose\", value: valueBytes, salt: incrementBytes, extra: incrementBytes, iterations: 100_000 });\n const hashWithDifferentIterations = await iteratedHash({ purpose: \"purpose\", value: valueBytes, iterations: 100_001 });\n\n\n expect(hash).toEqual(hash2);\n expect(hash).not.toEqual(hashWithDifferentPurpose);\n expect(hash).toEqual(hashWithEmptySalt);\n expect(hash).not.toEqual(hashWithDifferentSalt);\n expect(hash).toEqual(hashWithEmptyExtra);\n expect(hash).not.toEqual(hashWithDifferentExtra);\n expect(hash).not.toEqual(hashWithDifferentValue);\n expect(hash).not.toEqual(hashWithDifferentIterations);\n\n expect(hashWithDifferentSalt).not.toEqual(hashWithDifferentExtra);\n expect(hashWithDifferentSalt).not.toEqual(hashWithDifferentSaltAndExtra);\n expect(hashWithDifferentExtra).not.toEqual(hashWithDifferentSaltAndExtra);\n});\n"],"mappings":"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","names":[]}
|
|
@@ -1,39 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
decimals: number;
|
|
4
|
-
stripeDecimals: number;
|
|
5
|
-
};
|
|
1
|
+
import { SUPPORTED_CURRENCIES, MoneyAmount, Currency } from './currency-constants.mjs';
|
|
2
|
+
|
|
6
3
|
type SupportedCurrency = (typeof SUPPORTED_CURRENCIES)[number];
|
|
7
|
-
declare const SUPPORTED_CURRENCIES: [{
|
|
8
|
-
readonly code: "USD";
|
|
9
|
-
readonly decimals: 2;
|
|
10
|
-
readonly stripeDecimals: 2;
|
|
11
|
-
}, {
|
|
12
|
-
readonly code: "EUR";
|
|
13
|
-
readonly decimals: 2;
|
|
14
|
-
readonly stripeDecimals: 2;
|
|
15
|
-
}, {
|
|
16
|
-
readonly code: "GBP";
|
|
17
|
-
readonly decimals: 2;
|
|
18
|
-
readonly stripeDecimals: 2;
|
|
19
|
-
}, {
|
|
20
|
-
readonly code: "JPY";
|
|
21
|
-
readonly decimals: 0;
|
|
22
|
-
readonly stripeDecimals: 0;
|
|
23
|
-
}, {
|
|
24
|
-
readonly code: "INR";
|
|
25
|
-
readonly decimals: 2;
|
|
26
|
-
readonly stripeDecimals: 2;
|
|
27
|
-
}, {
|
|
28
|
-
readonly code: "AUD";
|
|
29
|
-
readonly decimals: 2;
|
|
30
|
-
readonly stripeDecimals: 2;
|
|
31
|
-
}, {
|
|
32
|
-
readonly code: "CAD";
|
|
33
|
-
readonly decimals: 2;
|
|
34
|
-
readonly stripeDecimals: 2;
|
|
35
|
-
}];
|
|
36
|
-
type MoneyAmount = `${number}` | `${number}.${number}`;
|
|
37
4
|
declare function moneyAmountToStripeUnits(amount: MoneyAmount, currency: Currency): number;
|
|
38
5
|
|
|
39
|
-
export { type
|
|
6
|
+
export { type SupportedCurrency, moneyAmountToStripeUnits };
|
|
@@ -1,39 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
decimals: number;
|
|
4
|
-
stripeDecimals: number;
|
|
5
|
-
};
|
|
1
|
+
import { SUPPORTED_CURRENCIES, MoneyAmount, Currency } from './currency-constants.js';
|
|
2
|
+
|
|
6
3
|
type SupportedCurrency = (typeof SUPPORTED_CURRENCIES)[number];
|
|
7
|
-
declare const SUPPORTED_CURRENCIES: [{
|
|
8
|
-
readonly code: "USD";
|
|
9
|
-
readonly decimals: 2;
|
|
10
|
-
readonly stripeDecimals: 2;
|
|
11
|
-
}, {
|
|
12
|
-
readonly code: "EUR";
|
|
13
|
-
readonly decimals: 2;
|
|
14
|
-
readonly stripeDecimals: 2;
|
|
15
|
-
}, {
|
|
16
|
-
readonly code: "GBP";
|
|
17
|
-
readonly decimals: 2;
|
|
18
|
-
readonly stripeDecimals: 2;
|
|
19
|
-
}, {
|
|
20
|
-
readonly code: "JPY";
|
|
21
|
-
readonly decimals: 0;
|
|
22
|
-
readonly stripeDecimals: 0;
|
|
23
|
-
}, {
|
|
24
|
-
readonly code: "INR";
|
|
25
|
-
readonly decimals: 2;
|
|
26
|
-
readonly stripeDecimals: 2;
|
|
27
|
-
}, {
|
|
28
|
-
readonly code: "AUD";
|
|
29
|
-
readonly decimals: 2;
|
|
30
|
-
readonly stripeDecimals: 2;
|
|
31
|
-
}, {
|
|
32
|
-
readonly code: "CAD";
|
|
33
|
-
readonly decimals: 2;
|
|
34
|
-
readonly stripeDecimals: 2;
|
|
35
|
-
}];
|
|
36
|
-
type MoneyAmount = `${number}` | `${number}.${number}`;
|
|
37
4
|
declare function moneyAmountToStripeUnits(amount: MoneyAmount, currency: Currency): number;
|
|
38
5
|
|
|
39
|
-
export { type
|
|
6
|
+
export { type SupportedCurrency, moneyAmountToStripeUnits };
|
package/dist/utils/currencies.js
CHANGED
|
@@ -20,49 +20,11 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
20
20
|
// src/utils/currencies.tsx
|
|
21
21
|
var currencies_exports = {};
|
|
22
22
|
__export(currencies_exports, {
|
|
23
|
-
SUPPORTED_CURRENCIES: () => SUPPORTED_CURRENCIES,
|
|
24
23
|
moneyAmountToStripeUnits: () => moneyAmountToStripeUnits
|
|
25
24
|
});
|
|
26
25
|
module.exports = __toCommonJS(currencies_exports);
|
|
27
26
|
var import_schema_fields = require("../schema-fields.js");
|
|
28
27
|
var import_errors = require("./errors.js");
|
|
29
|
-
var SUPPORTED_CURRENCIES = [
|
|
30
|
-
{
|
|
31
|
-
code: "USD",
|
|
32
|
-
decimals: 2,
|
|
33
|
-
stripeDecimals: 2
|
|
34
|
-
},
|
|
35
|
-
{
|
|
36
|
-
code: "EUR",
|
|
37
|
-
decimals: 2,
|
|
38
|
-
stripeDecimals: 2
|
|
39
|
-
},
|
|
40
|
-
{
|
|
41
|
-
code: "GBP",
|
|
42
|
-
decimals: 2,
|
|
43
|
-
stripeDecimals: 2
|
|
44
|
-
},
|
|
45
|
-
{
|
|
46
|
-
code: "JPY",
|
|
47
|
-
decimals: 0,
|
|
48
|
-
stripeDecimals: 0
|
|
49
|
-
},
|
|
50
|
-
{
|
|
51
|
-
code: "INR",
|
|
52
|
-
decimals: 2,
|
|
53
|
-
stripeDecimals: 2
|
|
54
|
-
},
|
|
55
|
-
{
|
|
56
|
-
code: "AUD",
|
|
57
|
-
decimals: 2,
|
|
58
|
-
stripeDecimals: 2
|
|
59
|
-
},
|
|
60
|
-
{
|
|
61
|
-
code: "CAD",
|
|
62
|
-
decimals: 2,
|
|
63
|
-
stripeDecimals: 2
|
|
64
|
-
}
|
|
65
|
-
];
|
|
66
28
|
function moneyAmountToStripeUnits(amount, currency) {
|
|
67
29
|
const validated = (0, import_schema_fields.moneyAmountSchema)(currency).defined().validateSync(amount);
|
|
68
30
|
if (currency.stripeDecimals !== currency.decimals) {
|
|
@@ -72,7 +34,6 @@ function moneyAmountToStripeUnits(amount, currency) {
|
|
|
72
34
|
}
|
|
73
35
|
// Annotate the CommonJS export names for ESM import in node:
|
|
74
36
|
0 && (module.exports = {
|
|
75
|
-
SUPPORTED_CURRENCIES,
|
|
76
37
|
moneyAmountToStripeUnits
|
|
77
38
|
});
|
|
78
39
|
//# sourceMappingURL=currencies.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/utils/currencies.tsx"],"sourcesContent":["import { moneyAmountSchema } from \"../schema-fields\";\nimport {
|
|
1
|
+
{"version":3,"sources":["../../src/utils/currencies.tsx"],"sourcesContent":["import { moneyAmountSchema } from \"../schema-fields\";\nimport { SUPPORTED_CURRENCIES, type Currency, type MoneyAmount } from \"./currency-constants\";\nimport { StackAssertionError } from \"./errors\";\n\nexport type SupportedCurrency = (typeof SUPPORTED_CURRENCIES)[number];\n\nexport function moneyAmountToStripeUnits(amount: MoneyAmount, currency: Currency): number {\n const validated = moneyAmountSchema(currency).defined().validateSync(amount);\n if (currency.stripeDecimals !== currency.decimals) {\n throw new StackAssertionError(\"unimplemented: TODO support different decimal configurations\");\n }\n\n return Number.parseInt(validated.replace('.', ''), 10);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,2BAAkC;AAElC,oBAAoC;AAI7B,SAAS,yBAAyB,QAAqB,UAA4B;AACxF,QAAM,gBAAY,wCAAkB,QAAQ,EAAE,QAAQ,EAAE,aAAa,MAAM;AAC3E,MAAI,SAAS,mBAAmB,SAAS,UAAU;AACjD,UAAM,IAAI,kCAAoB,8DAA8D;AAAA,EAC9F;AAEA,SAAO,OAAO,SAAS,UAAU,QAAQ,KAAK,EAAE,GAAG,EAAE;AACvD;","names":[]}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
type MoneyAmount = `${number}` | `${number}.${number}`;
|
|
2
|
+
type Currency = {
|
|
3
|
+
code: Uppercase<string>;
|
|
4
|
+
decimals: number;
|
|
5
|
+
stripeDecimals: number;
|
|
6
|
+
};
|
|
7
|
+
declare const SUPPORTED_CURRENCIES: [{
|
|
8
|
+
readonly code: "USD";
|
|
9
|
+
readonly decimals: 2;
|
|
10
|
+
readonly stripeDecimals: 2;
|
|
11
|
+
}, {
|
|
12
|
+
readonly code: "EUR";
|
|
13
|
+
readonly decimals: 2;
|
|
14
|
+
readonly stripeDecimals: 2;
|
|
15
|
+
}, {
|
|
16
|
+
readonly code: "GBP";
|
|
17
|
+
readonly decimals: 2;
|
|
18
|
+
readonly stripeDecimals: 2;
|
|
19
|
+
}, {
|
|
20
|
+
readonly code: "JPY";
|
|
21
|
+
readonly decimals: 0;
|
|
22
|
+
readonly stripeDecimals: 0;
|
|
23
|
+
}, {
|
|
24
|
+
readonly code: "INR";
|
|
25
|
+
readonly decimals: 2;
|
|
26
|
+
readonly stripeDecimals: 2;
|
|
27
|
+
}, {
|
|
28
|
+
readonly code: "AUD";
|
|
29
|
+
readonly decimals: 2;
|
|
30
|
+
readonly stripeDecimals: 2;
|
|
31
|
+
}, {
|
|
32
|
+
readonly code: "CAD";
|
|
33
|
+
readonly decimals: 2;
|
|
34
|
+
readonly stripeDecimals: 2;
|
|
35
|
+
}];
|
|
36
|
+
|
|
37
|
+
export { type Currency, type MoneyAmount, SUPPORTED_CURRENCIES };
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
type MoneyAmount = `${number}` | `${number}.${number}`;
|
|
2
|
+
type Currency = {
|
|
3
|
+
code: Uppercase<string>;
|
|
4
|
+
decimals: number;
|
|
5
|
+
stripeDecimals: number;
|
|
6
|
+
};
|
|
7
|
+
declare const SUPPORTED_CURRENCIES: [{
|
|
8
|
+
readonly code: "USD";
|
|
9
|
+
readonly decimals: 2;
|
|
10
|
+
readonly stripeDecimals: 2;
|
|
11
|
+
}, {
|
|
12
|
+
readonly code: "EUR";
|
|
13
|
+
readonly decimals: 2;
|
|
14
|
+
readonly stripeDecimals: 2;
|
|
15
|
+
}, {
|
|
16
|
+
readonly code: "GBP";
|
|
17
|
+
readonly decimals: 2;
|
|
18
|
+
readonly stripeDecimals: 2;
|
|
19
|
+
}, {
|
|
20
|
+
readonly code: "JPY";
|
|
21
|
+
readonly decimals: 0;
|
|
22
|
+
readonly stripeDecimals: 0;
|
|
23
|
+
}, {
|
|
24
|
+
readonly code: "INR";
|
|
25
|
+
readonly decimals: 2;
|
|
26
|
+
readonly stripeDecimals: 2;
|
|
27
|
+
}, {
|
|
28
|
+
readonly code: "AUD";
|
|
29
|
+
readonly decimals: 2;
|
|
30
|
+
readonly stripeDecimals: 2;
|
|
31
|
+
}, {
|
|
32
|
+
readonly code: "CAD";
|
|
33
|
+
readonly decimals: 2;
|
|
34
|
+
readonly stripeDecimals: 2;
|
|
35
|
+
}];
|
|
36
|
+
|
|
37
|
+
export { type Currency, type MoneyAmount, SUPPORTED_CURRENCIES };
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/utils/currency-constants.tsx
|
|
21
|
+
var currency_constants_exports = {};
|
|
22
|
+
__export(currency_constants_exports, {
|
|
23
|
+
SUPPORTED_CURRENCIES: () => SUPPORTED_CURRENCIES
|
|
24
|
+
});
|
|
25
|
+
module.exports = __toCommonJS(currency_constants_exports);
|
|
26
|
+
var SUPPORTED_CURRENCIES = [
|
|
27
|
+
{
|
|
28
|
+
code: "USD",
|
|
29
|
+
decimals: 2,
|
|
30
|
+
stripeDecimals: 2
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
code: "EUR",
|
|
34
|
+
decimals: 2,
|
|
35
|
+
stripeDecimals: 2
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
code: "GBP",
|
|
39
|
+
decimals: 2,
|
|
40
|
+
stripeDecimals: 2
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
code: "JPY",
|
|
44
|
+
decimals: 0,
|
|
45
|
+
stripeDecimals: 0
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
code: "INR",
|
|
49
|
+
decimals: 2,
|
|
50
|
+
stripeDecimals: 2
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
code: "AUD",
|
|
54
|
+
decimals: 2,
|
|
55
|
+
stripeDecimals: 2
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
code: "CAD",
|
|
59
|
+
decimals: 2,
|
|
60
|
+
stripeDecimals: 2
|
|
61
|
+
}
|
|
62
|
+
];
|
|
63
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
64
|
+
0 && (module.exports = {
|
|
65
|
+
SUPPORTED_CURRENCIES
|
|
66
|
+
});
|
|
67
|
+
//# sourceMappingURL=currency-constants.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/utils/currency-constants.tsx"],"sourcesContent":["export type MoneyAmount = `${number}` | `${number}.${number}`;\n\nexport type Currency = {\n code: Uppercase<string>,\n decimals: number,\n stripeDecimals: number,\n};\n\nexport const SUPPORTED_CURRENCIES = [\n {\n code: 'USD',\n decimals: 2,\n stripeDecimals: 2,\n },\n {\n code: 'EUR',\n decimals: 2,\n stripeDecimals: 2,\n },\n {\n code: 'GBP',\n decimals: 2,\n stripeDecimals: 2,\n },\n {\n code: 'JPY',\n decimals: 0,\n stripeDecimals: 0,\n },\n {\n code: 'INR',\n decimals: 2,\n stripeDecimals: 2,\n },\n {\n code: 'AUD',\n decimals: 2,\n stripeDecimals: 2,\n },\n {\n code: 'CAD',\n decimals: 2,\n stripeDecimals: 2,\n },\n] as const satisfies Currency[];\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAQO,IAAM,uBAAuB;AAAA,EAClC;AAAA,IACE,MAAM;AAAA,IACN,UAAU;AAAA,IACV,gBAAgB;AAAA,EAClB;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,UAAU;AAAA,IACV,gBAAgB;AAAA,EAClB;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,UAAU;AAAA,IACV,gBAAgB;AAAA,EAClB;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,UAAU;AAAA,IACV,gBAAgB;AAAA,EAClB;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,UAAU;AAAA,IACV,gBAAgB;AAAA,EAClB;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,UAAU;AAAA,IACV,gBAAgB;AAAA,EAClB;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,UAAU;AAAA,IACV,gBAAgB;AAAA,EAClB;AACF;","names":[]}
|
package/dist/utils/dates.d.mts
CHANGED
|
@@ -15,5 +15,7 @@ type Interval = [number, 'millisecond' | 'second' | 'minute' | 'hour' | 'day' |
|
|
|
15
15
|
type DayInterval = [number, 'day' | 'week' | 'month' | 'year'];
|
|
16
16
|
declare function subtractInterval(date: Date, interval: Interval): Date;
|
|
17
17
|
declare function addInterval(date: Date, interval: Interval): Date;
|
|
18
|
+
declare const FAR_FUTURE_DATE: Date;
|
|
19
|
+
declare function getIntervalsElapsed(anchor: Date, to: Date, repeat: DayInterval): number;
|
|
18
20
|
|
|
19
|
-
export { type DayInterval, type Interval, addInterval, fromNow, fromNowDetailed, getInputDatetimeLocalString, isWeekend, subtractInterval };
|
|
21
|
+
export { type DayInterval, FAR_FUTURE_DATE, type Interval, addInterval, fromNow, fromNowDetailed, getInputDatetimeLocalString, getIntervalsElapsed, isWeekend, subtractInterval };
|
package/dist/utils/dates.d.ts
CHANGED
|
@@ -15,5 +15,7 @@ type Interval = [number, 'millisecond' | 'second' | 'minute' | 'hour' | 'day' |
|
|
|
15
15
|
type DayInterval = [number, 'day' | 'week' | 'month' | 'year'];
|
|
16
16
|
declare function subtractInterval(date: Date, interval: Interval): Date;
|
|
17
17
|
declare function addInterval(date: Date, interval: Interval): Date;
|
|
18
|
+
declare const FAR_FUTURE_DATE: Date;
|
|
19
|
+
declare function getIntervalsElapsed(anchor: Date, to: Date, repeat: DayInterval): number;
|
|
18
20
|
|
|
19
|
-
export { type DayInterval, type Interval, addInterval, fromNow, fromNowDetailed, getInputDatetimeLocalString, isWeekend, subtractInterval };
|
|
21
|
+
export { type DayInterval, FAR_FUTURE_DATE, type Interval, addInterval, fromNow, fromNowDetailed, getInputDatetimeLocalString, getIntervalsElapsed, isWeekend, subtractInterval };
|
package/dist/utils/dates.js
CHANGED
|
@@ -20,10 +20,12 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
20
20
|
// src/utils/dates.tsx
|
|
21
21
|
var dates_exports = {};
|
|
22
22
|
__export(dates_exports, {
|
|
23
|
+
FAR_FUTURE_DATE: () => FAR_FUTURE_DATE,
|
|
23
24
|
addInterval: () => addInterval,
|
|
24
25
|
fromNow: () => fromNow,
|
|
25
26
|
fromNowDetailed: () => fromNowDetailed,
|
|
26
27
|
getInputDatetimeLocalString: () => getInputDatetimeLocalString,
|
|
28
|
+
getIntervalsElapsed: () => getIntervalsElapsed,
|
|
27
29
|
isWeekend: () => isWeekend,
|
|
28
30
|
subtractInterval: () => subtractInterval
|
|
29
31
|
});
|
|
@@ -136,12 +138,42 @@ function subtractInterval(date, interval) {
|
|
|
136
138
|
function addInterval(date, interval) {
|
|
137
139
|
return applyInterval(date, 1, interval);
|
|
138
140
|
}
|
|
141
|
+
var FAR_FUTURE_DATE = /* @__PURE__ */ new Date(864e13);
|
|
142
|
+
function getMsPerDayIntervalUnit(unit) {
|
|
143
|
+
if (unit === "day") {
|
|
144
|
+
return 24 * 60 * 60 * 1e3;
|
|
145
|
+
}
|
|
146
|
+
return 7 * 24 * 60 * 60 * 1e3;
|
|
147
|
+
}
|
|
148
|
+
function getIntervalsElapsed(anchor, to, repeat) {
|
|
149
|
+
const [amount, unit] = repeat;
|
|
150
|
+
if (to <= anchor) return 0;
|
|
151
|
+
if (unit === "day" || unit === "week") {
|
|
152
|
+
const msPerUnit = getMsPerDayIntervalUnit(unit);
|
|
153
|
+
const diffMs = to.getTime() - anchor.getTime();
|
|
154
|
+
return Math.floor(diffMs / (msPerUnit * amount));
|
|
155
|
+
}
|
|
156
|
+
if (["month", "year"].includes(unit)) {
|
|
157
|
+
let count = 0;
|
|
158
|
+
let current = new Date(anchor);
|
|
159
|
+
for (; ; ) {
|
|
160
|
+
const next = addInterval(new Date(current), [amount, unit]);
|
|
161
|
+
if (next > to) break;
|
|
162
|
+
current = next;
|
|
163
|
+
count += 1;
|
|
164
|
+
}
|
|
165
|
+
return count;
|
|
166
|
+
}
|
|
167
|
+
return 0;
|
|
168
|
+
}
|
|
139
169
|
// Annotate the CommonJS export names for ESM import in node:
|
|
140
170
|
0 && (module.exports = {
|
|
171
|
+
FAR_FUTURE_DATE,
|
|
141
172
|
addInterval,
|
|
142
173
|
fromNow,
|
|
143
174
|
fromNowDetailed,
|
|
144
175
|
getInputDatetimeLocalString,
|
|
176
|
+
getIntervalsElapsed,
|
|
145
177
|
isWeekend,
|
|
146
178
|
subtractInterval
|
|
147
179
|
});
|
package/dist/utils/dates.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/utils/dates.tsx"],"sourcesContent":["import { intervalSchema } from \"../schema-fields\";\nimport { StackAssertionError } from \"./errors\";\nimport { remainder } from \"./math\";\n\nexport function isWeekend(date: Date): boolean {\n return date.getDay() === 0 || date.getDay() === 6;\n}\n\nundefined?.test(\"isWeekend\", ({ expect }) => {\n // Sunday (day 0)\n expect(isWeekend(new Date(2023, 0, 1))).toBe(true);\n // Saturday (day 6)\n expect(isWeekend(new Date(2023, 0, 7))).toBe(true);\n // Monday (day 1)\n expect(isWeekend(new Date(2023, 0, 2))).toBe(false);\n // Friday (day 5)\n expect(isWeekend(new Date(2023, 0, 6))).toBe(false);\n});\n\nconst agoUnits = [\n [60, 'second'],\n [60, 'minute'],\n [24, 'hour'],\n [7, 'day'],\n [5, 'week'],\n] as const;\n\nexport function fromNow(date: Date): string {\n return fromNowDetailed(date).result;\n}\n\nundefined?.test(\"fromNow\", ({ expect }) => {\n // Set a fixed date for testing\n const fixedDate = new Date(\"2023-01-15T12:00:00.000Z\");\n\n // Use Vitest's fake timers\n undefined?.vi.useFakeTimers();\n undefined?.vi.setSystemTime(fixedDate);\n\n // Test past times\n expect(fromNow(new Date(\"2023-01-15T11:59:50.000Z\"))).toBe(\"just now\");\n expect(fromNow(new Date(\"2023-01-15T11:59:00.000Z\"))).toBe(\"1 minute ago\");\n expect(fromNow(new Date(\"2023-01-15T11:00:00.000Z\"))).toBe(\"1 hour ago\");\n expect(fromNow(new Date(\"2023-01-14T12:00:00.000Z\"))).toBe(\"1 day ago\");\n expect(fromNow(new Date(\"2023-01-08T12:00:00.000Z\"))).toBe(\"1 week ago\");\n\n // Test future times\n expect(fromNow(new Date(\"2023-01-15T12:00:10.000Z\"))).toBe(\"just now\");\n expect(fromNow(new Date(\"2023-01-15T12:01:00.000Z\"))).toBe(\"in 1 minute\");\n expect(fromNow(new Date(\"2023-01-15T13:00:00.000Z\"))).toBe(\"in 1 hour\");\n expect(fromNow(new Date(\"2023-01-16T12:00:00.000Z\"))).toBe(\"in 1 day\");\n expect(fromNow(new Date(\"2023-01-22T12:00:00.000Z\"))).toBe(\"in 1 week\");\n\n // Test very old dates (should use date format)\n expect(fromNow(new Date(\"2022-01-15T12:00:00.000Z\"))).toMatch(/Jan 15, 2022/);\n\n // Restore real timers\n undefined?.vi.useRealTimers();\n});\n\nexport function fromNowDetailed(date: Date): {\n result: string,\n /**\n * May be Infinity if the result will never change.\n */\n secondsUntilChange: number,\n} {\n if (!(date instanceof Date)) {\n throw new Error(`fromNow only accepts Date objects (received: ${date})`);\n }\n\n const now = new Date();\n const elapsed = now.getTime() - date.getTime();\n\n let remainingInUnit = Math.abs(elapsed) / 1000;\n if (remainingInUnit < 15) {\n return {\n result: 'just now',\n secondsUntilChange: 15 - remainingInUnit,\n };\n }\n let unitInSeconds = 1;\n for (const [nextUnitSize, unitName] of agoUnits) {\n const rounded = Math.round(remainingInUnit);\n if (rounded < nextUnitSize) {\n if (elapsed < 0) {\n return {\n result: `in ${rounded} ${unitName}${rounded === 1 ? '' : 's'}`,\n secondsUntilChange: remainder((remainingInUnit - rounded + 0.5) * unitInSeconds, unitInSeconds),\n };\n } else {\n return {\n result: `${rounded} ${unitName}${rounded === 1 ? '' : 's'} ago`,\n secondsUntilChange: remainder((rounded - remainingInUnit - 0.5) * unitInSeconds, unitInSeconds),\n };\n }\n }\n unitInSeconds *= nextUnitSize;\n remainingInUnit /= nextUnitSize;\n }\n\n return {\n result: date.toLocaleDateString('en-US', { year: 'numeric', month: 'short', day: 'numeric' }),\n secondsUntilChange: Infinity,\n };\n}\n\n/**\n * Returns a string representation of the given date in the format expected by the `datetime-local` input type.\n */\nexport function getInputDatetimeLocalString(date: Date): string {\n date = new Date(date);\n date.setMinutes(date.getMinutes() - date.getTimezoneOffset());\n return date.toISOString().slice(0, 19);\n}\n\nundefined?.test(\"getInputDatetimeLocalString\", ({ expect }) => {\n // Use Vitest's fake timers to ensure consistent timezone behavior\n undefined?.vi.useFakeTimers();\n\n // Test with a specific date\n const mockDate = new Date(\"2023-01-15T12:30:45.000Z\");\n const result = getInputDatetimeLocalString(mockDate);\n\n // The result should be in the format YYYY-MM-DDTHH:MM:SS\n expect(result).toMatch(/^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}$/);\n\n // Test with different dates\n const dates = [\n new Date(\"2023-01-01T00:00:00.000Z\"),\n new Date(\"2023-06-15T23:59:59.000Z\"),\n new Date(\"2023-12-31T12:34:56.000Z\"),\n ];\n\n for (const date of dates) {\n const result = getInputDatetimeLocalString(date);\n expect(result).toMatch(/^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}$/);\n }\n\n // Restore real timers\n undefined?.vi.useRealTimers();\n});\n\n\nexport type Interval = [number, 'millisecond' | 'second' | 'minute' | 'hour' | 'day' | 'week' | 'month' | 'year'];\nexport type DayInterval = [number, 'day' | 'week' | 'month' | 'year'];\n\nfunction applyInterval(date: Date, times: number, interval: Interval): Date {\n if (!intervalSchema.isValidSync(interval)) {\n throw new StackAssertionError(`Invalid interval`, { interval });\n }\n const [amount, unit] = interval;\n switch (unit) {\n case 'millisecond': {\n date.setMilliseconds(date.getMilliseconds() + amount * times);\n break;\n }\n case 'second': {\n date.setSeconds(date.getSeconds() + amount * times);\n break;\n }\n case 'minute': {\n date.setMinutes(date.getMinutes() + amount * times);\n break;\n }\n case 'hour': {\n date.setHours(date.getHours() + amount * times);\n break;\n }\n case 'day': {\n date.setDate(date.getDate() + amount * times);\n break;\n }\n case 'week': {\n date.setDate(date.getDate() + amount * times * 7);\n break;\n }\n case 'month': {\n date.setMonth(date.getMonth() + amount * times);\n break;\n }\n case 'year': {\n date.setFullYear(date.getFullYear() + amount * times);\n break;\n }\n default: {\n throw new StackAssertionError(`Invalid interval despite schema validation`, { interval });\n }\n }\n return date;\n}\n\nexport function subtractInterval(date: Date, interval: Interval): Date {\n return applyInterval(date, -1, interval);\n}\n\nexport function addInterval(date: Date, interval: Interval): Date {\n return applyInterval(date, 1, interval);\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/utils/dates.tsx"],"sourcesContent":["import { intervalSchema } from \"../schema-fields\";\nimport { StackAssertionError } from \"./errors\";\nimport { remainder } from \"./math\";\n\nexport function isWeekend(date: Date): boolean {\n return date.getDay() === 0 || date.getDay() === 6;\n}\n\nundefined?.test(\"isWeekend\", ({ expect }) => {\n // Sunday (day 0)\n expect(isWeekend(new Date(2023, 0, 1))).toBe(true);\n // Saturday (day 6)\n expect(isWeekend(new Date(2023, 0, 7))).toBe(true);\n // Monday (day 1)\n expect(isWeekend(new Date(2023, 0, 2))).toBe(false);\n // Friday (day 5)\n expect(isWeekend(new Date(2023, 0, 6))).toBe(false);\n});\n\nconst agoUnits = [\n [60, 'second'],\n [60, 'minute'],\n [24, 'hour'],\n [7, 'day'],\n [5, 'week'],\n] as const;\n\nexport function fromNow(date: Date): string {\n return fromNowDetailed(date).result;\n}\n\nundefined?.test(\"fromNow\", ({ expect }) => {\n // Set a fixed date for testing\n const fixedDate = new Date(\"2023-01-15T12:00:00.000Z\");\n\n // Use Vitest's fake timers\n undefined?.vi.useFakeTimers();\n undefined?.vi.setSystemTime(fixedDate);\n\n // Test past times\n expect(fromNow(new Date(\"2023-01-15T11:59:50.000Z\"))).toBe(\"just now\");\n expect(fromNow(new Date(\"2023-01-15T11:59:00.000Z\"))).toBe(\"1 minute ago\");\n expect(fromNow(new Date(\"2023-01-15T11:00:00.000Z\"))).toBe(\"1 hour ago\");\n expect(fromNow(new Date(\"2023-01-14T12:00:00.000Z\"))).toBe(\"1 day ago\");\n expect(fromNow(new Date(\"2023-01-08T12:00:00.000Z\"))).toBe(\"1 week ago\");\n\n // Test future times\n expect(fromNow(new Date(\"2023-01-15T12:00:10.000Z\"))).toBe(\"just now\");\n expect(fromNow(new Date(\"2023-01-15T12:01:00.000Z\"))).toBe(\"in 1 minute\");\n expect(fromNow(new Date(\"2023-01-15T13:00:00.000Z\"))).toBe(\"in 1 hour\");\n expect(fromNow(new Date(\"2023-01-16T12:00:00.000Z\"))).toBe(\"in 1 day\");\n expect(fromNow(new Date(\"2023-01-22T12:00:00.000Z\"))).toBe(\"in 1 week\");\n\n // Test very old dates (should use date format)\n expect(fromNow(new Date(\"2022-01-15T12:00:00.000Z\"))).toMatch(/Jan 15, 2022/);\n\n // Restore real timers\n undefined?.vi.useRealTimers();\n});\n\nexport function fromNowDetailed(date: Date): {\n result: string,\n /**\n * May be Infinity if the result will never change.\n */\n secondsUntilChange: number,\n} {\n if (!(date instanceof Date)) {\n throw new Error(`fromNow only accepts Date objects (received: ${date})`);\n }\n\n const now = new Date();\n const elapsed = now.getTime() - date.getTime();\n\n let remainingInUnit = Math.abs(elapsed) / 1000;\n if (remainingInUnit < 15) {\n return {\n result: 'just now',\n secondsUntilChange: 15 - remainingInUnit,\n };\n }\n let unitInSeconds = 1;\n for (const [nextUnitSize, unitName] of agoUnits) {\n const rounded = Math.round(remainingInUnit);\n if (rounded < nextUnitSize) {\n if (elapsed < 0) {\n return {\n result: `in ${rounded} ${unitName}${rounded === 1 ? '' : 's'}`,\n secondsUntilChange: remainder((remainingInUnit - rounded + 0.5) * unitInSeconds, unitInSeconds),\n };\n } else {\n return {\n result: `${rounded} ${unitName}${rounded === 1 ? '' : 's'} ago`,\n secondsUntilChange: remainder((rounded - remainingInUnit - 0.5) * unitInSeconds, unitInSeconds),\n };\n }\n }\n unitInSeconds *= nextUnitSize;\n remainingInUnit /= nextUnitSize;\n }\n\n return {\n result: date.toLocaleDateString('en-US', { year: 'numeric', month: 'short', day: 'numeric' }),\n secondsUntilChange: Infinity,\n };\n}\n\n/**\n * Returns a string representation of the given date in the format expected by the `datetime-local` input type.\n */\nexport function getInputDatetimeLocalString(date: Date): string {\n date = new Date(date);\n date.setMinutes(date.getMinutes() - date.getTimezoneOffset());\n return date.toISOString().slice(0, 19);\n}\n\nundefined?.test(\"getInputDatetimeLocalString\", ({ expect }) => {\n // Use Vitest's fake timers to ensure consistent timezone behavior\n undefined?.vi.useFakeTimers();\n\n // Test with a specific date\n const mockDate = new Date(\"2023-01-15T12:30:45.000Z\");\n const result = getInputDatetimeLocalString(mockDate);\n\n // The result should be in the format YYYY-MM-DDTHH:MM:SS\n expect(result).toMatch(/^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}$/);\n\n // Test with different dates\n const dates = [\n new Date(\"2023-01-01T00:00:00.000Z\"),\n new Date(\"2023-06-15T23:59:59.000Z\"),\n new Date(\"2023-12-31T12:34:56.000Z\"),\n ];\n\n for (const date of dates) {\n const result = getInputDatetimeLocalString(date);\n expect(result).toMatch(/^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}$/);\n }\n\n // Restore real timers\n undefined?.vi.useRealTimers();\n});\n\n\nexport type Interval = [number, 'millisecond' | 'second' | 'minute' | 'hour' | 'day' | 'week' | 'month' | 'year'];\nexport type DayInterval = [number, 'day' | 'week' | 'month' | 'year'];\n\nfunction applyInterval(date: Date, times: number, interval: Interval): Date {\n if (!intervalSchema.isValidSync(interval)) {\n throw new StackAssertionError(`Invalid interval`, { interval });\n }\n const [amount, unit] = interval;\n switch (unit) {\n case 'millisecond': {\n date.setMilliseconds(date.getMilliseconds() + amount * times);\n break;\n }\n case 'second': {\n date.setSeconds(date.getSeconds() + amount * times);\n break;\n }\n case 'minute': {\n date.setMinutes(date.getMinutes() + amount * times);\n break;\n }\n case 'hour': {\n date.setHours(date.getHours() + amount * times);\n break;\n }\n case 'day': {\n date.setDate(date.getDate() + amount * times);\n break;\n }\n case 'week': {\n date.setDate(date.getDate() + amount * times * 7);\n break;\n }\n case 'month': {\n date.setMonth(date.getMonth() + amount * times);\n break;\n }\n case 'year': {\n date.setFullYear(date.getFullYear() + amount * times);\n break;\n }\n default: {\n throw new StackAssertionError(`Invalid interval despite schema validation`, { interval });\n }\n }\n return date;\n}\n\nexport function subtractInterval(date: Date, interval: Interval): Date {\n return applyInterval(date, -1, interval);\n}\n\nexport function addInterval(date: Date, interval: Interval): Date {\n return applyInterval(date, 1, interval);\n}\n\nexport const FAR_FUTURE_DATE = new Date(8640000000000000); // 13 Sep 275760 00:00:00 UTC\n\nfunction getMsPerDayIntervalUnit(unit: 'day' | 'week'): number {\n if (unit === 'day') {\n return 24 * 60 * 60 * 1000;\n }\n return 7 * 24 * 60 * 60 * 1000;\n}\n\n\nexport function getIntervalsElapsed(anchor: Date, to: Date, repeat: DayInterval): number {\n const [amount, unit] = repeat;\n if (to <= anchor) return 0;\n if (unit === 'day' || unit === 'week') {\n const msPerUnit = getMsPerDayIntervalUnit(unit);\n const diffMs = to.getTime() - anchor.getTime();\n return Math.floor(diffMs / (msPerUnit * amount));\n }\n if ([\"month\", \"year\"].includes(unit)) {\n let count = 0;\n let current = new Date(anchor);\n for (; ;) {\n const next = addInterval(new Date(current), [amount, unit]);\n if (next > to) break;\n current = next;\n count += 1;\n }\n return count;\n }\n return 0;\n}\n\nundefined?.test(\"getIntervalsElapsed\", ({ expect }) => {\n const anchor = new Date('2025-01-01T00:00:00.000Z');\n const to = new Date('2025-01-15T00:00:00.000Z');\n expect(getIntervalsElapsed(anchor, to, [1, 'week'])).toBe(2);\n expect(getIntervalsElapsed(anchor, to, [3, 'day'])).toBe(4);\n\n const mAnchor = new Date('2023-01-31T00:00:00.000Z');\n const mTo = new Date('2023-03-01T00:00:00.000Z');\n expect(getIntervalsElapsed(mAnchor, mTo, [1, 'month'])).toBe(0);\n\n const yAnchor = new Date('2020-01-01T00:00:00.000Z');\n const yTo = new Date('2022-06-01T00:00:00.000Z');\n expect(getIntervalsElapsed(yAnchor, yTo, [1, 'year'])).toBe(2);\n});\n"],"mappings":"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","names":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/utils/numbers.tsx"],"sourcesContent":["const magnitudes = [\n [1_000_000_000_000_000, \"trln\"],\n [1_000_000_000_000, \"bln\"],\n [1_000_000_000, \"bn\"],\n [1_000_000, \"M\"],\n [1_000, \"k\"],\n] as const;\n\nexport function prettyPrintWithMagnitudes(num: number): string {\n if (typeof num !== \"number\") throw new Error(\"Expected a number\");\n if (Number.isNaN(num)) return \"NaN\";\n if (num < 0) return \"-\" + prettyPrintWithMagnitudes(-num);\n if (!Number.isFinite(num)) return \"∞\";\n\n for (const [magnitude, suffix] of magnitudes) {\n if (num >= magnitude) {\n return toFixedMax(num / magnitude, 1) + suffix;\n }\n }\n return toFixedMax(num, 1); // Handle numbers less than 1,000 without suffix.\n}\nundefined?.test(\"prettyPrintWithMagnitudes\", ({ expect }) => {\n // Test different magnitudes\n expect(prettyPrintWithMagnitudes(1000)).toBe(\"1k\");\n expect(prettyPrintWithMagnitudes(1500)).toBe(\"1.5k\");\n expect(prettyPrintWithMagnitudes(1000000)).toBe(\"1M\");\n expect(prettyPrintWithMagnitudes(1500000)).toBe(\"1.5M\");\n expect(prettyPrintWithMagnitudes(1000000000)).toBe(\"1bn\");\n expect(prettyPrintWithMagnitudes(1500000000)).toBe(\"1.5bn\");\n expect(prettyPrintWithMagnitudes(1000000000000)).toBe(\"1bln\");\n expect(prettyPrintWithMagnitudes(1500000000000)).toBe(\"1.5bln\");\n expect(prettyPrintWithMagnitudes(1000000000000000)).toBe(\"1trln\");\n expect(prettyPrintWithMagnitudes(1500000000000000)).toBe(\"1.5trln\");\n // Test small numbers\n expect(prettyPrintWithMagnitudes(100)).toBe(\"100\");\n expect(prettyPrintWithMagnitudes(0)).toBe(\"0\");\n expect(prettyPrintWithMagnitudes(0.5)).toBe(\"0.5\");\n // Test negative numbers\n expect(prettyPrintWithMagnitudes(-1000)).toBe(\"-1k\");\n expect(prettyPrintWithMagnitudes(-1500000)).toBe(\"-1.5M\");\n // Test special cases\n expect(prettyPrintWithMagnitudes(NaN)).toBe(\"NaN\");\n expect(prettyPrintWithMagnitudes(Infinity)).toBe(\"∞\");\n expect(prettyPrintWithMagnitudes(-Infinity)).toBe(\"-∞\");\n});\n\nexport function toFixedMax(num: number, maxDecimals: number): string {\n return num.toFixed(maxDecimals).replace(/\\.?0+$/, \"\");\n}\nundefined?.test(\"toFixedMax\", ({ expect }) => {\n expect(toFixedMax(1, 2)).toBe(\"1\");\n expect(toFixedMax(1.2, 2)).toBe(\"1.2\");\n expect(toFixedMax(1.23, 2)).toBe(\"1.23\");\n expect(toFixedMax(1.234, 2)).toBe(\"1.23\");\n expect(toFixedMax(1.0, 2)).toBe(\"1\");\n expect(toFixedMax(1.20, 2)).toBe(\"1.2\");\n expect(toFixedMax(0, 2)).toBe(\"0\");\n});\n\nexport function numberCompare(a: number, b: number): number {\n return Math.sign(a - b);\n}\nundefined?.test(\"numberCompare\", ({ expect }) => {\n expect(numberCompare(1, 2)).toBe(-1);\n expect(numberCompare(2, 1)).toBe(1);\n expect(numberCompare(1, 1)).toBe(0);\n expect(numberCompare(0, 0)).toBe(0);\n expect(numberCompare(-1, -2)).toBe(1);\n expect(numberCompare(-2, -1)).toBe(-1);\n expect(numberCompare(-1, 1)).toBe(-1);\n expect(numberCompare(1, -1)).toBe(1);\n});\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAAM,aAAa;AAAA,EACjB,CAAC,MAAuB,MAAM;AAAA,EAC9B,CAAC,MAAmB,KAAK;AAAA,EACzB,CAAC,KAAe,IAAI;AAAA,EACpB,CAAC,KAAW,GAAG;AAAA,EACf,CAAC,KAAO,GAAG;AACb;AAEO,SAAS,0BAA0B,KAAqB;AAC7D,MAAI,OAAO,QAAQ,SAAU,OAAM,IAAI,MAAM,mBAAmB;AAChE,MAAI,OAAO,MAAM,GAAG,EAAG,QAAO;AAC9B,MAAI,MAAM,EAAG,QAAO,MAAM,0BAA0B,CAAC,GAAG;AACxD,MAAI,CAAC,OAAO,SAAS,GAAG,EAAG,QAAO;AAElC,aAAW,CAAC,WAAW,MAAM,KAAK,YAAY;AAC5C,QAAI,OAAO,WAAW;AACpB,aAAO,WAAW,MAAM,WAAW,CAAC,IAAI;AAAA,IAC1C;AAAA,EACF;AACA,SAAO,WAAW,KAAK,CAAC;AAC1B;
|
|
1
|
+
{"version":3,"sources":["../../src/utils/numbers.tsx"],"sourcesContent":["const magnitudes = [\n [1_000_000_000_000_000, \"trln\"],\n [1_000_000_000_000, \"bln\"],\n [1_000_000_000, \"bn\"],\n [1_000_000, \"M\"],\n [1_000, \"k\"],\n] as const;\n\nexport function prettyPrintWithMagnitudes(num: number): string {\n if (typeof num !== \"number\") throw new Error(\"Expected a number\");\n if (Number.isNaN(num)) return \"NaN\";\n if (num < 0) return \"-\" + prettyPrintWithMagnitudes(-num);\n if (!Number.isFinite(num)) return \"∞\";\n\n for (const [magnitude, suffix] of magnitudes) {\n if (num >= magnitude) {\n return toFixedMax(num / magnitude, 1) + suffix;\n }\n }\n return toFixedMax(num, 1); // Handle numbers less than 1,000 without suffix.\n}\nundefined?.test(\"prettyPrintWithMagnitudes\", ({ expect }) => {\n // Test different magnitudes\n expect(prettyPrintWithMagnitudes(999)).toBe(\"999\");\n expect(prettyPrintWithMagnitudes(1000)).toBe(\"1k\");\n expect(prettyPrintWithMagnitudes(1500)).toBe(\"1.5k\");\n expect(prettyPrintWithMagnitudes(1000000)).toBe(\"1M\");\n expect(prettyPrintWithMagnitudes(1500000)).toBe(\"1.5M\");\n expect(prettyPrintWithMagnitudes(1000000000)).toBe(\"1bn\");\n expect(prettyPrintWithMagnitudes(1500000000)).toBe(\"1.5bn\");\n expect(prettyPrintWithMagnitudes(1000000000000)).toBe(\"1bln\");\n expect(prettyPrintWithMagnitudes(1500000000000)).toBe(\"1.5bln\");\n expect(prettyPrintWithMagnitudes(1000000000000000)).toBe(\"1trln\");\n expect(prettyPrintWithMagnitudes(1500000000000000)).toBe(\"1.5trln\");\n // Test small numbers\n expect(prettyPrintWithMagnitudes(100)).toBe(\"100\");\n expect(prettyPrintWithMagnitudes(0)).toBe(\"0\");\n expect(prettyPrintWithMagnitudes(0.5)).toBe(\"0.5\");\n // Test negative numbers\n expect(prettyPrintWithMagnitudes(-1000)).toBe(\"-1k\");\n expect(prettyPrintWithMagnitudes(-1500000)).toBe(\"-1.5M\");\n // Test special cases\n expect(prettyPrintWithMagnitudes(NaN)).toBe(\"NaN\");\n expect(prettyPrintWithMagnitudes(Infinity)).toBe(\"∞\");\n expect(prettyPrintWithMagnitudes(-Infinity)).toBe(\"-∞\");\n});\n\nexport function toFixedMax(num: number, maxDecimals: number): string {\n return num.toFixed(maxDecimals).replace(/\\.?0+$/, \"\");\n}\nundefined?.test(\"toFixedMax\", ({ expect }) => {\n expect(toFixedMax(1, 2)).toBe(\"1\");\n expect(toFixedMax(1.2, 2)).toBe(\"1.2\");\n expect(toFixedMax(1.23, 2)).toBe(\"1.23\");\n expect(toFixedMax(1.234, 2)).toBe(\"1.23\");\n expect(toFixedMax(1.0, 2)).toBe(\"1\");\n expect(toFixedMax(1.20, 2)).toBe(\"1.2\");\n expect(toFixedMax(0, 2)).toBe(\"0\");\n});\n\nexport function numberCompare(a: number, b: number): number {\n return Math.sign(a - b);\n}\nundefined?.test(\"numberCompare\", ({ expect }) => {\n expect(numberCompare(1, 2)).toBe(-1);\n expect(numberCompare(2, 1)).toBe(1);\n expect(numberCompare(1, 1)).toBe(0);\n expect(numberCompare(0, 0)).toBe(0);\n expect(numberCompare(-1, -2)).toBe(1);\n expect(numberCompare(-2, -1)).toBe(-1);\n expect(numberCompare(-1, 1)).toBe(-1);\n expect(numberCompare(1, -1)).toBe(1);\n});\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAAM,aAAa;AAAA,EACjB,CAAC,MAAuB,MAAM;AAAA,EAC9B,CAAC,MAAmB,KAAK;AAAA,EACzB,CAAC,KAAe,IAAI;AAAA,EACpB,CAAC,KAAW,GAAG;AAAA,EACf,CAAC,KAAO,GAAG;AACb;AAEO,SAAS,0BAA0B,KAAqB;AAC7D,MAAI,OAAO,QAAQ,SAAU,OAAM,IAAI,MAAM,mBAAmB;AAChE,MAAI,OAAO,MAAM,GAAG,EAAG,QAAO;AAC9B,MAAI,MAAM,EAAG,QAAO,MAAM,0BAA0B,CAAC,GAAG;AACxD,MAAI,CAAC,OAAO,SAAS,GAAG,EAAG,QAAO;AAElC,aAAW,CAAC,WAAW,MAAM,KAAK,YAAY;AAC5C,QAAI,OAAO,WAAW;AACpB,aAAO,WAAW,MAAM,WAAW,CAAC,IAAI;AAAA,IAC1C;AAAA,EACF;AACA,SAAO,WAAW,KAAK,CAAC;AAC1B;AA2BO,SAAS,WAAW,KAAa,aAA6B;AACnE,SAAO,IAAI,QAAQ,WAAW,EAAE,QAAQ,UAAU,EAAE;AACtD;AAWO,SAAS,cAAc,GAAW,GAAmB;AAC1D,SAAO,KAAK,KAAK,IAAI,CAAC;AACxB;","names":[]}
|
package/dist/utils/react.js
CHANGED
|
@@ -95,13 +95,17 @@ function useRefState(initialValue) {
|
|
|
95
95
|
const ref = import_react.default.useRef(initialValue);
|
|
96
96
|
const setValue = import_react.default.useCallback((updater) => {
|
|
97
97
|
const value = typeof updater === "function" ? updater(ref.current) : updater;
|
|
98
|
-
|
|
98
|
+
console.log("setValue", ref.current);
|
|
99
99
|
ref.current = value;
|
|
100
|
+
console.log("setValue", ref.current);
|
|
101
|
+
setState(value);
|
|
100
102
|
}, []);
|
|
101
103
|
const res = import_react.default.useMemo(() => ({
|
|
102
|
-
current
|
|
104
|
+
get current() {
|
|
105
|
+
return ref.current;
|
|
106
|
+
},
|
|
103
107
|
set: setValue
|
|
104
|
-
}), [
|
|
108
|
+
}), [setValue]);
|
|
105
109
|
return res;
|
|
106
110
|
}
|
|
107
111
|
function mapRefState(refState, mapper, reverseMapper) {
|