@alextheman/utility 5.11.3 → 5.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -63,77 +63,42 @@ function paralleliseArrays(firstArray, secondArray) {
63
63
  return outputArray;
64
64
  }
65
65
  //#endregion
66
- //#region src/root/types/APIError.ts
67
- const httpErrorCodeLookup = {
68
- 400: "BAD_REQUEST",
69
- 401: "UNAUTHORISED",
70
- 403: "FORBIDDEN",
71
- 404: "NOT_FOUND",
72
- 418: "I_AM_A_TEAPOT",
73
- 500: "INTERNAL_SERVER_ERROR"
74
- };
75
- /**
76
- * Represents common errors you may get from a HTTP API request.
77
- *
78
- * @category Types
79
- */
80
- var APIError = class APIError extends Error {
81
- status;
82
- /**
83
- * @param status - A HTTP status code. Can be any number, but numbers between 400 and 600 are encouraged to fit with HTTP status code conventions.
84
- * @param message - An error message to display alongside the status code.
85
- * @param options - Extra options to be passed to super Error constructor.
86
- */
87
- constructor(status = 500, message, options) {
88
- super(message, options);
89
- this.status = status;
90
- if (message) this.message = message;
91
- else this.message = httpErrorCodeLookup[this.status] ?? "API_ERROR";
92
- Object.defineProperty(this, "message", { enumerable: true });
93
- Object.setPrototypeOf(this, new.target.prototype);
94
- }
95
- /**
96
- * Checks whether the given input may have been caused by an APIError.
97
- *
98
- * @param input - The input to check.
99
- *
100
- * @returns `true` if the input is an APIError, and `false` otherwise. The type of the input will also be narrowed down to APIError if `true`.
101
- */
102
- static check(input) {
103
- if (input instanceof APIError) return true;
104
- const data = input;
105
- return typeof data === "object" && data !== null && typeof data?.status === "number" && typeof data?.message === "string";
106
- }
107
- };
108
- //#endregion
109
- //#region src/root/types/DataError.ts
66
+ //#region src/v6/CodeError.ts
110
67
  /**
111
- * Represents errors you may get that may've been caused by a specific piece of data.
68
+ * Represents errors that can be described using a standardised error code, and a human-readable error message.
112
69
  *
113
70
  * @category Types
114
71
  *
115
- * @template DataType - The type of the data that caused the error.
72
+ * @template ErrorCode The type of the standardised error code.
116
73
  */
117
- var DataError = class DataError extends Error {
74
+ var CodeError = class CodeError extends Error {
118
75
  code;
119
- data;
120
76
  /**
121
- * @param data - The data that caused the error.
122
77
  * @param code - A standardised code (e.g. UNEXPECTED_DATA).
123
78
  * @param message - A human-readable error message (e.g. The data provided is invalid).
124
79
  * @param options - Extra options to pass to super Error constructor.
125
80
  */
126
- constructor(data, code = "INVALID_DATA", message = "The data provided is invalid", options) {
81
+ constructor(code, message = "Something went wrong.", options) {
127
82
  super(message, options);
128
83
  if (Error.captureStackTrace) Error.captureStackTrace(this, new.target);
129
84
  this.name = new.target.name;
130
85
  this.code = code;
131
- this.data = data;
132
86
  Object.defineProperty(this, "message", { enumerable: true });
133
87
  Object.setPrototypeOf(this, new.target.prototype);
134
88
  }
89
+ /**
90
+ * Checks whether the given input may have been caused by a CodeError.
91
+ *
92
+ * @param input - The input to check.
93
+ *
94
+ * @returns `true` if the input is a CodeError, and `false` otherwise. The type of the input will also be narrowed down to CodeError if `true`.
95
+ */
96
+ static check(input) {
97
+ if (input instanceof CodeError) return true;
98
+ return typeof input === "object" && input !== null && "message" in input && typeof input.message === "string" && "code" in input && typeof input.code === "string";
99
+ }
135
100
  static checkCaughtError(error, options) {
136
- if (DataError.check(error)) {
101
+ if (this.check(error)) {
137
102
  if (options?.expectedCode && error.code !== options.expectedCode) throw new Error(normaliseIndents`The error code on the thrown error does not match the expected error code.
138
103
 
139
104
  Expected: ${options.expectedCode}
@@ -144,223 +109,110 @@ var DataError = class DataError extends Error {
144
109
  throw error;
145
110
  }
146
111
  /**
147
- * Checks whether the given input may have been caused by a DataError.
148
- *
149
- * @param input - The input to check.
150
- *
151
- * @returns `true` if the input is a DataError, and `false` otherwise. The type of the input will also be narrowed down to DataError if `true`.
152
- */
153
- static check(input) {
154
- if (input instanceof DataError) return true;
155
- const data = input;
156
- return typeof data === "object" && data !== null && typeof data.message === "string" && typeof data.code === "string" && "data" in data;
157
- }
158
- /**
159
- * Gets the thrown `DataError` from a given function if one was thrown, and re-throws any other errors, or throws a default `DataError` if no error thrown.
112
+ * Gets the thrown `CodeError` from a given function if one was thrown, and re-throws any other errors, or throws a default `CodeError` if no error thrown.
160
113
  *
161
114
  * @param errorFunction - The function expected to throw the error.
162
115
  * @param options - Extra options to apply.
163
116
  *
164
- * @throws {Error} Any other errors thrown by the `errorFunction` that are not a `DataError`.
165
- * @throws {Error} If no `DataError` was thrown by the `errorFunction`
117
+ * @throws {Error} Any other errors thrown by the `errorFunction` that are not a `CodeError`.
118
+ * @throws {Error} If no `CodeError` was thrown by the `errorFunction`
166
119
  *
167
- * @returns The `DataError` that was thrown by the `errorFunction`
120
+ * @returns The `CodeError` that was thrown by the `errorFunction`
168
121
  */
169
122
  static expectError(errorFunction, options) {
170
123
  try {
171
124
  errorFunction();
172
125
  } catch (error) {
173
- return DataError.checkCaughtError(error, options);
126
+ return this.checkCaughtError(error, options);
174
127
  }
175
- throw new Error("Expected a DataError to be thrown but none was thrown");
128
+ throw new Error(`Expected a ${this.name} to be thrown but none was thrown`);
176
129
  }
177
130
  /**
178
- * Gets the thrown `DataError` from a given asynchronous function if one was thrown, and re-throws any other errors, or throws a default `DataError` if no error thrown.
131
+ * Gets the thrown `CodeError` from a given asynchronous function if one was thrown, and re-throws any other errors, or throws a default `CodeError` if no error thrown.
179
132
  *
180
133
  * @param errorFunction - The function expected to throw the error.
181
134
  * @param options - Extra options to apply.
182
135
  *
183
- * @throws {Error} Any other errors thrown by the `errorFunction` that are not a `DataError`.
184
- * @throws {Error} If no `DataError` was thrown by the `errorFunction`
136
+ * @throws {Error} Any other errors thrown by the `errorFunction` that are not a `CodeError`.
137
+ * @throws {Error} If no `CodeError` was thrown by the `errorFunction`
185
138
  *
186
- * @returns The `DataError` that was thrown by the `errorFunction`
139
+ * @returns The `CodeError` that was thrown by the `errorFunction`
187
140
  */
188
141
  static async expectErrorAsync(errorFunction, options) {
189
142
  try {
190
143
  await errorFunction();
191
144
  } catch (error) {
192
- return DataError.checkCaughtError(error, options);
145
+ return this.checkCaughtError(error, options);
193
146
  }
194
- throw new Error("Expected a DataError to be thrown but none was thrown");
147
+ throw new Error(`Expected a ${this.name} to be thrown but none was thrown`);
195
148
  }
196
149
  };
197
150
  //#endregion
198
- //#region src/root/types/VersionNumber.ts
151
+ //#region src/v6/DataError.ts
199
152
  /**
200
- * Represents a software version number, considered to be made up of a major, minor, and patch part.
153
+ * Represents errors you may get that may've been caused by a specific piece of data.
201
154
  *
202
155
  * @category Types
156
+ *
157
+ * @template DataType - The type of the data that caused the error.
203
158
  */
204
- var VersionNumber = class VersionNumber {
205
- static NON_NEGATIVE_TUPLE_ERROR = "Input array must be a tuple of three non-negative integers.";
206
- /** The major number. Increments when a feature is removed or changed in a way that is not backwards-compatible with the previous release. */
207
- major = 0;
208
- /** The minor number. Increments when a new feature is added/deprecated and is expected to be backwards-compatible with the previous release. */
209
- minor = 0;
210
- /** The patch number. Increments when the next release is fixing a bug or doing a small refactor that should not be noticeable in practice. */
211
- patch = 0;
212
- /**
213
- * @param input - The input to create a new instance of `VersionNumber` from.
214
- */
215
- constructor(input) {
216
- if (input instanceof VersionNumber) {
217
- this.major = input.major;
218
- this.minor = input.minor;
219
- this.patch = input.patch;
220
- } else if (typeof input === "string") {
221
- if (!VERSION_NUMBER_REGEX.test(input)) throw new DataError({ input }, "INVALID_VERSION", `"${input}" is not a valid version number. Version numbers must be of the format "X.Y.Z" or "vX.Y.Z", where X, Y, and Z are non-negative integers.`);
222
- const [major, minor, patch] = VersionNumber.formatString(input, { omitPrefix: true }).split(".").map((number) => {
223
- return parseIntStrict(number);
224
- });
225
- this.major = major;
226
- this.minor = minor;
227
- this.patch = patch;
228
- } else if (Array.isArray(input)) {
229
- if (input.length !== 3) throw new DataError({ input }, "INVALID_LENGTH", VersionNumber.NON_NEGATIVE_TUPLE_ERROR);
230
- const [major, minor, patch] = input.map((number) => {
231
- const parsedInteger = parseIntStrict(number?.toString());
232
- if (parsedInteger < 0) throw new DataError({ input }, "NEGATIVE_INPUTS", VersionNumber.NON_NEGATIVE_TUPLE_ERROR);
233
- return parsedInteger;
234
- });
235
- this.major = major;
236
- this.minor = minor;
237
- this.patch = patch;
238
- } else throw new DataError({ input }, "INVALID_INPUT", normaliseIndents`
239
- The provided input can not be parsed into a valid version number.
240
- Expected either a string of format X.Y.Z or vX.Y.Z, a tuple of three numbers, or another \`VersionNumber\` instance.
241
- `);
242
- }
159
+ var DataError = class DataError extends CodeError {
160
+ data;
243
161
  /**
244
- * Gets the current version type of the current instance of `VersionNumber`.
245
- *
246
- * @returns Either `"major"`, `"minor"`, or `"patch"`, depending on the version type.
162
+ * @param data - The data that caused the error.
163
+ * @param code - A standardised code (e.g. UNEXPECTED_DATA).
164
+ * @param message - A human-readable error message (e.g. The data provided is invalid).
165
+ * @param options - Extra options to pass to super Error constructor.
247
166
  */
248
- get type() {
249
- if (this.minor === 0 && this.patch === 0) return VersionType.MAJOR;
250
- if (this.patch === 0) return VersionType.MINOR;
251
- return VersionType.PATCH;
252
- }
253
- static formatString(input, options) {
254
- if (options?.omitPrefix) return input.startsWith("v") ? input.slice(1) : input;
255
- return input.startsWith("v") ? input : `v${input}`;
167
+ constructor(data, code = "INVALID_DATA", message = "The data provided is invalid", options) {
168
+ super(code, message, options);
169
+ if (Error.captureStackTrace) Error.captureStackTrace(this, new.target);
170
+ this.name = new.target.name;
171
+ this.code = code;
172
+ this.data = data;
173
+ Object.defineProperty(this, "message", { enumerable: true });
174
+ Object.setPrototypeOf(this, new.target.prototype);
256
175
  }
257
176
  /**
258
- * Checks if the provided version numbers have the exact same major, minor, and patch numbers.
177
+ * Checks whether the given input may have been caused by a DataError.
259
178
  *
260
- * @param firstVersion - The first version number to compare.
261
- * @param secondVersion - The second version number to compare.
179
+ * @param input - The input to check.
262
180
  *
263
- * @returns `true` if the provided version numbers have exactly the same major, minor, and patch numbers, and returns `false` otherwise.
181
+ * @returns `true` if the input is a DataError, and `false` otherwise. The type of the input will also be narrowed down to DataError if `true`.
264
182
  */
265
- static isEqual(firstVersion, secondVersion) {
266
- return firstVersion.major === secondVersion.major && firstVersion.minor === secondVersion.minor && firstVersion.patch === secondVersion.patch;
183
+ static check(input) {
184
+ if (input instanceof DataError) return true;
185
+ return typeof input === "object" && input !== null && "message" in input && typeof input.message === "string" && "code" in input && typeof input.code === "string" && "data" in input;
267
186
  }
268
187
  /**
269
- * Get a formatted string representation of the current version number
270
- *
271
- * @param options - Options to apply to the string formatting.
188
+ * Gets the thrown `DataError` from a given function if one was thrown, and re-throws any other errors, or throws a default `DataError` if no error thrown.
272
189
  *
273
- * @returns A formatted string representation of the current version number with the options applied.
274
- */
275
- format(options) {
276
- let baseOutput = `${this.major}`;
277
- if (!options?.omitMinor) {
278
- baseOutput += `.${this.minor}`;
279
- if (!options?.omitPatch) baseOutput += `.${this.patch}`;
280
- }
281
- return VersionNumber.formatString(baseOutput, { omitPrefix: options?.omitPrefix });
282
- }
283
- /**
284
- * Increments the current version number by the given increment type, returning the result as a new reference in memory.
190
+ * @param errorFunction - The function expected to throw the error.
191
+ * @param options - Extra options to apply.
285
192
  *
286
- * @param incrementType - The type of increment. Can be one of the following:
287
- * - `"major"`: Change the major version `v1.2.3` `v2.0.0`
288
- * - `"minor"`: Change the minor version `v1.2.3` → `v1.3.0`
289
- * - `"patch"`: Change the patch version `v1.2.3` → `v1.2.4`
290
- * @param incrementAmount - The amount to increment by (defaults to 1).
193
+ * @throws {Error} Any other errors thrown by the `errorFunction` that are not a `DataError`.
194
+ * @throws {Error} If no `DataError` was thrown by the `errorFunction`
291
195
  *
292
- * @returns A new instance of `VersionNumber` with the increment applied.
196
+ * @returns The `DataError` that was thrown by the `errorFunction`
293
197
  */
294
- increment(incrementType, incrementAmount = 1) {
295
- const incrementBy = parseIntStrict(String(incrementAmount));
296
- const calculatedRawVersion = {
297
- major: [
298
- this.major + incrementBy,
299
- 0,
300
- 0
301
- ],
302
- minor: [
303
- this.major,
304
- this.minor + incrementBy,
305
- 0
306
- ],
307
- patch: [
308
- this.major,
309
- this.minor,
310
- this.patch + incrementBy
311
- ]
312
- }[incrementType];
313
- try {
314
- return new VersionNumber(calculatedRawVersion);
315
- } catch (error) {
316
- if (DataError.check(error) && error.code === "NEGATIVE_INPUTS") throw new DataError({
317
- currentVersion: this.toString(),
318
- calculatedRawVersion: `v${calculatedRawVersion.join(".")}`,
319
- incrementAmount
320
- }, "NEGATIVE_VERSION", "Cannot apply this increment amount as it would lead to a negative version number.");
321
- else throw error;
322
- }
198
+ static expectError(errorFunction, options) {
199
+ return super.expectError(errorFunction, options);
323
200
  }
324
201
  /**
325
- * Ensures that the VersionNumber behaves correctly when attempted to be coerced to a string.
326
- *
327
- * @param hint - Not used as of now, but generally used to help with numeric coercion, I think (which we most likely do not need for version numbers).
202
+ * Gets the thrown `DataError` from a given asynchronous function if one was thrown, and re-throws any other errors, or throws a default `DataError` if no error thrown.
328
203
  *
329
- * @returns A stringified representation of the current version number, prefixed with `v`.
330
- */
331
- [Symbol.toPrimitive](hint) {
332
- if (hint === "number") throw new DataError({ thisVersion: this.toString() }, "INVALID_COERCION", "VersionNumber cannot be coerced to a number type.");
333
- return this.toString();
334
- }
335
- /**
336
- * Ensures that the VersionNumber behaves correctly when attempted to be converted to JSON.
204
+ * @param errorFunction - The function expected to throw the error.
205
+ * @param options - Extra options to apply.
337
206
  *
338
- * @returns A stringified representation of the current version number, prefixed with `v`.
339
- */
340
- toJSON() {
341
- return this.toString();
342
- }
343
- /**
344
- * Get a string representation of the current version number.
207
+ * @throws {Error} Any other errors thrown by the `errorFunction` that are not a `DataError`.
208
+ * @throws {Error} If no `DataError` was thrown by the `errorFunction`
345
209
  *
346
- * @returns A stringified representation of the current version number with the prefix.
210
+ * @returns The `DataError` that was thrown by the `errorFunction`
347
211
  */
348
- toString() {
349
- const rawString = `${this.major}.${this.minor}.${this.patch}`;
350
- return VersionNumber.formatString(rawString, { omitPrefix: false });
212
+ static async expectErrorAsync(errorFunction, options) {
213
+ return await super.expectErrorAsync(errorFunction, options);
351
214
  }
352
215
  };
353
- const zodVersionNumber = z$1.union([
354
- z$1.string(),
355
- z$1.tuple([
356
- z$1.number(),
357
- z$1.number(),
358
- z$1.number()
359
- ]),
360
- z$1.instanceof(VersionNumber)
361
- ]).transform((rawVersionNumber) => {
362
- return new VersionNumber(rawVersionNumber);
363
- });
364
216
  //#endregion
365
217
  //#region src/root/functions/parsers/parseIntStrict.ts
366
218
  /**
@@ -760,350 +612,116 @@ function isOrdered(array) {
760
612
  return true;
761
613
  }
762
614
  //#endregion
763
- //#region src/root/functions/recursive/deepFreeze.ts
615
+ //#region src/root/functions/miscellaneous/sayHello.ts
764
616
  /**
765
- * Deeply freezes an object or array such that all child objects/arrays are also frozen.
766
- *
767
- * Note that this will also freeze the input itself as well.
768
- * If the intent is to create a newly frozen object with a different reference in memory, pass your object through deepCopy first before passing to deepFreeze.
769
- *
770
- * @category Recursive
771
- *
772
- * @template ObjectType - The type of the input object.
773
- *
774
- * @param object - The object to freeze. May also be an array.
775
- *
776
- * @returns The input object completely frozen.
777
- */
778
- function deepFreeze(object) {
779
- for (const value of Object.values(object)) {
780
- if (typeof value === "function") continue;
781
- if (value && typeof value === "object") deepFreeze(value);
782
- }
783
- return Object.freeze(object);
784
- }
785
- //#endregion
786
- //#region src/root/functions/taggedTemplate/createTemplateStringsArray.ts
787
- /**
788
- * Creates a template strings array given a regular array of strings
789
- *
790
- * @category Tagged Template
791
- *
792
- * @param strings - The array of strings.
793
- *
794
- * @returns A template strings array that can be passed as the first argument of any tagged template function.
795
- */
796
- function createTemplateStringsArray(strings) {
797
- return deepFreeze(Object.assign([...strings], { raw: [...strings] }));
798
- }
799
- //#endregion
800
- //#region src/root/functions/taggedTemplate/getStringsAndInterpolations.ts
801
- /**
802
- *
803
- * Gets the strings and interpolations separately from a template string.
804
- * You can pass a template string directly by doing:
805
- *
806
- * ```typescript
807
- * getStringsAndInterpolations`Template string here`;
808
- * ```
809
- *
810
- * @category Tagged Template
811
- *
812
- * @template InterpolationsType - The type of the interpolations.
813
- *
814
- * @param strings - The strings from the template to process.
815
- * @param interpolations - An array of all interpolations from the template.
816
- *
817
- * @returns A tuple where the first item is the strings from the template, and the remaining items are the interpolations.
818
- *
819
- * The return of this function may also be spread into any other tagged template function in the following way:
820
- *
821
- * ```typescript
822
- * import { interpolate } from "@alextheman/utility"; // Example function
823
- *
824
- * const packageName = "@alextheman/utility";
825
- * const packageManager = getPackageManager(packageName);
826
- *
827
- * interpolate(...getStringsAndInterpolations`The package ${packageName} uses the ${packageManager} package manager.`);
828
- * ```
829
- */
830
- function getStringsAndInterpolations(strings, ...interpolations) {
831
- if (strings.length !== interpolations.length + 1) throw new DataError({
832
- stringsLength: strings.length,
833
- interpolationsLength: interpolations.length,
834
- strings,
835
- interpolations
836
- }, "INVALID_STRINGS_AND_INTERPOLATIONS_LENGTH", "The length of the strings must be exactly one more than the length of the interpolations.");
837
- return [createTemplateStringsArray(strings), ...interpolations];
838
- }
839
- //#endregion
840
- //#region src/root/functions/taggedTemplate/interpolate.ts
841
- /**
842
- * Returns the result of interpolating a template string when given the strings and interpolations separately.
843
- *
844
- * You can pass a template string directly by doing:
845
- *
846
- * ```
847
- * interpolate`Template string here`;
848
- * ```
849
- *
850
- * In this case, it will be functionally the same as if you just wrote the template string by itself.
851
- *
852
- * @category Tagged Template
853
- *
854
- * @template InterpolationsType - The type of the interpolations.
855
- *
856
- * @param strings - The strings from the template to process.
857
- * @param interpolations - An array of all interpolations from the template.
858
- *
859
- * @returns A new string with the strings and interpolations from the template applied.
860
- */
861
- function interpolate(strings, ...interpolations) {
862
- let result = "";
863
- for (const [string, interpolation = ""] of paralleliseArrays(strings, interpolations)) result += string + interpolation;
864
- return result;
865
- }
866
- //#endregion
867
- //#region src/root/functions/taggedTemplate/interpolateObjects.ts
868
- /**
869
- * Returns the result of interpolating a template string, also stringifying objects.
870
- *
871
- * You can pass a template string directly by doing:
872
- *
873
- * ```typescript
874
- * interpolateObjects`Template string here ${{ my: "object" }}`;
875
- * ```
876
- *
877
- * @category Tagged Template
878
- *
879
- * @template InterpolationsType - The type of the interpolations.
880
- *
881
- * @param strings - The strings from the template to process.
882
- * @param interpolations - An array of all interpolations from the template.
883
- *
884
- * @returns A new string with the strings and interpolations from the template applied, with objects stringified.
885
- */
886
- function interpolateObjects(strings, ...interpolations) {
887
- let result = "";
888
- for (let i = 0; i < strings.length; i++) {
889
- result += strings[i];
890
- if (i !== strings.length - 1) result += interpolations[i] && typeof interpolations[i] === "object" ? JSON.stringify(interpolations[i]) : interpolations[i];
891
- }
892
- return result;
893
- }
894
- //#endregion
895
- //#region src/root/functions/taggedTemplate/isTemplateStringsArray.ts
896
- /**
897
- * Determines whether or not the input is a valid `TemplateStringsArray`.
898
- *
899
- * @category Tagged Template
900
- *
901
- * @param input - The input to check
902
- *
903
- * @returns `true` if the input is a valid `TemplateStringsArray`, and false otherwise. The type of the input will also be narrowed down to `TemplateStringsArray` if `true`.
904
- */
905
- function isTemplateStringsArray(input) {
906
- return typeof input === "object" && input !== null && "raw" in input;
907
- }
908
- //#endregion
909
- //#region src/root/functions/taggedTemplate/normaliseIndents.ts
910
- function calculateTabSize(line, whitespaceLength) {
911
- const potentialWhitespacePart = line.slice(0, whitespaceLength);
912
- const trimmedString = line.trimStart();
913
- if (potentialWhitespacePart.trim() !== "") return 0;
914
- const tabSize = line.length - (trimmedString.length + whitespaceLength);
915
- return tabSize < 0 ? 0 : tabSize;
916
- }
917
- function getWhitespaceLength(lines) {
918
- const [firstNonEmptyLine] = lines.filter((line) => {
919
- return line.trim() !== "";
920
- });
921
- return firstNonEmptyLine.length - firstNonEmptyLine.trimStart().length;
922
- }
923
- function reduceLines(lines, { preserveTabs = true }) {
924
- const slicedLines = lines.slice(1);
925
- const isFirstLineEmpty = lines[0].trim() === "";
926
- const whitespaceLength = getWhitespaceLength(isFirstLineEmpty ? lines : slicedLines);
927
- return (isFirstLineEmpty ? slicedLines : lines).map((line) => {
928
- const tabSize = calculateTabSize(line, whitespaceLength);
929
- return (preserveTabs ? fillArray(() => {
930
- return " ";
931
- }, tabSize).join("") : "") + line.trimStart();
932
- }).join("\n");
933
- }
934
- /**
935
- * Applies any options if provided, then removes any extraneous indents from a multi-line template string.
936
- *
937
- * You can pass a template string directly by doing:
938
- *
939
- * ```typescript
940
- * normaliseIndents`Template string here
941
- * with a new line
942
- * and another new line`;
943
- * ```
944
- *
945
- * You may also pass the options first, then invoke the resulting function with a template string:
946
- *
947
- * ```typescript
948
- * normaliseIndents({ preserveTabs: false })`Template string here
949
- * with a new line
950
- * and another new line`;
951
- * ```
952
- *
953
- * @category Tagged Template
954
- *
955
- * @param first - The strings from the template to process, or the options to apply.
956
- * @param args - An array of all interpolations from the template.
957
- *
958
- * @returns An additional function to invoke, or a new string with the strings and interpolations from the template applied, and extraneous indents removed.
959
- */
960
- function normaliseIndents(first, ...args) {
961
- if (typeof first === "object" && first !== null && !Array.isArray(first)) {
962
- const options = first;
963
- return (strings, ...interpolations) => {
964
- return normaliseIndents(strings, ...interpolations, options);
965
- };
966
- }
967
- const strings = first;
968
- const options = typeof args[args.length - 1] === "object" && !Array.isArray(args[args.length - 1]) ? args.pop() : {};
969
- return reduceLines(interpolate(strings, ...[...args]).split("\n"), options);
970
- }
971
- /**
972
- * Applies any options if provided, then removes any extraneous indents from a multi-line template string.
973
- *
974
- * You can pass a template string directly by doing:
975
- *
976
- * ```typescript
977
- * normalizeIndents`Template string here
978
- * with a new line
979
- * and another new line`.
980
- * ```
981
- *
982
- * You may also pass the options first, then invoke the resulting function with a template string:
983
- *
984
- * ```typescript
985
- * normalizeIndents({ preserveTabs: false })`Template string here
986
- * with a new line
987
- * and another new line`.
988
- * ```
989
- *
990
- * @param first - The strings from the template to process, or the options to apply.
991
- * @param args - An array of all interpolations from the template.
992
- *
993
- * @returns An additional function to invoke, or a new string with the strings and interpolations from the template applied, and extraneous indents removed.
994
- */
995
- const normalizeIndents = normaliseIndents;
996
- //#endregion
997
- //#region src/root/functions/miscellaneous/sayHello.ts
998
- /**
999
- * Returns a string representing the lyrics to the package's theme song, Commit To You
617
+ * Returns a string representing the lyrics to the package's theme song, Commit To You
1000
618
  *
1001
619
  * [Pls listen!](https://www.youtube.com/watch?v=mH-Sg-8EnxM)
1002
620
  *
1003
621
  * @returns The lyrics string in markdown format.
1004
622
  */
1005
623
  function sayHello() {
1006
- return normaliseIndents`
1007
- # Commit To You
624
+ return `
625
+ # Commit To You
1008
626
 
1009
- ### Verse 1
627
+ ### Verse 1
1010
628
 
1011
- I know you've been checking me out,
1012
- Shall we take it to the next level now?
1013
- 'Cause I really wanna be there all for you,
1014
- All for you!
1015
- Come on now, let's make a fresh start!
1016
- Pin my number, then you can take me out!
1017
- Can't you see I really do care about you,
1018
- About you!
629
+ I know you've been checking me out,
630
+ Shall we take it to the next level now?
631
+ 'Cause I really wanna be there all for you,
632
+ All for you!
633
+ Come on now, let's make a fresh start!
634
+ Pin my number, then you can take me out!
635
+ Can't you see I really do care about you,
636
+ About you!
1019
637
 
1020
- ### Pre-chorus 1
1021
- Although our calendars are imperfect, at best,
1022
- I'd like to organise time with you! (with you!).
1023
- Just tell me when and I'll make it clear,
1024
- All clear for you,
1025
- All clear for you!
1026
- (One, two, three, go!)
638
+ ### Pre-chorus 1
639
+ Although our calendars are imperfect, at best,
640
+ I'd like to organise time with you! (with you!).
641
+ Just tell me when and I'll make it clear,
642
+ All clear for you,
643
+ All clear for you!
644
+ (One, two, three, go!)
1027
645
 
1028
- ### Chorus
1029
- I wanna be of utility, I'll help you on the run!
1030
- I'll be the one here in the back, while you go have some fun!
1031
- Looking out for you tonight, I'll be the one you can rely on!
1032
- Watch you go and watch me pass by,
1033
- I'll be here!
1034
- I'll commit to you!
646
+ ### Chorus
647
+ I wanna be of utility, I'll help you on the run!
648
+ I'll be the one here in the back, while you go have some fun!
649
+ Looking out for you tonight, I'll be the one you can rely on!
650
+ Watch you go and watch me pass by,
651
+ I'll be here!
652
+ I'll commit to you!
1035
653
 
1036
- ### Verse 2
1037
- Though sometimes it won't be easy,
1038
- You'll be here to bring out the best in me,
1039
- And I'll hold myself to high standards for you!
1040
- All for you!
1041
- We'll grow as a pair, you and me,
1042
- We'll build up a healthy dependency,
1043
- You can build with me and I'll develop with you!
1044
- I'm with you!
654
+ ### Verse 2
655
+ Though sometimes it won't be easy,
656
+ You'll be here to bring out the best in me,
657
+ And I'll hold myself to high standards for you!
658
+ All for you!
659
+ We'll grow as a pair, you and me,
660
+ We'll build up a healthy dependency,
661
+ You can build with me and I'll develop with you!
662
+ I'm with you!
1045
663
 
1046
- ### Pre-chorus 2
1047
- I'll be with you when you're up or you're down,
1048
- We'll deal with all our problems together (together!)
1049
- Just tell me what you want, I'll make it clear,
1050
- All clear for you,
1051
- All clear for you!
1052
- (One, three, one, go!)
664
+ ### Pre-chorus 2
665
+ I'll be with you when you're up or you're down,
666
+ We'll deal with all our problems together (together!)
667
+ Just tell me what you want, I'll make it clear,
668
+ All clear for you,
669
+ All clear for you!
670
+ (One, three, one, go!)
1053
671
 
1054
- ### Chorus
1055
- I wanna be of utility, I'll help you on the run!
1056
- (help you on the run!)
1057
- I'll be the one here in the back, while you go have some fun!
1058
- (you go have some fun!)
1059
- Looking out for you tonight, I'll be the one you can rely on!
1060
- Watch you go and watch me pass by,
1061
- I'll be here!
1062
- I'll commit to you!
672
+ ### Chorus
673
+ I wanna be of utility, I'll help you on the run!
674
+ (help you on the run!)
675
+ I'll be the one here in the back, while you go have some fun!
676
+ (you go have some fun!)
677
+ Looking out for you tonight, I'll be the one you can rely on!
678
+ Watch you go and watch me pass by,
679
+ I'll be here!
680
+ I'll commit to you!
1063
681
 
1064
- ### Bridge
1065
- Looking into our stack!
1066
- I'll commit to you!
1067
- We've got a lot to unpack!
1068
- I'll commit to you!
1069
- The environment that we're in!
1070
- I'll commit to you!
1071
- Delicate as a string!
1072
- I'll commit to you!
682
+ ### Bridge
683
+ Looking into our stack!
684
+ I'll commit to you!
685
+ We've got a lot to unpack!
686
+ I'll commit to you!
687
+ The environment that we're in!
688
+ I'll commit to you!
689
+ Delicate as a string!
690
+ I'll commit to you!
1073
691
 
1074
- But I think you're my type!
1075
- I'll commit to you!
1076
- Oh, this feels all so right!
1077
- I'll commit to you!
1078
- Nothing stopping us now!
1079
- I'll commit to you!
1080
- Let's show them what we're about!
1081
- Two, three, four, go!
692
+ But I think you're my type!
693
+ I'll commit to you!
694
+ Oh, this feels all so right!
695
+ I'll commit to you!
696
+ Nothing stopping us now!
697
+ I'll commit to you!
698
+ Let's show them what we're about!
699
+ Two, three, four, go!
1082
700
 
1083
- ### Final Chorus
1084
- I wanna be of utility, I'll help you on the run!
1085
- (help you on the run!)
1086
- I'll be the one here in the back, while you go have some fun!
1087
- (you go have some fun!)
1088
- Looking out for you tonight, I'll be the one you can rely on!
1089
- Watch you go and watch me pass by,
1090
- I'll be here!
1091
- I'll commit to you!
701
+ ### Final Chorus
702
+ I wanna be of utility, I'll help you on the run!
703
+ (help you on the run!)
704
+ I'll be the one here in the back, while you go have some fun!
705
+ (you go have some fun!)
706
+ Looking out for you tonight, I'll be the one you can rely on!
707
+ Watch you go and watch me pass by,
708
+ I'll be here!
709
+ I'll commit to you!
1092
710
 
1093
- I wanna be of utility, I'll help you on the run!
1094
- (I'll commit to you!)
1095
- I'll be the one here in the back, while you go have some fun!
1096
- (I'll commit to you!)
1097
- Looking out for you tonight, I'll be the one you can rely on!
1098
- (I'll commit to you!)
1099
- Watch you go and watch me pass by,
1100
- (I'll commit to you!)
1101
- I'll be here!
711
+ I wanna be of utility, I'll help you on the run!
712
+ (I'll commit to you!)
713
+ I'll be the one here in the back, while you go have some fun!
714
+ (I'll commit to you!)
715
+ Looking out for you tonight, I'll be the one you can rely on!
716
+ (I'll commit to you!)
717
+ Watch you go and watch me pass by,
718
+ (I'll commit to you!)
719
+ I'll be here!
1102
720
 
1103
- ### Outro
1104
- I'll commit to you!
1105
- I'll commit to you!
1106
- I'll commit to you!
721
+ ### Outro
722
+ I'll commit to you!
723
+ I'll commit to you!
724
+ I'll commit to you!
1107
725
  `;
1108
726
  }
1109
727
  //#endregion
@@ -1290,7 +908,7 @@ function _parseZodSchema(parsedResult, input, onError) {
1290
908
  * @param input - The data to parse.
1291
909
  * @param onError - A custom error to throw on invalid data (defaults to `DataError`). May either be the error itself, or a function that returns the error or nothing. If nothing is returned, the default error is thrown instead.
1292
910
  *
1293
- * @throws {DataError} If the given data cannot be parsed according to the schema.
911
+ * @throws {DataErrorCode} If the given data cannot be parsed according to the schema.
1294
912
  *
1295
913
  * @returns The parsed data from the Zod schema.
1296
914
  */
@@ -1438,6 +1056,29 @@ function deepCopy(object) {
1438
1056
  return clonedObject;
1439
1057
  }
1440
1058
  //#endregion
1059
+ //#region src/root/functions/recursive/deepFreeze.ts
1060
+ /**
1061
+ * Deeply freezes an object or array such that all child objects/arrays are also frozen.
1062
+ *
1063
+ * Note that this will also freeze the input itself as well.
1064
+ * If the intent is to create a newly frozen object with a different reference in memory, pass your object through deepCopy first before passing to deepFreeze.
1065
+ *
1066
+ * @category Recursive
1067
+ *
1068
+ * @template ObjectType - The type of the input object.
1069
+ *
1070
+ * @param object - The object to freeze. May also be an array.
1071
+ *
1072
+ * @returns The input object completely frozen.
1073
+ */
1074
+ function deepFreeze(object) {
1075
+ for (const value of Object.values(object)) {
1076
+ if (typeof value === "function") continue;
1077
+ if (value && typeof value === "object") deepFreeze(value);
1078
+ }
1079
+ return Object.freeze(object);
1080
+ }
1081
+ //#endregion
1441
1082
  //#region src/root/functions/stringHelpers/appendSemicolon.ts
1442
1083
  /**
1443
1084
  * Appends a semicolon to the end of a string, trimming where necessary first.
@@ -1589,4 +1230,425 @@ function truncate(stringToTruncate, maxLength = 5) {
1589
1230
  return stringToTruncate.length > maxLength ? `${stringToTruncate.slice(0, maxLength)}...` : stringToTruncate;
1590
1231
  }
1591
1232
  //#endregion
1592
- export { APIError, DataError, Env, FILE_PATH_PATTERN, FILE_PATH_REGEX, ONE_DAY_IN_MILLISECONDS, UUID_PATTERN, UUID_REGEX, VERSION_NUMBER_PATTERN, VERSION_NUMBER_REGEX, VersionNumber, VersionType, addDaysToDate, appendSemicolon, calculateMonthlyDifference, camelToKebab, convertFileToBase64, createFormData, createTemplateStringsArray, deepCopy, deepFreeze, escapeRegexPattern, fillArray, formatDateAndTime, getRandomNumber, getRecordKeys, getStringsAndInterpolations, httpErrorCodeLookup, interpolate, interpolateObjects, isAnniversary, isLeapYear, isMonthlyMultiple, isOrdered, isSameDate, isTemplateStringsArray, kebabToCamel, normaliseIndents, normalizeIndents, omitProperties, paralleliseArrays, parseBoolean, parseEnv, parseFormData, parseIntStrict, parseUUID, parseVersionType, parseZodSchema, parseZodSchemaAsync, randomiseArray, range, removeDuplicates, removeUndefinedFromObject, sayHello, stringListToArray, stringifyDotenv, toTitleCase, truncate, wait, zodVersionNumber };
1233
+ //#region src/root/functions/taggedTemplate/createTemplateStringsArray.ts
1234
+ /**
1235
+ * Creates a template strings array given a regular array of strings
1236
+ *
1237
+ * @category Tagged Template
1238
+ *
1239
+ * @param strings - The array of strings.
1240
+ *
1241
+ * @returns A template strings array that can be passed as the first argument of any tagged template function.
1242
+ */
1243
+ function createTemplateStringsArray(strings) {
1244
+ return deepFreeze(Object.assign([...strings], { raw: [...strings] }));
1245
+ }
1246
+ //#endregion
1247
+ //#region src/root/functions/taggedTemplate/getStringsAndInterpolations.ts
1248
+ /**
1249
+ *
1250
+ * Gets the strings and interpolations separately from a template string.
1251
+ * You can pass a template string directly by doing:
1252
+ *
1253
+ * ```typescript
1254
+ * getStringsAndInterpolations`Template string here`;
1255
+ * ```
1256
+ *
1257
+ * @category Tagged Template
1258
+ *
1259
+ * @template InterpolationsType - The type of the interpolations.
1260
+ *
1261
+ * @param strings - The strings from the template to process.
1262
+ * @param interpolations - An array of all interpolations from the template.
1263
+ *
1264
+ * @returns A tuple where the first item is the strings from the template, and the remaining items are the interpolations.
1265
+ *
1266
+ * The return of this function may also be spread into any other tagged template function in the following way:
1267
+ *
1268
+ * ```typescript
1269
+ * import { interpolate } from "@alextheman/utility"; // Example function
1270
+ *
1271
+ * const packageName = "@alextheman/utility";
1272
+ * const packageManager = getPackageManager(packageName);
1273
+ *
1274
+ * interpolate(...getStringsAndInterpolations`The package ${packageName} uses the ${packageManager} package manager.`);
1275
+ * ```
1276
+ */
1277
+ function getStringsAndInterpolations(strings, ...interpolations) {
1278
+ if (strings.length !== interpolations.length + 1) throw new DataError({
1279
+ stringsLength: strings.length,
1280
+ interpolationsLength: interpolations.length,
1281
+ strings,
1282
+ interpolations
1283
+ }, "INVALID_STRINGS_AND_INTERPOLATIONS_LENGTH", "The length of the strings must be exactly one more than the length of the interpolations.");
1284
+ return [createTemplateStringsArray(strings), ...interpolations];
1285
+ }
1286
+ //#endregion
1287
+ //#region src/root/functions/taggedTemplate/interpolate.ts
1288
+ /**
1289
+ * Returns the result of interpolating a template string when given the strings and interpolations separately.
1290
+ *
1291
+ * You can pass a template string directly by doing:
1292
+ *
1293
+ * ```
1294
+ * interpolate`Template string here`;
1295
+ * ```
1296
+ *
1297
+ * In this case, it will be functionally the same as if you just wrote the template string by itself.
1298
+ *
1299
+ * @category Tagged Template
1300
+ *
1301
+ * @template InterpolationsType - The type of the interpolations.
1302
+ *
1303
+ * @param strings - The strings from the template to process.
1304
+ * @param interpolations - An array of all interpolations from the template.
1305
+ *
1306
+ * @returns A new string with the strings and interpolations from the template applied.
1307
+ */
1308
+ function interpolate(strings, ...interpolations) {
1309
+ let result = "";
1310
+ for (const [string, interpolation = ""] of paralleliseArrays(strings, interpolations)) result += string + interpolation;
1311
+ return result;
1312
+ }
1313
+ //#endregion
1314
+ //#region src/root/functions/taggedTemplate/interpolateObjects.ts
1315
+ /**
1316
+ * Returns the result of interpolating a template string, also stringifying objects.
1317
+ *
1318
+ * You can pass a template string directly by doing:
1319
+ *
1320
+ * ```typescript
1321
+ * interpolateObjects`Template string here ${{ my: "object" }}`;
1322
+ * ```
1323
+ *
1324
+ * @category Tagged Template
1325
+ *
1326
+ * @template InterpolationsType - The type of the interpolations.
1327
+ *
1328
+ * @param strings - The strings from the template to process.
1329
+ * @param interpolations - An array of all interpolations from the template.
1330
+ *
1331
+ * @returns A new string with the strings and interpolations from the template applied, with objects stringified.
1332
+ */
1333
+ function interpolateObjects(strings, ...interpolations) {
1334
+ let result = "";
1335
+ for (let i = 0; i < strings.length; i++) {
1336
+ result += strings[i];
1337
+ if (i !== strings.length - 1) result += interpolations[i] && typeof interpolations[i] === "object" ? JSON.stringify(interpolations[i]) : interpolations[i];
1338
+ }
1339
+ return result;
1340
+ }
1341
+ //#endregion
1342
+ //#region src/root/functions/taggedTemplate/isTemplateStringsArray.ts
1343
+ /**
1344
+ * Determines whether or not the input is a valid `TemplateStringsArray`.
1345
+ *
1346
+ * @category Tagged Template
1347
+ *
1348
+ * @param input - The input to check
1349
+ *
1350
+ * @returns `true` if the input is a valid `TemplateStringsArray`, and false otherwise. The type of the input will also be narrowed down to `TemplateStringsArray` if `true`.
1351
+ */
1352
+ function isTemplateStringsArray(input) {
1353
+ return typeof input === "object" && input !== null && "raw" in input;
1354
+ }
1355
+ //#endregion
1356
+ //#region src/root/functions/taggedTemplate/normaliseIndents.ts
1357
+ function calculateTabSize(line, whitespaceLength) {
1358
+ const potentialWhitespacePart = line.slice(0, whitespaceLength);
1359
+ const trimmedString = line.trimStart();
1360
+ if (potentialWhitespacePart.trim() !== "") return 0;
1361
+ const tabSize = line.length - (trimmedString.length + whitespaceLength);
1362
+ return tabSize < 0 ? 0 : tabSize;
1363
+ }
1364
+ function getWhitespaceLength(lines) {
1365
+ const [firstNonEmptyLine] = lines.filter((line) => {
1366
+ return line.trim() !== "";
1367
+ });
1368
+ return firstNonEmptyLine.length - firstNonEmptyLine.trimStart().length;
1369
+ }
1370
+ function reduceLines(lines, { preserveTabs = true }) {
1371
+ const slicedLines = lines.slice(1);
1372
+ const isFirstLineEmpty = lines[0].trim() === "";
1373
+ const whitespaceLength = getWhitespaceLength(isFirstLineEmpty ? lines : slicedLines);
1374
+ return (isFirstLineEmpty ? slicedLines : lines).map((line) => {
1375
+ const tabSize = calculateTabSize(line, whitespaceLength);
1376
+ return (preserveTabs ? fillArray(() => {
1377
+ return " ";
1378
+ }, tabSize).join("") : "") + line.trimStart();
1379
+ }).join("\n");
1380
+ }
1381
+ /**
1382
+ * Applies any options if provided, then removes any extraneous indents from a multi-line template string.
1383
+ *
1384
+ * You can pass a template string directly by doing:
1385
+ *
1386
+ * ```typescript
1387
+ * normaliseIndents`Template string here
1388
+ * with a new line
1389
+ * and another new line`;
1390
+ * ```
1391
+ *
1392
+ * You may also pass the options first, then invoke the resulting function with a template string:
1393
+ *
1394
+ * ```typescript
1395
+ * normaliseIndents({ preserveTabs: false })`Template string here
1396
+ * with a new line
1397
+ * and another new line`;
1398
+ * ```
1399
+ *
1400
+ * @category Tagged Template
1401
+ *
1402
+ * @param first - The strings from the template to process, or the options to apply.
1403
+ * @param args - An array of all interpolations from the template.
1404
+ *
1405
+ * @returns An additional function to invoke, or a new string with the strings and interpolations from the template applied, and extraneous indents removed.
1406
+ */
1407
+ function normaliseIndents(first, ...args) {
1408
+ if (typeof first === "object" && first !== null && !Array.isArray(first)) {
1409
+ const options = first;
1410
+ return (strings, ...interpolations) => {
1411
+ return normaliseIndents(strings, ...interpolations, options);
1412
+ };
1413
+ }
1414
+ const strings = first;
1415
+ const options = typeof args[args.length - 1] === "object" && !Array.isArray(args[args.length - 1]) ? args.pop() : {};
1416
+ return reduceLines(interpolate(strings, ...[...args]).split("\n"), options);
1417
+ }
1418
+ /**
1419
+ * Applies any options if provided, then removes any extraneous indents from a multi-line template string.
1420
+ *
1421
+ * You can pass a template string directly by doing:
1422
+ *
1423
+ * ```typescript
1424
+ * normalizeIndents`Template string here
1425
+ * with a new line
1426
+ * and another new line`.
1427
+ * ```
1428
+ *
1429
+ * You may also pass the options first, then invoke the resulting function with a template string:
1430
+ *
1431
+ * ```typescript
1432
+ * normalizeIndents({ preserveTabs: false })`Template string here
1433
+ * with a new line
1434
+ * and another new line`.
1435
+ * ```
1436
+ *
1437
+ * @param first - The strings from the template to process, or the options to apply.
1438
+ * @param args - An array of all interpolations from the template.
1439
+ *
1440
+ * @returns An additional function to invoke, or a new string with the strings and interpolations from the template applied, and extraneous indents removed.
1441
+ */
1442
+ const normalizeIndents = normaliseIndents;
1443
+ //#endregion
1444
+ //#region src/root/types/APIError.ts
1445
+ const httpErrorCodeLookup = {
1446
+ 400: "BAD_REQUEST",
1447
+ 401: "UNAUTHORISED",
1448
+ 403: "FORBIDDEN",
1449
+ 404: "NOT_FOUND",
1450
+ 418: "I_AM_A_TEAPOT",
1451
+ 500: "INTERNAL_SERVER_ERROR"
1452
+ };
1453
+ /**
1454
+ * Represents common errors you may get from a HTTP API request.
1455
+ *
1456
+ * @category Types
1457
+ */
1458
+ var APIError = class APIError extends Error {
1459
+ status;
1460
+ /**
1461
+ * @param status - A HTTP status code. Can be any number, but numbers between 400 and 600 are encouraged to fit with HTTP status code conventions.
1462
+ * @param message - An error message to display alongside the status code.
1463
+ * @param options - Extra options to be passed to super Error constructor.
1464
+ */
1465
+ constructor(status = 500, message, options) {
1466
+ super(message, options);
1467
+ this.status = status;
1468
+ if (message) this.message = message;
1469
+ else this.message = httpErrorCodeLookup[this.status] ?? "API_ERROR";
1470
+ Object.defineProperty(this, "message", { enumerable: true });
1471
+ Object.setPrototypeOf(this, new.target.prototype);
1472
+ }
1473
+ /**
1474
+ * Checks whether the given input may have been caused by an APIError.
1475
+ *
1476
+ * @param input - The input to check.
1477
+ *
1478
+ * @returns `true` if the input is an APIError, and `false` otherwise. The type of the input will also be narrowed down to APIError if `true`.
1479
+ */
1480
+ static check(input) {
1481
+ if (input instanceof APIError) return true;
1482
+ const data = input;
1483
+ return typeof data === "object" && data !== null && typeof data?.status === "number" && typeof data?.message === "string";
1484
+ }
1485
+ };
1486
+ //#endregion
1487
+ //#region src/root/types/VersionNumber.ts
1488
+ /**
1489
+ * Represents a software version number, considered to be made up of a major, minor, and patch part.
1490
+ *
1491
+ * @category Types
1492
+ */
1493
+ var VersionNumber = class VersionNumber {
1494
+ static NON_NEGATIVE_TUPLE_ERROR = "Input array must be a tuple of three non-negative integers.";
1495
+ /** The major number. Increments when a feature is removed or changed in a way that is not backwards-compatible with the previous release. */
1496
+ major = 0;
1497
+ /** The minor number. Increments when a new feature is added/deprecated and is expected to be backwards-compatible with the previous release. */
1498
+ minor = 0;
1499
+ /** The patch number. Increments when the next release is fixing a bug or doing a small refactor that should not be noticeable in practice. */
1500
+ patch = 0;
1501
+ /**
1502
+ * @param input - The input to create a new instance of `VersionNumber` from.
1503
+ */
1504
+ constructor(input) {
1505
+ if (input instanceof VersionNumber) {
1506
+ this.major = input.major;
1507
+ this.minor = input.minor;
1508
+ this.patch = input.patch;
1509
+ } else if (typeof input === "string") {
1510
+ if (!VERSION_NUMBER_REGEX.test(input)) throw new DataError({ input }, "INVALID_VERSION", `"${input}" is not a valid version number. Version numbers must be of the format "X.Y.Z" or "vX.Y.Z", where X, Y, and Z are non-negative integers.`);
1511
+ const [major, minor, patch] = VersionNumber.formatString(input, { omitPrefix: true }).split(".").map((number) => {
1512
+ return parseIntStrict(number);
1513
+ });
1514
+ this.major = major;
1515
+ this.minor = minor;
1516
+ this.patch = patch;
1517
+ } else if (Array.isArray(input)) {
1518
+ if (input.length !== 3) throw new DataError({ input }, "INVALID_LENGTH", VersionNumber.NON_NEGATIVE_TUPLE_ERROR);
1519
+ const [major, minor, patch] = input.map((number) => {
1520
+ const parsedInteger = parseIntStrict(number?.toString());
1521
+ if (parsedInteger < 0) throw new DataError({ input }, "NEGATIVE_INPUTS", VersionNumber.NON_NEGATIVE_TUPLE_ERROR);
1522
+ return parsedInteger;
1523
+ });
1524
+ this.major = major;
1525
+ this.minor = minor;
1526
+ this.patch = patch;
1527
+ } else throw new DataError({ input }, "INVALID_INPUT", normaliseIndents`
1528
+ The provided input can not be parsed into a valid version number.
1529
+ Expected either a string of format X.Y.Z or vX.Y.Z, a tuple of three numbers, or another \`VersionNumber\` instance.
1530
+ `);
1531
+ }
1532
+ /**
1533
+ * Gets the current version type of the current instance of `VersionNumber`.
1534
+ *
1535
+ * @returns Either `"major"`, `"minor"`, or `"patch"`, depending on the version type.
1536
+ */
1537
+ get type() {
1538
+ if (this.minor === 0 && this.patch === 0) return VersionType.MAJOR;
1539
+ if (this.patch === 0) return VersionType.MINOR;
1540
+ return VersionType.PATCH;
1541
+ }
1542
+ static formatString(input, options) {
1543
+ if (options?.omitPrefix) return input.startsWith("v") ? input.slice(1) : input;
1544
+ return input.startsWith("v") ? input : `v${input}`;
1545
+ }
1546
+ /**
1547
+ * Checks if the provided version numbers have the exact same major, minor, and patch numbers.
1548
+ *
1549
+ * @param firstVersion - The first version number to compare.
1550
+ * @param secondVersion - The second version number to compare.
1551
+ *
1552
+ * @returns `true` if the provided version numbers have exactly the same major, minor, and patch numbers, and returns `false` otherwise.
1553
+ */
1554
+ static isEqual(firstVersion, secondVersion) {
1555
+ return firstVersion.major === secondVersion.major && firstVersion.minor === secondVersion.minor && firstVersion.patch === secondVersion.patch;
1556
+ }
1557
+ /**
1558
+ * Get a formatted string representation of the current version number
1559
+ *
1560
+ * @param options - Options to apply to the string formatting.
1561
+ *
1562
+ * @returns A formatted string representation of the current version number with the options applied.
1563
+ */
1564
+ format(options) {
1565
+ let baseOutput = `${this.major}`;
1566
+ if (!options?.omitMinor) {
1567
+ baseOutput += `.${this.minor}`;
1568
+ if (!options?.omitPatch) baseOutput += `.${this.patch}`;
1569
+ }
1570
+ return VersionNumber.formatString(baseOutput, { omitPrefix: options?.omitPrefix });
1571
+ }
1572
+ /**
1573
+ * Increments the current version number by the given increment type, returning the result as a new reference in memory.
1574
+ *
1575
+ * @param incrementType - The type of increment. Can be one of the following:
1576
+ * - `"major"`: Change the major version `v1.2.3` → `v2.0.0`
1577
+ * - `"minor"`: Change the minor version `v1.2.3` → `v1.3.0`
1578
+ * - `"patch"`: Change the patch version `v1.2.3` → `v1.2.4`
1579
+ * @param incrementAmount - The amount to increment by (defaults to 1).
1580
+ *
1581
+ * @returns A new instance of `VersionNumber` with the increment applied.
1582
+ */
1583
+ increment(incrementType, incrementAmount = 1) {
1584
+ const incrementBy = parseIntStrict(String(incrementAmount));
1585
+ const calculatedRawVersion = {
1586
+ major: [
1587
+ this.major + incrementBy,
1588
+ 0,
1589
+ 0
1590
+ ],
1591
+ minor: [
1592
+ this.major,
1593
+ this.minor + incrementBy,
1594
+ 0
1595
+ ],
1596
+ patch: [
1597
+ this.major,
1598
+ this.minor,
1599
+ this.patch + incrementBy
1600
+ ]
1601
+ }[incrementType];
1602
+ try {
1603
+ return new VersionNumber(calculatedRawVersion);
1604
+ } catch (error) {
1605
+ if (DataError.check(error) && error.code === "NEGATIVE_INPUTS") throw new DataError({
1606
+ currentVersion: this.toString(),
1607
+ calculatedRawVersion: `v${calculatedRawVersion.join(".")}`,
1608
+ incrementAmount
1609
+ }, "NEGATIVE_VERSION", "Cannot apply this increment amount as it would lead to a negative version number.");
1610
+ else throw error;
1611
+ }
1612
+ }
1613
+ /**
1614
+ * Ensures that the VersionNumber behaves correctly when attempted to be coerced to a string.
1615
+ *
1616
+ * @param hint - Not used as of now, but generally used to help with numeric coercion, I think (which we most likely do not need for version numbers).
1617
+ *
1618
+ * @returns A stringified representation of the current version number, prefixed with `v`.
1619
+ */
1620
+ [Symbol.toPrimitive](hint) {
1621
+ if (hint === "number") throw new DataError({ thisVersion: this.toString() }, "INVALID_COERCION", "VersionNumber cannot be coerced to a number type.");
1622
+ return this.toString();
1623
+ }
1624
+ /**
1625
+ * Ensures that the VersionNumber behaves correctly when attempted to be converted to JSON.
1626
+ *
1627
+ * @returns A stringified representation of the current version number, prefixed with `v`.
1628
+ */
1629
+ toJSON() {
1630
+ return this.toString();
1631
+ }
1632
+ /**
1633
+ * Get a string representation of the current version number.
1634
+ *
1635
+ * @returns A stringified representation of the current version number with the prefix.
1636
+ */
1637
+ toString() {
1638
+ const rawString = `${this.major}.${this.minor}.${this.patch}`;
1639
+ return VersionNumber.formatString(rawString, { omitPrefix: false });
1640
+ }
1641
+ };
1642
+ const zodVersionNumber = z$1.union([
1643
+ z$1.string(),
1644
+ z$1.tuple([
1645
+ z$1.number(),
1646
+ z$1.number(),
1647
+ z$1.number()
1648
+ ]),
1649
+ z$1.instanceof(VersionNumber)
1650
+ ]).transform((rawVersionNumber) => {
1651
+ return new VersionNumber(rawVersionNumber);
1652
+ });
1653
+ //#endregion
1654
+ export { APIError, Env, FILE_PATH_PATTERN, FILE_PATH_REGEX, ONE_DAY_IN_MILLISECONDS, UUID_PATTERN, UUID_REGEX, VERSION_NUMBER_PATTERN, VERSION_NUMBER_REGEX, VersionNumber, VersionType, addDaysToDate, appendSemicolon, calculateMonthlyDifference, camelToKebab, convertFileToBase64, createFormData, createTemplateStringsArray, deepCopy, deepFreeze, escapeRegexPattern, fillArray, formatDateAndTime, getRandomNumber, getRecordKeys, getStringsAndInterpolations, httpErrorCodeLookup, interpolate, interpolateObjects, isAnniversary, isLeapYear, isMonthlyMultiple, isOrdered, isSameDate, isTemplateStringsArray, kebabToCamel, normaliseIndents, normalizeIndents, omitProperties, paralleliseArrays, parseBoolean, parseEnv, parseFormData, parseIntStrict, parseUUID, parseVersionType, parseZodSchema, parseZodSchemaAsync, randomiseArray, range, removeDuplicates, removeUndefinedFromObject, sayHello, stringListToArray, stringifyDotenv, toTitleCase, truncate, wait, zodVersionNumber };