@postxl/utils 1.0.1 → 1.1.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 (114) hide show
  1. package/dist/index.browser.d.ts +2 -0
  2. package/dist/index.browser.js +3 -0
  3. package/dist/index.d.ts +22 -25
  4. package/dist/index.js +36 -41
  5. package/dist/index.js.map +1 -1
  6. package/dist/zod-excel.decoders-BJliEI1Y.js +1426 -0
  7. package/dist/zod-excel.decoders-BJliEI1Y.js.map +1 -0
  8. package/dist/zod-excel.decoders-CGmBv5CD.d.ts +773 -0
  9. package/package.json +24 -15
  10. package/dist/DefaultMap.d.ts +0 -12
  11. package/dist/DefaultMap.d.ts.map +0 -1
  12. package/dist/DefaultMap.js +0 -26
  13. package/dist/DefaultMap.js.map +0 -1
  14. package/dist/NestedMap.d.ts +0 -17
  15. package/dist/NestedMap.d.ts.map +0 -1
  16. package/dist/NestedMap.js +0 -72
  17. package/dist/NestedMap.js.map +0 -1
  18. package/dist/TypedMapping.d.ts +0 -27
  19. package/dist/TypedMapping.d.ts.map +0 -1
  20. package/dist/TypedMapping.js +0 -33
  21. package/dist/TypedMapping.js.map +0 -1
  22. package/dist/array.d.ts +0 -7
  23. package/dist/array.d.ts.map +0 -1
  24. package/dist/array.js +0 -21
  25. package/dist/array.js.map +0 -1
  26. package/dist/async.d.ts +0 -4
  27. package/dist/async.d.ts.map +0 -1
  28. package/dist/async.js +0 -41
  29. package/dist/async.js.map +0 -1
  30. package/dist/buffer.d.ts +0 -5
  31. package/dist/buffer.d.ts.map +0 -1
  32. package/dist/buffer.js +0 -16
  33. package/dist/buffer.js.map +0 -1
  34. package/dist/check-port.d.ts +0 -10
  35. package/dist/check-port.d.ts.map +0 -1
  36. package/dist/check-port.js +0 -40
  37. package/dist/check-port.js.map +0 -1
  38. package/dist/datetime.d.ts +0 -23
  39. package/dist/datetime.d.ts.map +0 -1
  40. package/dist/datetime.js +0 -37
  41. package/dist/datetime.js.map +0 -1
  42. package/dist/dictionary.d.ts +0 -9
  43. package/dist/dictionary.d.ts.map +0 -1
  44. package/dist/dictionary.js +0 -45
  45. package/dist/dictionary.js.map +0 -1
  46. package/dist/format.d.ts +0 -2
  47. package/dist/format.d.ts.map +0 -1
  48. package/dist/format.js +0 -7
  49. package/dist/format.js.map +0 -1
  50. package/dist/group-by.d.ts +0 -9
  51. package/dist/group-by.d.ts.map +0 -1
  52. package/dist/group-by.js +0 -66
  53. package/dist/group-by.js.map +0 -1
  54. package/dist/index.d.ts.map +0 -1
  55. package/dist/is-object.d.ts +0 -13
  56. package/dist/is-object.d.ts.map +0 -1
  57. package/dist/is-object.js +0 -25
  58. package/dist/is-object.js.map +0 -1
  59. package/dist/map.d.ts +0 -23
  60. package/dist/map.d.ts.map +0 -1
  61. package/dist/map.js +0 -76
  62. package/dist/map.js.map +0 -1
  63. package/dist/omit.d.ts +0 -5
  64. package/dist/omit.d.ts.map +0 -1
  65. package/dist/omit.js +0 -11
  66. package/dist/omit.js.map +0 -1
  67. package/dist/pagination.d.ts +0 -7
  68. package/dist/pagination.d.ts.map +0 -1
  69. package/dist/pagination.js +0 -14
  70. package/dist/pagination.js.map +0 -1
  71. package/dist/random.d.ts +0 -11
  72. package/dist/random.d.ts.map +0 -1
  73. package/dist/random.js +0 -56
  74. package/dist/random.js.map +0 -1
  75. package/dist/remove-secrets.d.ts +0 -12
  76. package/dist/remove-secrets.d.ts.map +0 -1
  77. package/dist/remove-secrets.js +0 -62
  78. package/dist/remove-secrets.js.map +0 -1
  79. package/dist/remove-undefined.d.ts +0 -10
  80. package/dist/remove-undefined.d.ts.map +0 -1
  81. package/dist/remove-undefined.js +0 -22
  82. package/dist/remove-undefined.js.map +0 -1
  83. package/dist/result.d.ts +0 -35
  84. package/dist/result.d.ts.map +0 -1
  85. package/dist/result.js +0 -120
  86. package/dist/result.js.map +0 -1
  87. package/dist/sort.d.ts +0 -18
  88. package/dist/sort.d.ts.map +0 -1
  89. package/dist/sort.js +0 -64
  90. package/dist/sort.js.map +0 -1
  91. package/dist/string-colors.d.ts +0 -45
  92. package/dist/string-colors.d.ts.map +0 -1
  93. package/dist/string-colors.js +0 -73
  94. package/dist/string-colors.js.map +0 -1
  95. package/dist/string.d.ts +0 -162
  96. package/dist/string.d.ts.map +0 -1
  97. package/dist/string.js +0 -434
  98. package/dist/string.js.map +0 -1
  99. package/dist/types.d.ts +0 -98
  100. package/dist/types.d.ts.map +0 -1
  101. package/dist/types.js +0 -57
  102. package/dist/types.js.map +0 -1
  103. package/dist/uniq.d.ts +0 -2
  104. package/dist/uniq.d.ts.map +0 -1
  105. package/dist/uniq.js +0 -16
  106. package/dist/uniq.js.map +0 -1
  107. package/dist/zod-excel.decoders.d.ts +0 -162
  108. package/dist/zod-excel.decoders.d.ts.map +0 -1
  109. package/dist/zod-excel.decoders.js +0 -361
  110. package/dist/zod-excel.decoders.js.map +0 -1
  111. package/dist/zod.d.ts +0 -30
  112. package/dist/zod.d.ts.map +0 -1
  113. package/dist/zod.js +0 -56
  114. package/dist/zod.js.map +0 -1
@@ -0,0 +1,1426 @@
1
+ import z$1, { z } from "zod";
2
+
3
+ //#region src/DefaultMap.ts
4
+ /**
5
+ * A map extension that returns a default value if the key is not found.
6
+ */
7
+ var DefaultMap = class extends Map {
8
+ /**
9
+ * A function that returns the default value for the map.
10
+ */
11
+ constructor(_default) {
12
+ super();
13
+ this._default = _default;
14
+ }
15
+ get(key) {
16
+ const value = super.get(key);
17
+ if (value) return value;
18
+ const defaultValue = this._default();
19
+ this.set(key, defaultValue);
20
+ return defaultValue;
21
+ }
22
+ };
23
+
24
+ //#endregion
25
+ //#region src/NestedMap.ts
26
+ var NestedMap = class {
27
+ _map = new Map();
28
+ constructor(getKey1, getKey2, items = []) {
29
+ this.getKey1 = getKey1;
30
+ this.getKey2 = getKey2;
31
+ for (const item of items) this.set(item);
32
+ }
33
+ get(key1, key2) {
34
+ const map = this._map.get(key1);
35
+ if (!map) return void 0;
36
+ return map.get(key2);
37
+ }
38
+ getKeys(item) {
39
+ return [this.getKey1(item), this.getKey2(item)];
40
+ }
41
+ set(item) {
42
+ const [key1, key2] = this.getKeys(item);
43
+ let map = this._map.get(key1);
44
+ if (!map) {
45
+ map = new Map();
46
+ this._map.set(key1, map);
47
+ }
48
+ map.set(key2, item);
49
+ }
50
+ delete(item) {
51
+ const [key1, key2] = this.getKeys(item);
52
+ const map = this._map.get(key1);
53
+ if (!map) return;
54
+ map.delete(key2);
55
+ }
56
+ clear() {
57
+ this._map.clear();
58
+ }
59
+ get size() {
60
+ return Array.from(this._map.values()).reduce((sum, map) => sum + map.size, 0);
61
+ }
62
+ get keys() {
63
+ return this._map.keys();
64
+ }
65
+ get values() {
66
+ const values = [];
67
+ for (const map of this._map.values()) for (const value of map.values()) values.push(value);
68
+ return values;
69
+ }
70
+ get entries() {
71
+ const entries = [];
72
+ for (const [key1, map] of this._map.entries()) for (const [key2, value] of map.entries()) entries.push([
73
+ key1,
74
+ key2,
75
+ value
76
+ ]);
77
+ return entries;
78
+ }
79
+ get [Symbol.iterator]() {
80
+ return this.entries;
81
+ }
82
+ };
83
+
84
+ //#endregion
85
+ //#region src/TypedMapping.ts
86
+ /**
87
+ * A map of typed mappings.
88
+ */
89
+ var TypedMapping = class {
90
+ constructor(mappings = {}) {
91
+ this.mappings = mappings;
92
+ }
93
+ /**
94
+ * Creates a new mapping from the original id to the new id.
95
+ */
96
+ set({ model, id }, newId) {
97
+ if (!this.mappings[model]) this.mappings[model] = new Map();
98
+ this.mappings[model].set(id, newId);
99
+ return this;
100
+ }
101
+ /**
102
+ * Returns the id if there exists a mapping, otherwise returns the original id.
103
+ */
104
+ get({ model, id }) {
105
+ const mapping = this.mappings[model]?.get(id);
106
+ if (!mapping) return id;
107
+ return mapping;
108
+ }
109
+ };
110
+
111
+ //#endregion
112
+ //#region src/array.ts
113
+ /**
114
+ * Adds a value to a sorted array and keeps the array sorted.
115
+ *
116
+ * O(n)
117
+ */
118
+ function addAndSort(arr, value, predicate) {
119
+ arr.push(value);
120
+ let i = arr.length - 1;
121
+ const item = arr[i];
122
+ const order = predicate(item);
123
+ while (i > 0 && order < predicate(arr[i - 1])) {
124
+ arr[i] = arr[i - 1];
125
+ i -= 1;
126
+ }
127
+ arr[i] = item;
128
+ return arr;
129
+ }
130
+
131
+ //#endregion
132
+ //#region src/async.ts
133
+ const defaultConcurrency = 5;
134
+ async function mapLimit(arr, iterator, limit = defaultConcurrency) {
135
+ const result = [];
136
+ if (arr.length === 0) return result;
137
+ const generator = arrayGenerator(arr);
138
+ limit = Math.min(limit, arr.length);
139
+ const workers = new Array(limit);
140
+ for (let i = 0; i < limit; i++) workers.push(worker(i, generator, iterator, result));
141
+ await Promise.all(workers);
142
+ return result;
143
+ }
144
+ function* arrayGenerator(array) {
145
+ for (let index = 0; index < array.length; index++) {
146
+ const currentValue = array[index];
147
+ yield [currentValue, index];
148
+ }
149
+ }
150
+ async function worker(id, gen, mapFn, result) {
151
+ for (const [currentValue, index] of gen) {
152
+ result[index] = void 0;
153
+ if (currentValue === void 0) return;
154
+ try {
155
+ result[index] = await mapFn(currentValue, index);
156
+ } catch (e) {
157
+ console.error(`Worker ${id} --- index ${index} item ${JSON.stringify(currentValue)} failed`, e);
158
+ }
159
+ }
160
+ }
161
+
162
+ //#endregion
163
+ //#region src/buffer.ts
164
+ /**
165
+ * Compares two string or buffer values for equality.
166
+ */
167
+ function equals(a, b) {
168
+ if (typeof a === "string" && typeof b === "string") return a === b;
169
+ if (Buffer.isBuffer(a) && Buffer.isBuffer(b)) return a.equals(b);
170
+ return false;
171
+ }
172
+
173
+ //#endregion
174
+ //#region src/datetime.ts
175
+ /**
176
+ * returns the default time stamp in format YYMMDD HHMM
177
+ */
178
+ function timeStamp() {
179
+ const date = new Date();
180
+ const year = date.getFullYear().toString().slice(-2);
181
+ const month = (date.getMonth() + 1).toString().padStart(2, "0");
182
+ const day = date.getDate().toString().padStart(2, "0");
183
+ const hour = date.getHours().toString().padStart(2, "0");
184
+ const minute = date.getMinutes().toString().padStart(2, "0");
185
+ return `${year}${month}${day} ${hour}${minute}`;
186
+ }
187
+ /**
188
+ * Checks if a value is a valid Date object.
189
+ * Returns true only for Date instances that are not Invalid Date.
190
+ * This function is isomorphic and works in both Node.js and browser environments.
191
+ *
192
+ * @param value - The value to check
193
+ * @returns true if the value is a valid Date object, false otherwise
194
+ *
195
+ * @example
196
+ * isDate(new Date()) // true
197
+ * isDate(new Date('2023-01-01')) // true
198
+ * isDate(new Date('invalid')) // false (Invalid Date)
199
+ * isDate('2023-01-01') // false
200
+ * isDate(1234567890) // false
201
+ * isDate(null) // false
202
+ * isDate(undefined) // false
203
+ */
204
+ function isDate(value) {
205
+ return value instanceof Date && !Number.isNaN(value.getTime());
206
+ }
207
+
208
+ //#endregion
209
+ //#region src/dictionary.ts
210
+ function toDict(data, keyFn) {
211
+ const result = {};
212
+ for (const item of data) result[keyFn(item)] = item;
213
+ return result;
214
+ }
215
+ function idToDict(data) {
216
+ const result = {};
217
+ for (const item of data) result[item.id.toString()] = item;
218
+ return result;
219
+ }
220
+ function mapDict(dict, predicate) {
221
+ const result = {};
222
+ for (const [key, value] of Object.entries(dict)) result[key] = predicate(value);
223
+ return result;
224
+ }
225
+ function filterDict(dict, predicate) {
226
+ const result = {};
227
+ for (const [key, value] of Object.entries(dict)) if (predicate(value)) result[key] = value;
228
+ return result;
229
+ }
230
+ function dictGetAndAssert(dict, key) {
231
+ const value = dict[key];
232
+ if (value === null || value === void 0) throw new Error(`Key ${key} not found`);
233
+ return value;
234
+ }
235
+
236
+ //#endregion
237
+ //#region src/format.ts
238
+ function format(n, locale = "en-US") {
239
+ return new Intl.NumberFormat(locale).format(n);
240
+ }
241
+
242
+ //#endregion
243
+ //#region src/group-by.ts
244
+ const groupBy = (predicate, items) => {
245
+ const result = {};
246
+ for (const item of items) {
247
+ const key = predicate(item);
248
+ const group = result[key];
249
+ if (group === void 0) result[key] = [item];
250
+ else group.push(item);
251
+ }
252
+ return result;
253
+ };
254
+ const groupByCurried = (predicate) => {
255
+ const result = {};
256
+ return (items) => {
257
+ for (const item of items) {
258
+ const key = predicate(item);
259
+ const group = result[key];
260
+ if (group === void 0) result[key] = [item];
261
+ else group.push(item);
262
+ }
263
+ return result;
264
+ };
265
+ };
266
+ const groupByToDict = (predicate, items) => {
267
+ const result = {};
268
+ for (const item of items) {
269
+ const key = predicate(item);
270
+ const group = result[key];
271
+ if (group === void 0) result[key] = { [item.id]: item };
272
+ else group[item.id] = item;
273
+ }
274
+ return result;
275
+ };
276
+ const groupByToMap = (getGroupId, items) => {
277
+ const result = new Map();
278
+ for (const item of items) {
279
+ const groupId = getGroupId(item);
280
+ if (!result.has(groupId)) result.set(groupId, new Map());
281
+ const group = result.get(groupId);
282
+ if (group !== void 0) group.set(item.id, item);
283
+ }
284
+ return result;
285
+ };
286
+
287
+ //#endregion
288
+ //#region src/map.ts
289
+ function mapMap(map, predicate) {
290
+ const result = new Map();
291
+ for (const [key, value] of map.entries()) result.set(key, predicate(value, key));
292
+ return result;
293
+ }
294
+ function mapMapValues(map, predicate) {
295
+ const result = [];
296
+ for (const [key, value] of map.entries()) result.push(predicate(value, key));
297
+ return result;
298
+ }
299
+ /**
300
+ *
301
+ */
302
+ function mapMapKeyValues(map, keyPredicate, valuePredicate) {
303
+ const result = new Map();
304
+ for (const [key, value] of map.entries()) result.set(keyPredicate(key, value), valuePredicate(value, key));
305
+ return result;
306
+ }
307
+ /**
308
+ * Maps keys of a map.
309
+ */
310
+ function mapMapKeys(map, keyPredicate) {
311
+ const result = new Map();
312
+ for (const [key, value] of map.entries()) result.set(keyPredicate(key, value), value);
313
+ return result;
314
+ }
315
+ /**
316
+ * Filters values from a map.
317
+ */
318
+ function filterMap(map, predicate) {
319
+ const result = new Map();
320
+ for (const [key, value] of map.entries()) if (predicate(value, key)) result.set(key, value);
321
+ return result;
322
+ }
323
+ /**
324
+ * Gets a value from a map and asserts that it exists.
325
+ */
326
+ function mapGetAndAssert(map, key) {
327
+ const value = map.get(key);
328
+ if (!value) throw new Error(`Key ${JSON.stringify(key)} not found`);
329
+ return value;
330
+ }
331
+ /**
332
+ * Converts a map to a dictionary.
333
+ */
334
+ function mapToDict(map) {
335
+ const result = {};
336
+ for (const [key, value] of map.entries()) result[key] = value;
337
+ return result;
338
+ }
339
+
340
+ //#endregion
341
+ //#region src/omit.ts
342
+ /**
343
+ * Omits a key from an object
344
+ */
345
+ function omit(obj, key) {
346
+ const { [key]: _,...rest } = obj;
347
+ return rest;
348
+ }
349
+
350
+ //#endregion
351
+ //#region src/pagination.ts
352
+ const slice = (items, skip, take) => {
353
+ const start = skip ?? 0;
354
+ const end = take ? start + take : void 0;
355
+ return {
356
+ total: items.length,
357
+ offset: start,
358
+ items: items.slice(start, end)
359
+ };
360
+ };
361
+
362
+ //#endregion
363
+ //#region src/random.ts
364
+ /**
365
+ * Returns a random integer between 0 (inclusive) and max (exclusive).
366
+ *
367
+ * Isomorphic implementation for Node.js, Deno, Bun, and browser environments.
368
+ * Uses Web Crypto API which is available in all modern JavaScript runtimes.
369
+ */
370
+ function randomInt(max) {
371
+ if (typeof crypto !== "undefined" && typeof crypto.getRandomValues === "function") return browserRandomInt(max);
372
+ throw new Error("No suitable random number generator available");
373
+ }
374
+ function browserRandomInt(max) {
375
+ const randomMax = 4294967296;
376
+ const range = randomMax - randomMax % max;
377
+ let value;
378
+ do {
379
+ const array = new Uint32Array(1);
380
+ crypto.getRandomValues(array);
381
+ value = array[0];
382
+ } while (value >= range);
383
+ return value % max;
384
+ }
385
+ const CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
386
+ /**
387
+ * Returns a randomly generated alphanumeric string.
388
+ */
389
+ function generateRandomAlphanumericString(length) {
390
+ let result = "";
391
+ for (let i = 0; i < length; i++) result += CHARS[randomInt(CHARS.length)];
392
+ return result;
393
+ }
394
+
395
+ //#endregion
396
+ //#region src/is-object.ts
397
+ /**
398
+ * Only returns true for plain objects, not for classes or functions
399
+ */
400
+ function isPlainObject(value) {
401
+ return value !== null && typeof value === "object" && value.constructor === Object;
402
+ }
403
+
404
+ //#endregion
405
+ //#region src/remove-secrets.ts
406
+ /**
407
+ * Removes secrets from the given configuration object.
408
+ *
409
+ * Recursively checks all properties of the object for keys that end with
410
+ * `key`, `secret`, `password`, `token` and replaces them with '***'.
411
+ *
412
+ * Also checks for keys the end with `connection` or `connectionString` and
413
+ * replaces the connection string with the same string but with the password
414
+ * replaced with '***'.
415
+ */
416
+ function removeSecrets(config) {
417
+ const newConfig = {};
418
+ for (const [key, value] of Object.entries(config)) newConfig[key] = processValue(key, value);
419
+ return newConfig;
420
+ }
421
+ function processValue(key, value) {
422
+ if (typeof value === "string") return processString(key, value);
423
+ else if (Array.isArray(value)) return value.map((item) => removeSecrets(item));
424
+ else if (typeof value === "object" && isPlainObject(value)) return removeSecrets(value);
425
+ return value;
426
+ }
427
+ function processString(key, value) {
428
+ if (key.endsWith("key") || key.endsWith("secret") || key.endsWith("password") || key.endsWith("token")) return hidePassword(value);
429
+ else if (key.endsWith("connection") || key.endsWith("connectionString")) return hidePasswordInConnectionString(value);
430
+ else return value;
431
+ }
432
+ function hidePasswordInConnectionString(connection) {
433
+ return connection.replace(/^(.*):\/\/(.*):(.*)@(.*)$/, "$1://$2:***@$4").replace(/^(.*):(.*)@(.*)$/, "$1:***@$3");
434
+ }
435
+ function hidePassword(password) {
436
+ if (password === "") return "nothing provided!";
437
+ if (password === "secret") return "default provided!";
438
+ return "***";
439
+ }
440
+
441
+ //#endregion
442
+ //#region src/remove-undefined.ts
443
+ /**
444
+ * Removes all undefined properties from an object.
445
+ *
446
+ * NOTE: This is useful when using the `Partial<T>` type modifier in combination with the spread operator to prevent
447
+ * overriding with undefined values.
448
+ */
449
+ function removeUndefinedProperties(obj) {
450
+ const newObj = {};
451
+ for (const key in obj) {
452
+ const value = obj[key];
453
+ if (value !== void 0) newObj[key] = value;
454
+ }
455
+ return newObj;
456
+ }
457
+
458
+ //#endregion
459
+ //#region src/result.ts
460
+ var Result = class Result {
461
+ static ok(value) {
462
+ return new Result("ok", value);
463
+ }
464
+ static err(value) {
465
+ return new Result("err", value);
466
+ }
467
+ static fromThrowable(f, handleError) {
468
+ try {
469
+ return Result.ok(f());
470
+ } catch (error) {
471
+ return Result.err(handleError(error));
472
+ }
473
+ }
474
+ static fromObject(result) {
475
+ if (result.tag === "ok") return Result.ok(result.value);
476
+ else return Result.err(result.err);
477
+ }
478
+ static async fromPromise(promise, handleError) {
479
+ try {
480
+ return Result.ok(await promise());
481
+ } catch (error) {
482
+ return Result.err(handleError(error));
483
+ }
484
+ }
485
+ #tag;
486
+ #value;
487
+ #error;
488
+ constructor(tag, value) {
489
+ this.#tag = tag;
490
+ this.#value = tag === "ok" ? value : null;
491
+ this.#error = tag === "err" ? value : null;
492
+ }
493
+ isOk() {
494
+ return this.#tag === "ok";
495
+ }
496
+ isErr() {
497
+ return this.#tag === "err";
498
+ }
499
+ map(f) {
500
+ if (this.#tag === "ok") return Result.ok(f(this.#value));
501
+ else return this;
502
+ }
503
+ mapErr(f) {
504
+ if (this.#tag === "err") return Result.err(f(this.#error));
505
+ else return this;
506
+ }
507
+ andThen(f) {
508
+ if (this.#tag === "ok") return f(this.#value);
509
+ else return this;
510
+ }
511
+ withDefault(fallback) {
512
+ return this.#tag === "ok" ? this.#value : fallback;
513
+ }
514
+ unwrap() {
515
+ if (this.#tag === "ok") return this.#value;
516
+ else throw new Error(typeof this.#error === "string" ? this.#error : JSON.stringify(this.#error));
517
+ }
518
+ unwrapErr() {
519
+ if (this.#tag === "err") return this.#error;
520
+ else throw new Error("tried to unwrap an ok result");
521
+ }
522
+ toJSON() {
523
+ if (this.#tag === "ok") return {
524
+ tag: "ok",
525
+ value: this.#value
526
+ };
527
+ else return {
528
+ tag: "err",
529
+ err: this.#error
530
+ };
531
+ }
532
+ };
533
+ const ok = Result.ok;
534
+ const err = Result.err;
535
+
536
+ //#endregion
537
+ //#region src/sort.ts
538
+ const sort = (items, dir = "asc", compareFn = compareValues) => setDirection(items.toSorted(compareFn), dir);
539
+ /**
540
+ * Compare fields of an object.
541
+ */
542
+ const compareFields = (key) => {
543
+ return (a, b) => compareValues(a[key], b[key]);
544
+ };
545
+ const compare = (getter) => {
546
+ return (a, b) => compareValues(getter(a), getter(b));
547
+ };
548
+ const keyGetter = (key) => {
549
+ return (data) => data[key];
550
+ };
551
+ const compareValues = (a, b) => {
552
+ switch (true) {
553
+ case (a === void 0 || a === null) && b !== void 0 && b !== null: return 1;
554
+ case (b === void 0 || b === null) && a !== void 0 && a !== null: return -1;
555
+ case (a === void 0 || a === null) && (b === void 0 || b === null): return 0;
556
+ case a instanceof Date && b instanceof Date: return compareDates(a, b);
557
+ case typeof a === "string" && typeof b === "string": return compareStrings(a, b);
558
+ case typeof a === "number" && typeof b === "number": return compareNumbers(a, b);
559
+ case typeof a === "boolean" && typeof b === "boolean": return compareBooleans(a, b);
560
+ default: return 0;
561
+ }
562
+ };
563
+ const compareDates = (a, b) => compareStrings(a.toISOString(), b.toISOString());
564
+ const compareStrings = (a, b) => {
565
+ const aLower = a.trim().toLowerCase();
566
+ const bLower = b.trim().toLowerCase();
567
+ if (aLower === bLower) return 0;
568
+ return aLower > bLower ? 1 : -1;
569
+ };
570
+ const compareNumbers = (a, b) => a - b;
571
+ const compareBooleans = (a, b) => booleanCode(a) - booleanCode(b);
572
+ const booleanCode = (bool) => bool ? 1 : 0;
573
+ const setDirection = (items, dir) => {
574
+ return dir === "desc" ? items.toReversed() : items;
575
+ };
576
+
577
+ //#endregion
578
+ //#region src/string-colors.ts
579
+ /**
580
+ * Colors a string in red color (using ANSI escape codes).
581
+ */
582
+ function red(input) {
583
+ return `\u001b[31m${input}\u001b[39m`;
584
+ }
585
+ /**
586
+ * Colors a string in green color (using ANSI escape codes).
587
+ */
588
+ function green(input) {
589
+ return `\u001b[32m${input}\u001b[39m`;
590
+ }
591
+ /**
592
+ * Colors a string in yellow color (using ANSI escape codes).
593
+ */
594
+ function yellow(input) {
595
+ return `\u001b[33m${input}\u001b[39m`;
596
+ }
597
+ /**
598
+ * Colors a string in blue color (using ANSI escape codes).
599
+ */
600
+ function blue(input) {
601
+ return `\u001b[34m${input}\u001b[39m`;
602
+ }
603
+ /**
604
+ * Colors a string in cyan color (using ANSI escape codes).
605
+ */
606
+ function cyan(input) {
607
+ return `\u001b[36m${input}\u001b[39m`;
608
+ }
609
+ /**
610
+ * Colors a string in gray color (using ANSI escape codes).
611
+ */
612
+ function gray(input) {
613
+ return `\u001b[90m${input}\u001b[39m`;
614
+ }
615
+ /**
616
+ * Colors a string in bold color (using ANSI escape codes).
617
+ */
618
+ function bold(input) {
619
+ return `\u001b[1m${input}\u001b[22m`;
620
+ }
621
+ /**
622
+ * Colors a string in underline color (using ANSI escape codes).
623
+ */
624
+ function underline(input) {
625
+ return `\u001b[4m${input}\u001b[24m`;
626
+ }
627
+ /**
628
+ * Colors a string in italic color (using ANSI escape codes).
629
+ */
630
+ function italic(input) {
631
+ return `\u001b[3m${input}\u001b[23m`;
632
+ }
633
+ /**
634
+ * Inverts the color of a string (using ANSI escape codes).
635
+ */
636
+ function inverse(input) {
637
+ return `\u001b[7m${input}\u001b[27m`;
638
+ }
639
+
640
+ //#endregion
641
+ //#region src/string.ts
642
+ function upperFirst(s) {
643
+ return s.charAt(0).toUpperCase() + s.slice(1);
644
+ }
645
+ function lowerFirst(s) {
646
+ return s.charAt(0).toLowerCase() + s.slice(1);
647
+ }
648
+ const IRREGULAR_PLURALS = Object.entries({
649
+ abyss: "abysses",
650
+ aegis: "aegises",
651
+ alias: "aliases",
652
+ alumnus: "alumni",
653
+ amends: "amends",
654
+ analysis: "analyses",
655
+ apparatus: "apparatuses",
656
+ appendix: "appendices",
657
+ asbestos: "asbestoses",
658
+ atlas: "atlases",
659
+ axis: "axes",
660
+ basis: "bases",
661
+ biceps: "bicepses",
662
+ bus: "buses",
663
+ caress: "caresses",
664
+ child: "children",
665
+ children: "children",
666
+ circus: "circuses",
667
+ citrus: "citruses",
668
+ compass: "compasses",
669
+ crisis: "crises",
670
+ criterion: "criteria",
671
+ crocus: "crocuses",
672
+ data: "data",
673
+ datum: "data",
674
+ diabetes: "diabetes",
675
+ diagnosis: "diagnoses",
676
+ dross: "drosses",
677
+ egress: "egresses",
678
+ elvis: "elvises",
679
+ emboss: "embosses",
680
+ fiss: "fisses",
681
+ focus: "foci",
682
+ glass: "glasses",
683
+ hippocampus: "hippocampi",
684
+ history: "history",
685
+ hypothesis: "hypotheses",
686
+ ignis: "ignises",
687
+ index: "indices",
688
+ iris: "irises",
689
+ jesus: "jesuses",
690
+ kudos: "kudos",
691
+ lens: "lenses",
692
+ man: "men",
693
+ matrix: "matrices",
694
+ medium: "media",
695
+ news: "news",
696
+ oasis: "oases",
697
+ parenthesis: "parentheses",
698
+ pass: "passes",
699
+ phenomenon: "phenomena",
700
+ prognosis: "prognoses",
701
+ radius: "radii",
702
+ ras: "rasses",
703
+ sepsis: "sepses",
704
+ species: "species",
705
+ status: "statuses",
706
+ suffix: "suffixes",
707
+ syllabus: "syllabi",
708
+ synopsis: "synopses",
709
+ thesis: "theses",
710
+ tress: "tresses",
711
+ virus: "viruses",
712
+ vortex: "vortices",
713
+ woman: "women"
714
+ });
715
+ /**
716
+ * Returns a pluralized version of the given string based on the count.
717
+ */
718
+ function pluralize(s, count = 2) {
719
+ if (count === 1) return s;
720
+ const lower = s.toLowerCase();
721
+ for (const [singular, plural] of IRREGULAR_PLURALS) if (lower.endsWith(singular)) return s.slice(0, -singular.length + 1) + plural.slice(1);
722
+ if (lower.endsWith("ss")) return s.slice(0, -2) + "sses";
723
+ if (lower.endsWith("x") || lower.endsWith("z") || lower.endsWith("sh") || lower.endsWith("ch")) return s + "es";
724
+ if (s.endsWith("y") && !s.endsWith("ay") && !s.endsWith("ey") && !s.endsWith("iy") && !s.endsWith("oy") && !s.endsWith("uy")) return s.slice(0, -1) + "ies";
725
+ if (s.endsWith("s")) return s;
726
+ return s + "s";
727
+ }
728
+ /**
729
+ * Returns true if the given string is already pluralized.
730
+ */
731
+ function isPlural(s) {
732
+ const plural = pluralize(s);
733
+ return plural === s;
734
+ }
735
+ /**
736
+ * Converts all keys of an object to capitalized versions in a type-safe way.
737
+ */
738
+ function capitalizeKeys(obj) {
739
+ const result = {};
740
+ for (const key in obj) {
741
+ const cKey = `${key.charAt(0).toUpperCase()}${key.slice(1)}`;
742
+ const cVal = obj[key];
743
+ result[cKey] = cVal;
744
+ }
745
+ return result;
746
+ }
747
+ /**
748
+ * Converts all keys of an object to uncapitalized versions in a type-safe way.
749
+ */
750
+ function uncapitalizeKeys(obj) {
751
+ const result = {};
752
+ for (const key in obj) {
753
+ const unKey = `${key.charAt(0).toLowerCase()}${key.slice(1)}`;
754
+ const unVal = obj[key];
755
+ result[unKey] = unVal;
756
+ }
757
+ return result;
758
+ }
759
+ /**
760
+ * Returns the same string with a lowercase first letter.
761
+ */
762
+ function uncapitalize(str) {
763
+ return str.charAt(0).toLowerCase() + str.slice(1);
764
+ }
765
+ /**
766
+ * Returns the same string with an uppercase first letter.
767
+ */
768
+ function capitalize(str) {
769
+ return str.charAt(0).toUpperCase() + str.slice(1);
770
+ }
771
+ /**
772
+ * Returns the camelCase version of the given string.
773
+ *
774
+ * Camel case examples:
775
+ * `toCamelCase('hello world')` -> 'helloWorld',
776
+ * `toCamelCase('hello_world')` -> 'helloWorld',
777
+ * `toCamelCase('helloWorld')` -> 'helloWorld'
778
+ * `toCamelCase('HelloWorld')` -> 'helloWorld'
779
+ * `toCamelCase('hello_world')` -> 'helloWorld'
780
+ */
781
+ function toCamelCase(str) {
782
+ if (/^[a-z]+(?:[A-Z][a-z]*)*$/.test(str)) return str;
783
+ const words = str.split(/(?<![A-Z])(?=[A-Z])|[\s_-]+/);
784
+ const camelCasedWords = words.map((word) => word.toUpperCase() === word ? word.toLowerCase() : word).map((word, index) => index === 0 ? lowerFirst(word) : upperFirst(word));
785
+ const camelCasedStr = camelCasedWords.join("");
786
+ return camelCasedStr;
787
+ }
788
+ /**
789
+ * Validates if a string is in camelCase format
790
+ */
791
+ function isCamelCase(str) {
792
+ return str === toCamelCase(str);
793
+ }
794
+ /**
795
+ * Returns the PascalCase version of the given string.
796
+ *
797
+ * Examples:
798
+ * `toPascalCase('hello world')` -> 'HelloWorld'
799
+ * `toPascalCase('hello_world')` -> 'HelloWorld'
800
+ * `toPascalCase('helloWorld')` -> 'HelloWorld'
801
+ * `toPascalCase('Hello-World')` -> 'HelloWorld'
802
+ * `toPascalCase('HELLO_WORLD')` -> 'HelloWorld'
803
+ */
804
+ function toPascalCase(str) {
805
+ const s = toCamelCase(str);
806
+ return s.charAt(0).toUpperCase() + s.slice(1);
807
+ }
808
+ /**
809
+ * Validates if a string is in PascalCase format
810
+ */
811
+ function isPascalCase(str) {
812
+ return str === toPascalCase(str);
813
+ }
814
+ /**
815
+ * Returns the snake_case version of the given string.
816
+ *
817
+ * Examples:
818
+ * `toSnakeCase('hello world')` -> 'hello_world'
819
+ * `toSnakeCase('helloWorld')` -> 'hello_world'
820
+ * `toSnakeCase('HelloWorld')` -> 'hello_world'
821
+ * `toSnakeCase('HELLO_WORLD')` -> 'hello_world'
822
+ */
823
+ function toSnakeCase(str) {
824
+ const camelCase = toCamelCase(str);
825
+ return camelCase.replace(/([A-Z])/g, "_$1").toLowerCase();
826
+ }
827
+ /**
828
+ * Validates if a string is in snake_case format
829
+ */
830
+ function isSnakeCase(str) {
831
+ return str === toSnakeCase(str);
832
+ }
833
+ /**
834
+ * Returns the kebab-case version of the given string.
835
+ *
836
+ * Examples:
837
+ * `toKebabCase('hello world')` -> 'hello-world'
838
+ * `toKebabCase('helloWorld')` -> 'hello-world'
839
+ * `toKebabCase('HelloWorld')` -> 'hello-world'
840
+ * `toKebabCase('HELLO_WORLD')` -> 'hello-world'
841
+ */
842
+ function toKebabCase(str) {
843
+ const camelCase = toCamelCase(str);
844
+ return camelCase.replaceAll(/([A-Z])/g, "-$1").toLowerCase();
845
+ }
846
+ /**
847
+ * Validates if a string is in kebab-case format
848
+ */
849
+ function isKebabCase(str) {
850
+ return str === toKebabCase(str);
851
+ }
852
+ /**
853
+ * Converts each line of a string to a commented line
854
+ */
855
+ function commentLines(lines) {
856
+ return lines.split("\n").map((l) => `// ${l}`).join("\n");
857
+ }
858
+ function toHumanReadable(input) {
859
+ let result = input.replace(/[_-]/g, " ");
860
+ result = result.replace(/([a-z])([A-Z])/g, "$1 $2");
861
+ if (result === input) return input;
862
+ result = result.split(" ").map((s) => s.toLowerCase()).join(" ");
863
+ result = upperFirst(result);
864
+ return result;
865
+ }
866
+ /**
867
+ * Sanitizes a string to be a valid Excel ListObject (table) column name.
868
+ * - Removes illegal characters: [ ] : ? * / \
869
+ * - Trims leading/trailing spaces
870
+ * - Replaces multiple spaces with a single space
871
+ * - If the result is empty, returns a default name
872
+ */
873
+ function toExcelColumnName(input, defaultName = "Column1") {
874
+ let result = input.replace(/[[\]:?*/\\]/g, "");
875
+ result = result.replace(/\s+/g, " ");
876
+ result = result.trim();
877
+ if (!result) return toExcelColumnName(defaultName, "Column1");
878
+ return result;
879
+ }
880
+ /**
881
+ * Sanitizes a string to be a valid Excel ListObject (table) name.
882
+ * - Must start with a letter, underscore, or backslash
883
+ * - Only letters, numbers, and underscores allowed (no spaces or punctuation)
884
+ * - Cannot be a cell reference (e.g., "A1")
885
+ * - Max 255 characters
886
+ * - If invalid or empty, returns a default name
887
+ */
888
+ function toExcelTableName(input, defaultName = "Table1") {
889
+ let result = input.replace(/[^A-Za-z0-9_\\]/g, "");
890
+ if (!/^[A-Za-z_\\]/.test(result)) result = "_" + result;
891
+ result = result.slice(0, 255);
892
+ if (/^[A-Za-z]{1,3}[1-9][0-9]{0,6}$/.test(result)) result = "_" + result;
893
+ if (!result || result === "_") return toExcelTableName(defaultName, "Table1");
894
+ return result;
895
+ }
896
+ /**
897
+ * Provide all relevant conjugation of a name
898
+ */
899
+ function conjugateNames(name) {
900
+ const plural = pluralize(name);
901
+ return {
902
+ camelCase: toCamelCase(name),
903
+ camelCasePlural: toCamelCase(plural),
904
+ capitalized: capitalize(name),
905
+ capitalizedPlural: capitalize(plural),
906
+ kebabCase: toKebabCase(name),
907
+ kebabCasePlural: toKebabCase(plural),
908
+ PascalCase: toPascalCase(name),
909
+ PascalCasePlural: toPascalCase(plural),
910
+ pluralized: capitalize(plural),
911
+ snake_case_plural: toSnakeCase(plural),
912
+ snake_case: toSnakeCase(name),
913
+ uncapitalized: uncapitalize(name),
914
+ uncapitalizedPlural: uncapitalize(plural)
915
+ };
916
+ }
917
+ function slugify(str) {
918
+ return str.replace(/([A-Z])/g, "-$1").toLowerCase().replace(/([a-z])(\d)/g, "$1-$2").replace(/[^a-z0-9]+/g, "-").replace(/^-/g, "").replace(/-$/g, "").replace(/-+/g, "-");
919
+ }
920
+ function isSlug(str) {
921
+ return /^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(str);
922
+ }
923
+ /**
924
+ * Ensures that a string is unique within a list of existing values by appending a sequential number if necessary.
925
+ *
926
+ * If the base string is not in the list, it is returned as is.
927
+ * If it is, the function looks for existing strings in the format "base (n)" where n is a number,
928
+ * and returns the next available sequential string.
929
+ *
930
+ * @param value - The base string to ensure uniqueness for.
931
+ * @param existingValues - An array of existing strings to check against.
932
+ * @param maxChecks - Maximum number of checks to be performed. An error is thrown if this limit is exceeded. Default is 100
933
+ * @returns A unique string based on the base string.
934
+ */
935
+ const ensureUnique = (value, existingValues, maxChecks = 100) => {
936
+ const values = new Set(existingValues);
937
+ if (!values.has(value)) return value;
938
+ let base = value.trim();
939
+ let num = 0;
940
+ const m = /\((\d{1,5})\)$/.exec(value);
941
+ if (m && /\s$/.test(value.slice(0, m.index))) base = value.slice(0, m.index).trimEnd();
942
+ while (num < maxChecks) {
943
+ const newValue = num === 0 ? base : `${base} (${num})`;
944
+ if (!values.has(newValue)) return newValue;
945
+ num++;
946
+ }
947
+ throw new Error(`Could not find a unique name for "${value}" after ${maxChecks} checks`);
948
+ };
949
+
950
+ //#endregion
951
+ //#region src/types.ts
952
+ /**
953
+ * Makes a type check that is only valid when all cases of a switch
954
+ * statement have been covered.
955
+ */
956
+ var ExhaustiveSwitchCheck = class extends Error {
957
+ constructor(val) {
958
+ super(`Unreachable case: ${JSON.stringify(val)}`);
959
+ }
960
+ };
961
+ /**
962
+ * Allows inline type assertions.
963
+ *
964
+ * Example:
965
+ * ```
966
+ * type Original = { a: number }
967
+ * type WithExtra = Original & { b: string }
968
+ *
969
+ * function addExtra(obj: Original): asserts obj is WithExtra {
970
+ * assertType(obj)
971
+ * obj.b = 'new value' // ✅ No TypeScript error!
972
+ * }
973
+ * ```
974
+ */
975
+ function assertType(value) {
976
+ return value;
977
+ }
978
+ /**
979
+ * Applies a type assertion to an entire array and hence asserts the array.
980
+ *
981
+ * Type assertions are especially useful when you want to modify and object in place - a function
982
+ * with a type assertion lets TypeScript know that the object was modified.
983
+ *
984
+ * Example:
985
+ * ```
986
+ * type Original = { a: number }
987
+ * type WithExtra = Original & { b: string }
988
+ *
989
+ * function addExtra(obj: Original): asserts obj is WithExtra {
990
+ * obj.b = 'new value'
991
+ * }
992
+ *
993
+ * const myObj: Original[] = [{ a: 1 }]
994
+ * console.log(myObj[0].b) // ❌ TypeScript error!
995
+ * assertArray(myObj, addExtra)
996
+ * console.log(myObj[0].b) // ✅ No TypeScript error!
997
+ * ```
998
+ */
999
+ function assertArray(obj, fn) {
1000
+ obj.forEach(fn);
1001
+ }
1002
+
1003
+ //#endregion
1004
+ //#region src/uniq.ts
1005
+ function uniq(items, predicate) {
1006
+ const result = [];
1007
+ const seen = {};
1008
+ for (const item of items) {
1009
+ const key = predicate(item);
1010
+ if (!seen[key]) {
1011
+ seen[key] = true;
1012
+ result.push(item);
1013
+ }
1014
+ }
1015
+ return result;
1016
+ }
1017
+
1018
+ //#endregion
1019
+ //#region src/zod.ts
1020
+ /**
1021
+ * Pipes a list of Zod transformers together in a type-safe way. Returns early if any of the transformers returns `z.NEVER`.
1022
+ */
1023
+ function pipeZodTransformers(initialValue, ctx, fns) {
1024
+ let result = initialValue;
1025
+ for (const fn of fns) {
1026
+ result = fn(result, ctx);
1027
+ if (result === z.NEVER) return z.NEVER;
1028
+ }
1029
+ return result;
1030
+ }
1031
+ /**
1032
+ * Returns a Zod error map that returns the given message for union errors.
1033
+ *
1034
+ * This is useful for generating meaningful error messages for unions, e.g.
1035
+ * ```
1036
+ * z.union(
1037
+ * [z.constant('foo'), z.constant('bar')],
1038
+ * unionErrorMessage('Value must be either "foo" or "bar"!')
1039
+ * )
1040
+ * ```
1041
+ *
1042
+ * In addition to a static message, you can also pass a function that takes the
1043
+ * invalid data as input and returns a string. Example:
1044
+ * ```
1045
+ * z.union(
1046
+ * [z.constant('foo'), z.constant('bar')],
1047
+ * unionErrorMessage((data) => `Value must be either "foo" or "bar" - received ${JSON.stringify(data)} instead!`)
1048
+ * )
1049
+ * ```
1050
+ */
1051
+ function unionErrorMessage(param) {
1052
+ return { errorMap: (issue, ctx) => {
1053
+ let message;
1054
+ if (typeof param === "string") message = param;
1055
+ else message = param(ctx.data);
1056
+ if (issue.code === z.ZodIssueCode.invalid_union) return { message };
1057
+ return { message: ctx.defaultError };
1058
+ } };
1059
+ }
1060
+
1061
+ //#endregion
1062
+ //#region src/zod-excel.decoders.ts
1063
+ /**
1064
+ * Transformer that verifies that the number is an integer
1065
+ */
1066
+ const intValidator = (x, ctx) => {
1067
+ if (x % 1 !== 0) {
1068
+ ctx.addIssue({
1069
+ code: z$1.ZodIssueCode.custom,
1070
+ message: `Not an integer: ${x}`
1071
+ });
1072
+ return z$1.NEVER;
1073
+ }
1074
+ return x;
1075
+ };
1076
+ /**
1077
+ * Converts any transformer to a nullable transformer, i.e. if the input is
1078
+ * null or undefined, the output will be nullable. Else the original transformer
1079
+ * is applied
1080
+ */
1081
+ const nullableTransformer = (transformer) => (x, ctx) => {
1082
+ if (x === null || x === void 0 || typeof x === "string" && x === "") return null;
1083
+ return transformer(x, ctx);
1084
+ };
1085
+ /**
1086
+ * A decoder that transforms Excel strings to JS strings, also handling numbers and boolean. Will not parse any other type!
1087
+ *
1088
+ * Background: In Excel, entering a number in a cell will automatically convert it to a number -
1089
+ * and xlPort will return it as a number. However, often we want to treat it as a string - this decoder
1090
+ * hence accepts both strings and numbers, and converts numbers to strings.
1091
+ *
1092
+ * It'll perform casting based on the following rules:
1093
+ * - If the value is a number, it'll be converted to a string
1094
+ * - If the value is a string, it'll be returned as-is
1095
+ * - If the value is boolean, it'll return 'true' or 'false'
1096
+ *
1097
+ * Any other type will throw an error!
1098
+ */
1099
+ const excelStringStrictDecoder = z$1.union([
1100
+ z$1.string(),
1101
+ z$1.number(),
1102
+ z$1.boolean()
1103
+ ]).transform((x) => {
1104
+ if (typeof x === "number") return x.toString();
1105
+ if (typeof x === "boolean") return x ? "true" : "false";
1106
+ return x;
1107
+ });
1108
+ /**
1109
+ * A decoder that transforms Excel strings to JS strings, also handling numbers and any other potential type
1110
+ *
1111
+ * Background: In Excel, entering a number in a cell will automatically convert it to a number -
1112
+ * and xlPort will return it as a number. However, often we want to treat it as a string - this decoder
1113
+ * hence accepts both strings and numbers, and converts numbers to strings.
1114
+ *
1115
+ * It'll perform casting based on the following rules:
1116
+ * - If the value is a number, it'll be converted to a string
1117
+ * - If the value is a string, it'll be returned as-is
1118
+ * - If the value is boolean, it'll return 'true' or 'false'
1119
+ * - If the value is null or undefined, it'll return an empty string
1120
+ * - If the value is any other type, it'll return an empty string
1121
+ *
1122
+ * Null handling: Null values/blank cells will be converted to a blank string.
1123
+ */
1124
+ const excelStringDecoder = z$1.union([
1125
+ z$1.string(),
1126
+ z$1.number(),
1127
+ z$1.boolean(),
1128
+ z$1.null(),
1129
+ z$1.undefined(),
1130
+ z$1.any()
1131
+ ]).transform((x) => {
1132
+ if (x === null || x === void 0) return "";
1133
+ if (typeof x === "number") return x.toString();
1134
+ if (typeof x === "boolean") return x ? "true" : "false";
1135
+ if (typeof x === "string") return x;
1136
+ return "";
1137
+ });
1138
+ /**
1139
+ * A decoder that transforms Excel strings to JS strings, also handling numbers and null
1140
+ *
1141
+ * Background: In Excel, entering a number in a cell will automatically convert it to a number -
1142
+ * and xlPort will return it as a number. However, often we want to treat it as a string - this decoder
1143
+ * hence accepts both strings and numbers, and converts numbers to strings.
1144
+ *
1145
+ * It'll perform casting based on the following rules:
1146
+ * - If the value is a number, it'll be converted to a string
1147
+ * - If the value is a string, it'll be returned as-is - except blank strings, which will be converted to null
1148
+ * - If the value is boolean, it'll return 'true' or 'false'
1149
+ * - If the value is null or undefined, it'll return null
1150
+ * - If the value is any other type, it'll return null
1151
+ *
1152
+ * Null handling: Blank strings will be converted to null. This is to reflect that in Excel a formula
1153
+ * cannot return null. As a workaround, one often returns a blank string to represent null.
1154
+ */
1155
+ const excelStringNullableDecoder = z$1.union([
1156
+ z$1.string(),
1157
+ z$1.number(),
1158
+ z$1.boolean(),
1159
+ z$1.null(),
1160
+ z$1.undefined(),
1161
+ z$1.any()
1162
+ ]).transform((x) => {
1163
+ if (x === null || x === void 0) return null;
1164
+ if (typeof x === "number") return x.toString();
1165
+ if (typeof x === "boolean") return x ? "true" : "false";
1166
+ if (typeof x === "string") {
1167
+ if (x === "") return null;
1168
+ return x;
1169
+ }
1170
+ return null;
1171
+ });
1172
+ const excelNumberTransformer = (x) => {
1173
+ if (typeof x === "string") {
1174
+ if (x.trim() === "") return null;
1175
+ const parsed = parseFloat(x);
1176
+ if (Number.isNaN(parsed)) return null;
1177
+ return parsed;
1178
+ }
1179
+ if (typeof x === "boolean") return x ? 1 : 0;
1180
+ if (typeof x === "number") return x;
1181
+ return null;
1182
+ };
1183
+ /**
1184
+ * A decoder that transforms Excel numbers to JS numbers, also handling strings and boolean. Will not parse any other type!
1185
+ *
1186
+ * It'll perform casting based on the following rules:
1187
+ * - If the value is a number, it'll be returned as-is
1188
+ * - If the value is a string, it'll try to parse it to a number. Blank strings will be converted to 0. Non-numeric strings will throw an error.
1189
+ * - If the value is boolean, it'll return 1 or 0
1190
+ * - If the value is any other type, it'll throw an error
1191
+ *
1192
+ */
1193
+ const excelNumberStrictDecoder = z$1.union([
1194
+ z$1.string(),
1195
+ z$1.number(),
1196
+ z$1.boolean()
1197
+ ]).transform((x, ctx) => {
1198
+ const result = excelNumberTransformer(x);
1199
+ if (result === null) {
1200
+ ctx.addIssue({
1201
+ code: z$1.ZodIssueCode.custom,
1202
+ message: `Not a number: ${x}`
1203
+ });
1204
+ return z$1.NEVER;
1205
+ }
1206
+ return result;
1207
+ });
1208
+ /**
1209
+ * A decoder that transforms Excel numbers to JS numbers, also handling strings, boolean and other types.
1210
+ *
1211
+ * It'll perform casting based on the following rules:
1212
+ * - If the value is a number, it'll be returned as-is
1213
+ * - If the value is a string, it'll try to parse it to a number. Blank strings and non-numerical strings will be converted to 0.
1214
+ * - If the value is boolean, it'll return 1 or 0
1215
+ * - If the value is any other type, it'll return 0
1216
+ *
1217
+ * Null handling: Null values/blank cells will be converted to blank strings.
1218
+ */
1219
+ const excelNumberDecoder = z$1.union([
1220
+ z$1.string(),
1221
+ z$1.number(),
1222
+ z$1.boolean(),
1223
+ z$1.null(),
1224
+ z$1.undefined(),
1225
+ z$1.any()
1226
+ ]).transform((x) => {
1227
+ const result = excelNumberTransformer(x);
1228
+ if (result === null) return 0;
1229
+ return result;
1230
+ });
1231
+ /**
1232
+ * A decoder that transforms Excel numbers to JS numbers, also handling strings, boolean and other types.
1233
+
1234
+ * It'll perform casting based on the following rules:
1235
+ * - If the value is a number, it'll be returned as-is
1236
+ * - If the value is a string, it'll try to parse it to a number. Non-numerical and blank strings will be converted to null
1237
+ * - If the value is boolean, it'll return 1 or 0
1238
+ * - If the value is any other type, it'll return null
1239
+ *
1240
+ * Null handling: Blank strings will be converted to null. This is to reflect that in Excel a formula
1241
+ * cannot return null. As a workaround, one often returns a blank string to represent null.
1242
+ */
1243
+ const excelNumberNullableDecoder = z$1.union([
1244
+ z$1.string(),
1245
+ z$1.null(),
1246
+ z$1.number(),
1247
+ z$1.undefined(),
1248
+ z$1.any()
1249
+ ]).transform(excelNumberTransformer);
1250
+ /**
1251
+ * A decoder that transforms Excel numbers to JS integers, also handling strings and boolean. Other types will throw an error.
1252
+ *
1253
+ * If the number is not an integer, an error will be thrown.
1254
+ */
1255
+ const excelIntStrictDecoder = excelNumberStrictDecoder.transform(intValidator);
1256
+ /**
1257
+ * A decoder that transforms Excel numbers to JS integers, also handling strings and boolean. Other types will be converted to 0.
1258
+ *
1259
+ * If the number is not an integer, an error will be thrown.
1260
+ */
1261
+ const excelIntDecoder = excelNumberDecoder.transform(intValidator);
1262
+ /**
1263
+ * A decoder that transforms Excel numbers to JS integers, also handling strings and boolean. Other types will be converted to null.
1264
+ *
1265
+ * If the number is not an integer, an error will be thrown.
1266
+ */
1267
+ const excelIntNullableDecoder = excelNumberNullableDecoder.transform(nullableTransformer(intValidator));
1268
+ const bigIntTransformer = (x, ctx) => {
1269
+ if (x % 1 !== 0) {
1270
+ ctx.addIssue({
1271
+ code: z$1.ZodIssueCode.custom,
1272
+ message: `Not an integer: ${x}`
1273
+ });
1274
+ return z$1.NEVER;
1275
+ }
1276
+ return BigInt(x);
1277
+ };
1278
+ /**
1279
+ * A decoder that transforms Excel numbers to JS BigInts, also handling strings and boolean. Other types will throw an error.
1280
+ *
1281
+ * If the number is not a (big) integer, an error will be thrown.
1282
+ */
1283
+ const excelBigIntStrictDecoder = excelNumberStrictDecoder.transform(bigIntTransformer);
1284
+ /**
1285
+ * A decoder that transforms Excel numbers to JS BigInts, also handling strings and boolean. Other types will be converted to 0.
1286
+ *
1287
+ * If the number is not a (big) integer, an error will be thrown.
1288
+ */
1289
+ const excelBigIntDecoder = excelNumberDecoder.transform(bigIntTransformer);
1290
+ /**
1291
+ * A decoder that transforms Excel numbers to JS BigInts, also handling strings and boolean. Other types will be converted to null.
1292
+ *
1293
+ * If the number is not a (big) integer, an error will be thrown.
1294
+ */
1295
+ const excelBigIntNullableDecoder = excelNumberNullableDecoder.transform(nullableTransformer(bigIntTransformer));
1296
+ const excelBooleanNullableTransformer = (x) => {
1297
+ if (typeof x === "number") return x !== 0;
1298
+ if (typeof x === "string") switch (x.toLowerCase()) {
1299
+ case "true":
1300
+ case "1": return true;
1301
+ case "false":
1302
+ case "0": return false;
1303
+ default: return null;
1304
+ }
1305
+ if (typeof x === "boolean") return x;
1306
+ return null;
1307
+ };
1308
+ /**
1309
+ * A decoder that transforms Excel booleans to JS booleans, also handling numbers and strings.
1310
+ *
1311
+ * It'll perform casting based on the following rules:
1312
+ * - If the value is a number, it'll return true if it's not 0
1313
+ * - If the value is a string, it'll return true if it's 'true' or '1', false if it's 'false' or '0', and throw an error otherwise
1314
+ * - If the value is boolean, it'll return it as-is
1315
+ * - If the value is any other type, it'll throw an error
1316
+ */
1317
+ const excelBooleanStrictDecoder = z$1.union([
1318
+ z$1.boolean(),
1319
+ z$1.number(),
1320
+ z$1.string()
1321
+ ]).transform((x, ctx) => {
1322
+ const result = excelBooleanNullableTransformer(x);
1323
+ if (result === null) {
1324
+ ctx.addIssue({
1325
+ code: z$1.ZodIssueCode.custom,
1326
+ message: `Not a boolean: ${x}`
1327
+ });
1328
+ return z$1.NEVER;
1329
+ }
1330
+ return result;
1331
+ });
1332
+ /**
1333
+ * A decoder that transforms Excel booleans to JS booleans, also handling numbers and strings.
1334
+ *
1335
+ * It'll perform casting based on the following rules:
1336
+ * - If the value is a number, it'll return true if it's not 0
1337
+ * - If the value is a string, it'll return true if it's 'true' or '1', false otherwise
1338
+ * - If the value is boolean, it'll return it as-is
1339
+ * - If the value is any other type, it'll return false
1340
+ */
1341
+ const excelBooleanDecoder = z$1.union([
1342
+ z$1.boolean(),
1343
+ z$1.number(),
1344
+ z$1.null(),
1345
+ z$1.string(),
1346
+ z$1.undefined(),
1347
+ z$1.any()
1348
+ ]).transform((x) => {
1349
+ const result = excelBooleanNullableTransformer(x);
1350
+ if (result === null) return false;
1351
+ return result;
1352
+ });
1353
+ /**
1354
+ * A decoder that transforms Excel booleans to JS booleans, also handling numbers and strings.
1355
+ *
1356
+ * It'll perform casting based on the following rules:
1357
+ * - If the value is a number, it'll return true if it's not 0
1358
+ * - If the value is a string, it'll return true if it's 'true' or '1', false if it's 'false' or '0', and null otherwise
1359
+ * - If the value is boolean, it'll return it as-is
1360
+ * - If the value is any other type, it'll return null
1361
+ */
1362
+ const excelBooleanNullableDecoder = z$1.union([
1363
+ z$1.boolean(),
1364
+ z$1.number(),
1365
+ z$1.null(),
1366
+ z$1.string(),
1367
+ z$1.undefined(),
1368
+ z$1.any()
1369
+ ]).transform(excelBooleanNullableTransformer);
1370
+ const excelDateNullableTransformer = (x, ctx) => {
1371
+ if (x === null || x === void 0) return null;
1372
+ if (isDate(x)) return x;
1373
+ if (typeof x === "number") {
1374
+ const date = new Date(Date.UTC(1900, 0, 1));
1375
+ date.setUTCDate(date.getDate() + x - 2);
1376
+ const fractionalDay = x - Math.floor(x);
1377
+ date.setMilliseconds(date.getMilliseconds() + fractionalDay * 24 * 60 * 60 * 1e3);
1378
+ return date;
1379
+ }
1380
+ if (typeof x === "string") {
1381
+ if (typeof x === "string" && x.trim() === "") return null;
1382
+ const result = new Date(x);
1383
+ if (Number.isNaN(result.getTime())) {
1384
+ ctx.addIssue({
1385
+ code: z$1.ZodIssueCode.custom,
1386
+ message: `Not a date: ${x}`
1387
+ });
1388
+ return z$1.NEVER;
1389
+ }
1390
+ return result;
1391
+ }
1392
+ return null;
1393
+ };
1394
+ /**
1395
+ * A decoder that transforms Excel dates to JS Dates
1396
+ */
1397
+ const excelDateDecoder = z$1.union([
1398
+ z$1.date(),
1399
+ z$1.number(),
1400
+ z$1.string()
1401
+ ]).transform((x, ctx) => {
1402
+ const result = excelDateNullableTransformer(x, ctx);
1403
+ if (result === null) {
1404
+ ctx.addIssue({
1405
+ code: z$1.ZodIssueCode.custom,
1406
+ message: `Not a date: ${x.toString()}`
1407
+ });
1408
+ return z$1.NEVER;
1409
+ }
1410
+ return result;
1411
+ });
1412
+ /**
1413
+ * A decoder that transforms nullable Excel dates to JS Dates
1414
+ */
1415
+ const excelDateNullableDecoder = z$1.union([
1416
+ z$1.date(),
1417
+ z$1.string(),
1418
+ z$1.number(),
1419
+ z$1.null(),
1420
+ z$1.undefined(),
1421
+ z$1.any()
1422
+ ]).transform(excelDateNullableTransformer);
1423
+
1424
+ //#endregion
1425
+ export { DefaultMap, ExhaustiveSwitchCheck, NestedMap, Result, TypedMapping, addAndSort, assertArray, assertType, blue, bold, booleanCode, capitalize, capitalizeKeys, commentLines, compare, compareBooleans, compareDates, compareFields, compareNumbers, compareStrings, compareValues, conjugateNames, cyan, dictGetAndAssert, ensureUnique, equals, err, excelBigIntDecoder, excelBigIntNullableDecoder, excelBigIntStrictDecoder, excelBooleanDecoder, excelBooleanNullableDecoder, excelBooleanStrictDecoder, excelDateDecoder, excelDateNullableDecoder, excelIntDecoder, excelIntNullableDecoder, excelIntStrictDecoder, excelNumberDecoder, excelNumberNullableDecoder, excelNumberStrictDecoder, excelStringDecoder, excelStringNullableDecoder, excelStringStrictDecoder, filterDict, filterMap, format, generateRandomAlphanumericString, gray, green, groupBy, groupByCurried, groupByToDict, groupByToMap, idToDict, inverse, isCamelCase, isDate, isKebabCase, isPascalCase, isPlural, isSlug, isSnakeCase, italic, keyGetter, lowerFirst, mapDict, mapGetAndAssert, mapLimit, mapMap, mapMapKeyValues, mapMapKeys, mapMapValues, mapToDict, ok, omit, pipeZodTransformers, pluralize, randomInt, red, removeSecrets, removeUndefinedProperties, setDirection, slice, slugify, sort, timeStamp, toCamelCase, toDict, toExcelColumnName, toExcelTableName, toHumanReadable, toKebabCase, toPascalCase, toSnakeCase, uncapitalize, uncapitalizeKeys, underline, unionErrorMessage, uniq, upperFirst, yellow };
1426
+ //# sourceMappingURL=zod-excel.decoders-BJliEI1Y.js.map