@evolu/common 8.9.0 → 8.10.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.
Files changed (233) hide show
  1. package/dist/src/Bytes.d.ts +647 -0
  2. package/dist/src/Bytes.d.ts.map +1 -0
  3. package/dist/src/{Binary.js → Bytes.js} +266 -16
  4. package/dist/src/Config.d.ts +142 -0
  5. package/dist/src/Config.d.ts.map +1 -0
  6. package/dist/src/Config.js +181 -0
  7. package/dist/src/Fs.d.ts +302 -0
  8. package/dist/src/Fs.d.ts.map +1 -0
  9. package/dist/src/Fs.js +111 -0
  10. package/dist/src/Number.d.ts +50 -7
  11. package/dist/src/Number.d.ts.map +1 -1
  12. package/dist/src/Number.js +47 -8
  13. package/dist/src/Object.d.ts +32 -0
  14. package/dist/src/Object.d.ts.map +1 -1
  15. package/dist/src/Object.js +41 -0
  16. package/dist/src/Sqlite.js +1 -1
  17. package/dist/src/Time.d.ts +74 -12
  18. package/dist/src/Time.d.ts.map +1 -1
  19. package/dist/src/Time.js +40 -2
  20. package/dist/src/Type.d.ts +1451 -79
  21. package/dist/src/Type.d.ts.map +1 -1
  22. package/dist/src/Type.js +1303 -96
  23. package/dist/src/index.d.ts +3 -1
  24. package/dist/src/index.d.ts.map +1 -1
  25. package/dist/src/index.js +3 -1
  26. package/dist/src/intl/_en.d.ts +24 -1
  27. package/dist/src/intl/_en.d.ts.map +1 -1
  28. package/dist/src/intl/_en.js +20 -0
  29. package/dist/src/intl/ar.d.ts +24 -1
  30. package/dist/src/intl/ar.d.ts.map +1 -1
  31. package/dist/src/intl/ar.js +20 -0
  32. package/dist/src/intl/bn.d.ts +24 -1
  33. package/dist/src/intl/bn.d.ts.map +1 -1
  34. package/dist/src/intl/bn.js +20 -0
  35. package/dist/src/intl/ca.d.ts +24 -1
  36. package/dist/src/intl/ca.d.ts.map +1 -1
  37. package/dist/src/intl/ca.js +20 -0
  38. package/dist/src/intl/cs.d.ts +24 -1
  39. package/dist/src/intl/cs.d.ts.map +1 -1
  40. package/dist/src/intl/cs.js +20 -0
  41. package/dist/src/intl/da.d.ts +24 -1
  42. package/dist/src/intl/da.d.ts.map +1 -1
  43. package/dist/src/intl/da.js +20 -0
  44. package/dist/src/intl/de.d.ts +24 -1
  45. package/dist/src/intl/de.d.ts.map +1 -1
  46. package/dist/src/intl/de.js +20 -0
  47. package/dist/src/intl/el.d.ts +24 -1
  48. package/dist/src/intl/el.d.ts.map +1 -1
  49. package/dist/src/intl/el.js +20 -0
  50. package/dist/src/intl/es.d.ts +24 -1
  51. package/dist/src/intl/es.d.ts.map +1 -1
  52. package/dist/src/intl/es.js +20 -0
  53. package/dist/src/intl/fa.d.ts +24 -1
  54. package/dist/src/intl/fa.d.ts.map +1 -1
  55. package/dist/src/intl/fa.js +20 -0
  56. package/dist/src/intl/fi.d.ts +24 -1
  57. package/dist/src/intl/fi.d.ts.map +1 -1
  58. package/dist/src/intl/fi.js +20 -0
  59. package/dist/src/intl/fil.d.ts +24 -1
  60. package/dist/src/intl/fil.d.ts.map +1 -1
  61. package/dist/src/intl/fil.js +20 -0
  62. package/dist/src/intl/fr.d.ts +24 -1
  63. package/dist/src/intl/fr.d.ts.map +1 -1
  64. package/dist/src/intl/fr.js +20 -0
  65. package/dist/src/intl/he.d.ts +24 -1
  66. package/dist/src/intl/he.d.ts.map +1 -1
  67. package/dist/src/intl/he.js +20 -0
  68. package/dist/src/intl/hi.d.ts +24 -1
  69. package/dist/src/intl/hi.d.ts.map +1 -1
  70. package/dist/src/intl/hi.js +20 -0
  71. package/dist/src/intl/hr.d.ts +24 -1
  72. package/dist/src/intl/hr.d.ts.map +1 -1
  73. package/dist/src/intl/hr.js +20 -0
  74. package/dist/src/intl/hu.d.ts +22 -1
  75. package/dist/src/intl/hu.d.ts.map +1 -1
  76. package/dist/src/intl/hu.js +18 -0
  77. package/dist/src/intl/id.d.ts +24 -1
  78. package/dist/src/intl/id.d.ts.map +1 -1
  79. package/dist/src/intl/id.js +20 -0
  80. package/dist/src/intl/it.d.ts +24 -1
  81. package/dist/src/intl/it.d.ts.map +1 -1
  82. package/dist/src/intl/it.js +20 -0
  83. package/dist/src/intl/ja.d.ts +24 -1
  84. package/dist/src/intl/ja.d.ts.map +1 -1
  85. package/dist/src/intl/ja.js +20 -0
  86. package/dist/src/intl/ko.d.ts +24 -1
  87. package/dist/src/intl/ko.d.ts.map +1 -1
  88. package/dist/src/intl/ko.js +20 -0
  89. package/dist/src/intl/ml.d.ts +24 -1
  90. package/dist/src/intl/ml.d.ts.map +1 -1
  91. package/dist/src/intl/ml.js +20 -0
  92. package/dist/src/intl/mr.d.ts +24 -1
  93. package/dist/src/intl/mr.d.ts.map +1 -1
  94. package/dist/src/intl/mr.js +20 -0
  95. package/dist/src/intl/ms.d.ts +24 -1
  96. package/dist/src/intl/ms.d.ts.map +1 -1
  97. package/dist/src/intl/ms.js +20 -0
  98. package/dist/src/intl/nb.d.ts +22 -1
  99. package/dist/src/intl/nb.d.ts.map +1 -1
  100. package/dist/src/intl/nb.js +18 -0
  101. package/dist/src/intl/nl.d.ts +24 -1
  102. package/dist/src/intl/nl.d.ts.map +1 -1
  103. package/dist/src/intl/nl.js +20 -0
  104. package/dist/src/intl/pa.d.ts +24 -1
  105. package/dist/src/intl/pa.d.ts.map +1 -1
  106. package/dist/src/intl/pa.js +20 -0
  107. package/dist/src/intl/pl.d.ts +23 -0
  108. package/dist/src/intl/pl.d.ts.map +1 -1
  109. package/dist/src/intl/pl.js +20 -0
  110. package/dist/src/intl/pt-BR.d.ts +24 -1
  111. package/dist/src/intl/pt-BR.d.ts.map +1 -1
  112. package/dist/src/intl/pt-BR.js +20 -0
  113. package/dist/src/intl/pt.d.ts +24 -1
  114. package/dist/src/intl/pt.d.ts.map +1 -1
  115. package/dist/src/intl/pt.js +20 -0
  116. package/dist/src/intl/ro.d.ts +24 -1
  117. package/dist/src/intl/ro.d.ts.map +1 -1
  118. package/dist/src/intl/ro.js +20 -0
  119. package/dist/src/intl/sk.d.ts +24 -1
  120. package/dist/src/intl/sk.d.ts.map +1 -1
  121. package/dist/src/intl/sk.js +20 -0
  122. package/dist/src/intl/sl.d.ts +24 -1
  123. package/dist/src/intl/sl.d.ts.map +1 -1
  124. package/dist/src/intl/sl.js +20 -0
  125. package/dist/src/intl/sv.d.ts +24 -1
  126. package/dist/src/intl/sv.d.ts.map +1 -1
  127. package/dist/src/intl/sv.js +20 -0
  128. package/dist/src/intl/sw.d.ts +21 -0
  129. package/dist/src/intl/sw.d.ts.map +1 -1
  130. package/dist/src/intl/sw.js +18 -0
  131. package/dist/src/intl/ta.d.ts +24 -1
  132. package/dist/src/intl/ta.d.ts.map +1 -1
  133. package/dist/src/intl/ta.js +20 -0
  134. package/dist/src/intl/te.d.ts +24 -1
  135. package/dist/src/intl/te.d.ts.map +1 -1
  136. package/dist/src/intl/te.js +20 -0
  137. package/dist/src/intl/th.d.ts +24 -1
  138. package/dist/src/intl/th.d.ts.map +1 -1
  139. package/dist/src/intl/th.js +20 -0
  140. package/dist/src/intl/tr.d.ts +24 -1
  141. package/dist/src/intl/tr.d.ts.map +1 -1
  142. package/dist/src/intl/tr.js +20 -0
  143. package/dist/src/intl/uk.d.ts +80 -57
  144. package/dist/src/intl/uk.d.ts.map +1 -1
  145. package/dist/src/intl/uk.js +174 -149
  146. package/dist/src/intl/ur.d.ts +24 -1
  147. package/dist/src/intl/ur.d.ts.map +1 -1
  148. package/dist/src/intl/ur.js +20 -0
  149. package/dist/src/intl/vi.d.ts +24 -1
  150. package/dist/src/intl/vi.d.ts.map +1 -1
  151. package/dist/src/intl/vi.js +20 -0
  152. package/dist/src/intl/zh-CN.d.ts +24 -1
  153. package/dist/src/intl/zh-CN.d.ts.map +1 -1
  154. package/dist/src/intl/zh-CN.js +20 -0
  155. package/dist/src/intl/zh-TW.d.ts +24 -1
  156. package/dist/src/intl/zh-TW.d.ts.map +1 -1
  157. package/dist/src/intl/zh-TW.js +20 -0
  158. package/dist/src/local-first/Protocol.d.ts +1 -1
  159. package/dist/src/local-first/Protocol.d.ts.map +1 -1
  160. package/dist/src/local-first/Protocol.js +1 -1
  161. package/dist/src/local-first/Schema.d.ts +10 -0
  162. package/dist/src/local-first/Schema.d.ts.map +1 -1
  163. package/dist/src/local-first/Storage.js +1 -1
  164. package/dist/src/local-first/Timestamp.js +1 -1
  165. package/package.json +1 -1
  166. package/src/{Binary.test.ts → Bytes.test.ts} +286 -1
  167. package/src/{Binary.ts → Bytes.ts} +652 -21
  168. package/src/Config.test.ts +672 -0
  169. package/src/Config.ts +410 -0
  170. package/src/Fs.test.ts +105 -0
  171. package/src/Fs.ts +414 -0
  172. package/src/Number.test.ts +82 -18
  173. package/src/Number.ts +76 -8
  174. package/src/Object.test.ts +115 -1
  175. package/src/Object.ts +44 -0
  176. package/src/Sqlite.ts +1 -1
  177. package/src/Time.test.ts +82 -11
  178. package/src/Time.ts +141 -16
  179. package/src/Type.test.ts +3114 -157
  180. package/src/Type.ts +2553 -196
  181. package/src/index.ts +3 -1
  182. package/src/intl/_en.ts +70 -0
  183. package/src/intl/ar.ts +71 -0
  184. package/src/intl/bn.ts +70 -0
  185. package/src/intl/ca.ts +70 -0
  186. package/src/intl/cs.ts +70 -0
  187. package/src/intl/da.ts +70 -0
  188. package/src/intl/de.ts +70 -0
  189. package/src/intl/el.ts +70 -0
  190. package/src/intl/es.ts +70 -0
  191. package/src/intl/fa.ts +70 -0
  192. package/src/intl/fi.ts +70 -0
  193. package/src/intl/fil.ts +70 -0
  194. package/src/intl/fr.ts +70 -0
  195. package/src/intl/he.ts +70 -0
  196. package/src/intl/hi.ts +70 -0
  197. package/src/intl/hr.ts +70 -0
  198. package/src/intl/hu.ts +69 -0
  199. package/src/intl/id.ts +70 -0
  200. package/src/intl/intl.test.ts +819 -1
  201. package/src/intl/it.ts +70 -0
  202. package/src/intl/ja.ts +70 -0
  203. package/src/intl/ko.ts +68 -0
  204. package/src/intl/ml.ts +70 -0
  205. package/src/intl/mr.ts +70 -0
  206. package/src/intl/ms.ts +71 -0
  207. package/src/intl/nb.ts +69 -0
  208. package/src/intl/nl.ts +70 -0
  209. package/src/intl/pa.ts +70 -0
  210. package/src/intl/pl.ts +63 -0
  211. package/src/intl/pt-BR.ts +70 -0
  212. package/src/intl/pt.ts +71 -0
  213. package/src/intl/ro.ts +70 -0
  214. package/src/intl/sk.ts +71 -0
  215. package/src/intl/sl.ts +70 -0
  216. package/src/intl/sv.ts +70 -0
  217. package/src/intl/sw.ts +62 -0
  218. package/src/intl/ta.ts +70 -0
  219. package/src/intl/te.ts +70 -0
  220. package/src/intl/th.ts +68 -0
  221. package/src/intl/tr.ts +70 -0
  222. package/src/intl/uk.ts +228 -155
  223. package/src/intl/ur.ts +70 -0
  224. package/src/intl/vi.ts +70 -0
  225. package/src/intl/zh-CN.ts +68 -0
  226. package/src/intl/zh-TW.ts +68 -0
  227. package/src/local-first/Protocol.test.ts +1 -1
  228. package/src/local-first/Protocol.ts +1 -1
  229. package/src/local-first/Schema.ts +11 -0
  230. package/src/local-first/Storage.ts +1 -1
  231. package/src/local-first/Timestamp.ts +1 -1
  232. package/dist/src/Binary.d.ts +0 -254
  233. package/dist/src/Binary.d.ts.map +0 -1
@@ -2,7 +2,10 @@ import { describe, it, mock } from "node:test";
2
2
  import {
3
3
  assertEqual,
4
4
  assertEqualBytes,
5
+ assertErr,
6
+ assertFalse,
5
7
  assertInstanceOf,
8
+ assertOk,
6
9
  assertSame,
7
10
  assertThrowsInstanceOf,
8
11
  assertTrue,
@@ -10,6 +13,16 @@ import {
10
13
 
11
14
  import {
12
15
  BufferError,
16
+ ByteLength,
17
+ ByteLengthFromString,
18
+ ByteSizeLiteral,
19
+ ByteSizeLiteralBytes,
20
+ ByteSizeLiteralGiB,
21
+ ByteSizeLiteralKiB,
22
+ ByteSizeLiteralMiB,
23
+ ByteSizeLiteralTiB,
24
+ byteSizeToByteLength,
25
+ type ByteSize,
13
26
  createBuffer,
14
27
  createRunLengthEncoder,
15
28
  decodeFlags,
@@ -25,7 +38,7 @@ import {
25
38
  encodeNonNegativeInt,
26
39
  encodeNumber,
27
40
  encodeString,
28
- } from "./Binary.ts";
41
+ } from "./Bytes.ts";
29
42
  import {
30
43
  assertType,
31
44
  FiniteNumber,
@@ -34,7 +47,19 @@ import {
34
47
  Object,
35
48
  PositiveInt,
36
49
  zeroNonNegativeInt,
50
+ localizeTypes,
51
+ optional,
52
+ typeErrorToIssues,
53
+ withDefault,
54
+ type InferErrors,
37
55
  } from "./Type.ts";
56
+ import { env } from "./Config.ts";
57
+ import {
58
+ formatByteSizeLiteralError,
59
+ formatObjectError,
60
+ formatStringError,
61
+ formatIdentifierError,
62
+ } from "./intl/cs.ts";
38
63
 
39
64
  const encodeJson = (value: unknown): Uint8Array => {
40
65
  const result = JsonValue.fromUnknown(value);
@@ -1126,3 +1151,263 @@ describe("JSON binary codec", () => {
1126
1151
  assertTrue(error.message.includes("Invalid MessagePack data"));
1127
1152
  });
1128
1153
  });
1154
+
1155
+ describe("ByteLength", () => {
1156
+ it("accepts non-negative safe integers", () => {
1157
+ assertOk(ByteLength.fromUnknown(0), 0);
1158
+ assertOk(ByteLength.fromUnknown(1024), 1024);
1159
+
1160
+ const negativeZero = ByteLength.fromUnknown(-0);
1161
+ assertErr(negativeZero, { type: "ByteLength", value: -0 });
1162
+ assertEqual(
1163
+ ByteLength.formatError(negativeZero.error),
1164
+ "The value -0 is not a byte length. Use 0 instead.",
1165
+ );
1166
+ assertFalse(ByteLength.is(-0));
1167
+
1168
+ assertErr(ByteLength.fromUnknown(-1));
1169
+ assertErr(ByteLength.fromUnknown(1.5));
1170
+ assertErr(ByteLength.fromUnknown("1024"));
1171
+ });
1172
+ });
1173
+
1174
+ describe("ByteSizeLiteral", () => {
1175
+ it("reports its own error and retains the underlying union failure", () => {
1176
+ for (const value of ["1MB", "", "1024KiB", 1024, null, undefined]) {
1177
+ const result = ByteSizeLiteral.fromUnknown(value);
1178
+ assertErr(result);
1179
+ assertType<typeof result.error, InferErrors<typeof ByteSizeLiteral>>();
1180
+ assertEqual(result.error.type, "ByteSizeLiteral");
1181
+ assertSame(result.error.value, value);
1182
+ assertEqual(result.error.cause.type, "Union");
1183
+ assertEqual(result.error.cause.errors.length, 1);
1184
+ assertEqual(result.error.cause.errors[0].index, 0);
1185
+ assertEqual(result.error.cause.errors[0].error.type, "Union");
1186
+ }
1187
+
1188
+ const result = ByteSizeLiteral.fromUnknown("1MB", { errors: "all" });
1189
+ assertErr(result);
1190
+ assertEqual(result.error.cause.errors.length, 5);
1191
+ const message =
1192
+ 'The value "1MB" is not a byte-size literal. Use a value such as "512KiB" or "1MiB".';
1193
+ assertEqual(ByteSizeLiteral.formatError(result.error), message);
1194
+ assertEqual(typeErrorToIssues(ByteSizeLiteral, result.error), [
1195
+ { path: [], message },
1196
+ ]);
1197
+ });
1198
+
1199
+ it("preserves literal types and identity encoding", () => {
1200
+ // @ts-expect-error ByteSizeLiteral Input accepts binary-unit literals, not "1MB".
1201
+ const _invalidInput: typeof ByteSizeLiteral.Input = "1MB";
1202
+ assertType<
1203
+ InferErrors<typeof ByteSizeLiteral>["type"],
1204
+ "ByteSizeLiteral"
1205
+ >();
1206
+ assertEqual(ByteSizeLiteral.name, "ByteSizeLiteral");
1207
+ assertOk(ByteSizeLiteral.from("512KiB"), "512KiB");
1208
+ assertEqual(ByteSizeLiteral.to("512KiB"), "512KiB");
1209
+ });
1210
+
1211
+ it("preserves localized messages and ENV paths through composition", async () => {
1212
+ const { custom } = localizeTypes(
1213
+ { Size: ByteSizeLiteral },
1214
+ { custom: { ByteSizeLiteral: () => "Custom byte size." } },
1215
+ );
1216
+ const Env = env({
1217
+ APP: { quota: withDefault(optional(custom.Size), "1MiB") },
1218
+ });
1219
+ const { cs } = localizeTypes(
1220
+ { Env },
1221
+ {
1222
+ cs: {
1223
+ ByteSizeLiteral: formatByteSizeLiteralError,
1224
+ Object: formatObjectError,
1225
+ String: formatStringError,
1226
+ CamelCaseIdentifier: formatIdentifierError,
1227
+ },
1228
+ },
1229
+ );
1230
+ assertEqual(cs.Env.orThrow({}).quota, "1MiB");
1231
+ const supplied = cs.Env.orThrow({ APP_QUOTA: "512KiB" });
1232
+ assertEqual({ ...cs.Env.to(supplied) }, { APP_QUOTA: "512KiB" });
1233
+ const result = cs.Env.fromUnknown({ APP_QUOTA: "1MB" });
1234
+ assertErr(result);
1235
+ const message =
1236
+ 'Hodnota "1MB" není literál velikosti v bajtech. Použijte hodnotu jako "512KiB" nebo "1MiB".';
1237
+ assertEqual(cs.Env.formatError(result.error), message);
1238
+ assertEqual(typeErrorToIssues(cs.Env, result.error), [
1239
+ { path: ["APP_QUOTA"], message },
1240
+ ]);
1241
+ assertEqual(await cs.Env["~standard"].validate({ APP_QUOTA: "1MB" }), {
1242
+ issues: [{ path: ["APP_QUOTA"], message }],
1243
+ });
1244
+ assertEqual(Env.formatError(result.error), "Custom byte size.");
1245
+ });
1246
+
1247
+ it("accepts each unit within its bounds", () => {
1248
+ for (const value of [
1249
+ "0B",
1250
+ "9B",
1251
+ "10B",
1252
+ "999B",
1253
+ "1000B",
1254
+ "1019B",
1255
+ "1020B",
1256
+ "1023B",
1257
+ ])
1258
+ assertOk(ByteSizeLiteralBytes.fromUnknown(value), value);
1259
+
1260
+ const units = [
1261
+ ["KiB", ByteSizeLiteralKiB],
1262
+ ["MiB", ByteSizeLiteralMiB],
1263
+ ["GiB", ByteSizeLiteralGiB],
1264
+ ["TiB", ByteSizeLiteralTiB],
1265
+ ] as const;
1266
+
1267
+ for (const [unit, Type] of units) {
1268
+ for (const number of [
1269
+ "1",
1270
+ "99",
1271
+ "999",
1272
+ "1000",
1273
+ "1019",
1274
+ "1020",
1275
+ "1023",
1276
+ "1.5",
1277
+ "99.5",
1278
+ "1023.5",
1279
+ ]) {
1280
+ assertOk(Type.fromUnknown(`${number}${unit}`), `${number}${unit}`);
1281
+ assertTrue(ByteSizeLiteral.is(`${number}${unit}`));
1282
+ }
1283
+ for (const number of ["0", "0.5", "1.1", "1.0", "1024", "1024.5", "01"])
1284
+ assertFalse(ByteSizeLiteral.is(`${number}${unit}`));
1285
+ }
1286
+ });
1287
+
1288
+ it("rejects other units, spacing, letter case, and redundant forms", () => {
1289
+ for (const value of [
1290
+ "1MB",
1291
+ "1kib",
1292
+ "1 KiB",
1293
+ "1KiB ",
1294
+ "1024B",
1295
+ "0.5B",
1296
+ "-1B",
1297
+ "1",
1298
+ "",
1299
+ ])
1300
+ assertFalse(ByteSizeLiteral.is(value));
1301
+ });
1302
+
1303
+ it("is checked at compile time", () => {
1304
+ const literal: ByteSizeLiteral = "1023TiB";
1305
+ assertEqual(literal, "1023TiB");
1306
+ assertType<
1307
+ Extract<ByteSizeLiteral, "1023TiB" | "1000B" | "0B">,
1308
+ "1023TiB" | "1000B" | "0B"
1309
+ >();
1310
+ assertType<Extract<ByteSizeLiteral, "1MB" | "1.1KiB" | "1024KiB">, never>();
1311
+
1312
+ // @ts-expect-error Type '"1MB"' is not assignable to type 'ByteSizeLiteral'.
1313
+ const _decimalUnit: ByteSizeLiteral = "1MB";
1314
+ // @ts-expect-error Type '"1.1KiB"' is not assignable to type 'ByteSizeLiteral'.
1315
+ const _fraction: ByteSizeLiteral = "1.1KiB";
1316
+ // @ts-expect-error Type '"1024KiB"' is not assignable to type 'ByteSizeLiteral'.
1317
+ const _redundantUnit: ByteSizeLiteral = "1024KiB";
1318
+ // @ts-expect-error Type 'number' is not assignable to type 'ByteSize'.
1319
+ const _unbranded: ByteSize = 1024;
1320
+ });
1321
+ });
1322
+
1323
+ describe("byteSizeToByteLength", () => {
1324
+ it("converts exactly and passes a ByteLength through", () => {
1325
+ const cases: ReadonlyArray<readonly [ByteSizeLiteral, number]> = [
1326
+ ["0B", 0],
1327
+ ["1023B", 1023],
1328
+ ["1KiB", 1024],
1329
+ ["1.5KiB", 1536],
1330
+ ["1023.5KiB", 1048064],
1331
+ ["1000KiB", 1024000],
1332
+ ["1023KiB", 1047552],
1333
+ ["1MiB", 1048576],
1334
+ ["1023MiB", 1072693248],
1335
+ ["1GiB", 1073741824],
1336
+ ["1.5GiB", 1610612736],
1337
+ ["1023GiB", 1098437885952],
1338
+ ["1TiB", 1099511627776],
1339
+ ["1023TiB", 1124800395214848],
1340
+ ];
1341
+ for (const [literal, bytes] of cases)
1342
+ assertEqual(byteSizeToByteLength(literal), bytes);
1343
+
1344
+ const result = byteSizeToByteLength("1MiB");
1345
+ assertType<typeof result, ByteLength>();
1346
+
1347
+ const validated = ByteLength.orThrow(1000);
1348
+ assertSame(byteSizeToByteLength(validated), validated);
1349
+ });
1350
+
1351
+ it("asserts that a literal has a supported unit", () => {
1352
+ const error = assertThrowsInstanceOf(
1353
+ () => byteSizeToByteLength("1PiB" as ByteSize),
1354
+ Error,
1355
+ );
1356
+ assertEqual(error.message, "Unknown byte length unit: PiB");
1357
+ });
1358
+ });
1359
+
1360
+ describe("ByteLengthFromString", () => {
1361
+ it("parses a number of bytes or a literal", () => {
1362
+ assertOk(ByteLengthFromString.fromUnknown("0"), 0);
1363
+ assertOk(ByteLengthFromString.fromUnknown("1048576"), 1048576);
1364
+ assertOk(ByteLengthFromString.fromUnknown("007"), 7);
1365
+ assertOk(
1366
+ ByteLengthFromString.fromUnknown("9007199254740991"),
1367
+ Number.MAX_SAFE_INTEGER,
1368
+ );
1369
+ assertOk(ByteLengthFromString.fromUnknown("10MiB"), 10485760);
1370
+ assertOk(ByteLengthFromString.fromUnknown("1023MiB"), 1072693248);
1371
+ assertOk(ByteLengthFromString.fromUnknown("0B"), 0);
1372
+ assertEqual(
1373
+ ByteLengthFromString.to(ByteLengthFromString.orThrow("1KiB")),
1374
+ "1024",
1375
+ );
1376
+ assertSame(
1377
+ ByteLengthFromString.orThrow(
1378
+ ByteLengthFromString.to(ByteLength.orThrow(0)),
1379
+ ),
1380
+ 0,
1381
+ );
1382
+ });
1383
+
1384
+ it("rejects other text and unsafe numbers", () => {
1385
+ for (const value of [
1386
+ "",
1387
+ "10MB",
1388
+ "1.1KiB",
1389
+ "1024KiB",
1390
+ "-0",
1391
+ "-1",
1392
+ "1.5",
1393
+ "1e3",
1394
+ " 1",
1395
+ "1KiB ",
1396
+ "9007199254740992",
1397
+ "9007199254740993",
1398
+ "9".repeat(309),
1399
+ ]) {
1400
+ const invalid = ByteLengthFromString.fromUnknown(value);
1401
+ assertErr(invalid, { type: "ByteLengthFromString", value });
1402
+ assertEqual(
1403
+ ByteLengthFromString.formatError(invalid.error),
1404
+ `The value ${JSON.stringify(value)} is not a byte length. Use a number of bytes or a literal such as 10MiB.`,
1405
+ );
1406
+ }
1407
+ assertErr(ByteLengthFromString.fromUnknown(1024), {
1408
+ type: "TypeOf",
1409
+ expected: "String",
1410
+ value: 1024,
1411
+ });
1412
+ });
1413
+ });