@alextheman/utility 5.12.0 → 5.13.1

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.cjs CHANGED
@@ -87,77 +87,42 @@ function paralleliseArrays(firstArray, secondArray) {
87
87
  return outputArray;
88
88
  }
89
89
  //#endregion
90
- //#region src/root/types/APIError.ts
91
- const httpErrorCodeLookup = {
92
- 400: "BAD_REQUEST",
93
- 401: "UNAUTHORISED",
94
- 403: "FORBIDDEN",
95
- 404: "NOT_FOUND",
96
- 418: "I_AM_A_TEAPOT",
97
- 500: "INTERNAL_SERVER_ERROR"
98
- };
99
- /**
100
- * Represents common errors you may get from a HTTP API request.
101
- *
102
- * @category Types
103
- */
104
- var APIError = class APIError extends Error {
105
- status;
106
- /**
107
- * @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.
108
- * @param message - An error message to display alongside the status code.
109
- * @param options - Extra options to be passed to super Error constructor.
110
- */
111
- constructor(status = 500, message, options) {
112
- super(message, options);
113
- this.status = status;
114
- if (message) this.message = message;
115
- else this.message = httpErrorCodeLookup[this.status] ?? "API_ERROR";
116
- Object.defineProperty(this, "message", { enumerable: true });
117
- Object.setPrototypeOf(this, new.target.prototype);
118
- }
119
- /**
120
- * Checks whether the given input may have been caused by an APIError.
121
- *
122
- * @param input - The input to check.
123
- *
124
- * @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`.
125
- */
126
- static check(input) {
127
- if (input instanceof APIError) return true;
128
- const data = input;
129
- return typeof data === "object" && data !== null && typeof data?.status === "number" && typeof data?.message === "string";
130
- }
131
- };
132
- //#endregion
133
- //#region src/root/types/DataError.ts
90
+ //#region src/v6/CodeError.ts
134
91
  /**
135
- * Represents errors you may get that may've been caused by a specific piece of data.
92
+ * Represents errors that can be described using a standardised error code, and a human-readable error message.
136
93
  *
137
94
  * @category Types
138
95
  *
139
- * @template DataType - The type of the data that caused the error.
96
+ * @template ErrorCode The type of the standardised error code.
140
97
  */
141
- var DataError = class DataError extends Error {
98
+ var CodeError = class CodeError extends Error {
142
99
  code;
143
- data;
144
100
  /**
145
- * @param data - The data that caused the error.
146
101
  * @param code - A standardised code (e.g. UNEXPECTED_DATA).
147
102
  * @param message - A human-readable error message (e.g. The data provided is invalid).
148
103
  * @param options - Extra options to pass to super Error constructor.
149
104
  */
150
- constructor(data, code = "INVALID_DATA", message = "The data provided is invalid", options) {
105
+ constructor(code, message = "Something went wrong.", options) {
151
106
  super(message, options);
152
107
  if (Error.captureStackTrace) Error.captureStackTrace(this, new.target);
153
108
  this.name = new.target.name;
154
109
  this.code = code;
155
- this.data = data;
156
110
  Object.defineProperty(this, "message", { enumerable: true });
157
111
  Object.setPrototypeOf(this, new.target.prototype);
158
112
  }
113
+ /**
114
+ * Checks whether the given input may have been caused by a CodeError.
115
+ *
116
+ * @param input - The input to check.
117
+ *
118
+ * @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`.
119
+ */
120
+ static check(input) {
121
+ if (input instanceof CodeError) return true;
122
+ return typeof input === "object" && input !== null && "message" in input && typeof input.message === "string" && "code" in input && typeof input.code === "string";
123
+ }
159
124
  static checkCaughtError(error, options) {
160
- if (DataError.check(error)) {
125
+ if (this.check(error)) {
161
126
  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.
162
127
 
163
128
  Expected: ${options.expectedCode}
@@ -168,223 +133,110 @@ var DataError = class DataError extends Error {
168
133
  throw error;
169
134
  }
170
135
  /**
171
- * Checks whether the given input may have been caused by a DataError.
172
- *
173
- * @param input - The input to check.
174
- *
175
- * @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`.
176
- */
177
- static check(input) {
178
- if (input instanceof DataError) return true;
179
- const data = input;
180
- return typeof data === "object" && data !== null && typeof data.message === "string" && typeof data.code === "string" && "data" in data;
181
- }
182
- /**
183
- * 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.
136
+ * 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.
184
137
  *
185
138
  * @param errorFunction - The function expected to throw the error.
186
139
  * @param options - Extra options to apply.
187
140
  *
188
- * @throws {Error} Any other errors thrown by the `errorFunction` that are not a `DataError`.
189
- * @throws {Error} If no `DataError` was thrown by the `errorFunction`
141
+ * @throws {Error} Any other errors thrown by the `errorFunction` that are not a `CodeError`.
142
+ * @throws {Error} If no `CodeError` was thrown by the `errorFunction`
190
143
  *
191
- * @returns The `DataError` that was thrown by the `errorFunction`
144
+ * @returns The `CodeError` that was thrown by the `errorFunction`
192
145
  */
193
146
  static expectError(errorFunction, options) {
194
147
  try {
195
148
  errorFunction();
196
149
  } catch (error) {
197
- return DataError.checkCaughtError(error, options);
150
+ return this.checkCaughtError(error, options);
198
151
  }
199
- throw new Error("Expected a DataError to be thrown but none was thrown");
152
+ throw new Error(`Expected a ${this.name} to be thrown but none was thrown`);
200
153
  }
201
154
  /**
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.
155
+ * 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.
203
156
  *
204
157
  * @param errorFunction - The function expected to throw the error.
205
158
  * @param options - Extra options to apply.
206
159
  *
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`
160
+ * @throws {Error} Any other errors thrown by the `errorFunction` that are not a `CodeError`.
161
+ * @throws {Error} If no `CodeError` was thrown by the `errorFunction`
209
162
  *
210
- * @returns The `DataError` that was thrown by the `errorFunction`
163
+ * @returns The `CodeError` that was thrown by the `errorFunction`
211
164
  */
212
165
  static async expectErrorAsync(errorFunction, options) {
213
166
  try {
214
167
  await errorFunction();
215
168
  } catch (error) {
216
- return DataError.checkCaughtError(error, options);
169
+ return this.checkCaughtError(error, options);
217
170
  }
218
- throw new Error("Expected a DataError to be thrown but none was thrown");
171
+ throw new Error(`Expected a ${this.name} to be thrown but none was thrown`);
219
172
  }
220
173
  };
221
174
  //#endregion
222
- //#region src/root/types/VersionNumber.ts
175
+ //#region src/v6/DataError.ts
223
176
  /**
224
- * Represents a software version number, considered to be made up of a major, minor, and patch part.
177
+ * Represents errors you may get that may've been caused by a specific piece of data.
225
178
  *
226
179
  * @category Types
180
+ *
181
+ * @template DataType - The type of the data that caused the error.
227
182
  */
228
- var VersionNumber = class VersionNumber {
229
- static NON_NEGATIVE_TUPLE_ERROR = "Input array must be a tuple of three non-negative integers.";
230
- /** The major number. Increments when a feature is removed or changed in a way that is not backwards-compatible with the previous release. */
231
- major = 0;
232
- /** The minor number. Increments when a new feature is added/deprecated and is expected to be backwards-compatible with the previous release. */
233
- minor = 0;
234
- /** The patch number. Increments when the next release is fixing a bug or doing a small refactor that should not be noticeable in practice. */
235
- patch = 0;
236
- /**
237
- * @param input - The input to create a new instance of `VersionNumber` from.
238
- */
239
- constructor(input) {
240
- if (input instanceof VersionNumber) {
241
- this.major = input.major;
242
- this.minor = input.minor;
243
- this.patch = input.patch;
244
- } else if (typeof input === "string") {
245
- 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.`);
246
- const [major, minor, patch] = VersionNumber.formatString(input, { omitPrefix: true }).split(".").map((number) => {
247
- return parseIntStrict(number);
248
- });
249
- this.major = major;
250
- this.minor = minor;
251
- this.patch = patch;
252
- } else if (Array.isArray(input)) {
253
- if (input.length !== 3) throw new DataError({ input }, "INVALID_LENGTH", VersionNumber.NON_NEGATIVE_TUPLE_ERROR);
254
- const [major, minor, patch] = input.map((number) => {
255
- const parsedInteger = parseIntStrict(number?.toString());
256
- if (parsedInteger < 0) throw new DataError({ input }, "NEGATIVE_INPUTS", VersionNumber.NON_NEGATIVE_TUPLE_ERROR);
257
- return parsedInteger;
258
- });
259
- this.major = major;
260
- this.minor = minor;
261
- this.patch = patch;
262
- } else throw new DataError({ input }, "INVALID_INPUT", normaliseIndents`
263
- The provided input can not be parsed into a valid version number.
264
- Expected either a string of format X.Y.Z or vX.Y.Z, a tuple of three numbers, or another \`VersionNumber\` instance.
265
- `);
266
- }
183
+ var DataError$1 = class DataError$1 extends CodeError {
184
+ data;
267
185
  /**
268
- * Gets the current version type of the current instance of `VersionNumber`.
269
- *
270
- * @returns Either `"major"`, `"minor"`, or `"patch"`, depending on the version type.
186
+ * @param data - The data that caused the error.
187
+ * @param code - A standardised code (e.g. UNEXPECTED_DATA).
188
+ * @param message - A human-readable error message (e.g. The data provided is invalid).
189
+ * @param options - Extra options to pass to super Error constructor.
271
190
  */
272
- get type() {
273
- if (this.minor === 0 && this.patch === 0) return VersionType.MAJOR;
274
- if (this.patch === 0) return VersionType.MINOR;
275
- return VersionType.PATCH;
276
- }
277
- static formatString(input, options) {
278
- if (options?.omitPrefix) return input.startsWith("v") ? input.slice(1) : input;
279
- return input.startsWith("v") ? input : `v${input}`;
191
+ constructor(data, code = "INVALID_DATA", message = "The data provided is invalid", options) {
192
+ super(code, message, options);
193
+ if (Error.captureStackTrace) Error.captureStackTrace(this, new.target);
194
+ this.name = new.target.name;
195
+ this.code = code;
196
+ this.data = data;
197
+ Object.defineProperty(this, "message", { enumerable: true });
198
+ Object.setPrototypeOf(this, new.target.prototype);
280
199
  }
281
200
  /**
282
- * Checks if the provided version numbers have the exact same major, minor, and patch numbers.
201
+ * Checks whether the given input may have been caused by a DataError.
283
202
  *
284
- * @param firstVersion - The first version number to compare.
285
- * @param secondVersion - The second version number to compare.
203
+ * @param input - The input to check.
286
204
  *
287
- * @returns `true` if the provided version numbers have exactly the same major, minor, and patch numbers, and returns `false` otherwise.
205
+ * @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`.
288
206
  */
289
- static isEqual(firstVersion, secondVersion) {
290
- return firstVersion.major === secondVersion.major && firstVersion.minor === secondVersion.minor && firstVersion.patch === secondVersion.patch;
207
+ static check(input) {
208
+ if (input instanceof DataError$1) return true;
209
+ return typeof input === "object" && input !== null && "message" in input && typeof input.message === "string" && "code" in input && typeof input.code === "string" && "data" in input;
291
210
  }
292
211
  /**
293
- * Get a formatted string representation of the current version number
294
- *
295
- * @param options - Options to apply to the string formatting.
212
+ * 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.
296
213
  *
297
- * @returns A formatted string representation of the current version number with the options applied.
298
- */
299
- format(options) {
300
- let baseOutput = `${this.major}`;
301
- if (!options?.omitMinor) {
302
- baseOutput += `.${this.minor}`;
303
- if (!options?.omitPatch) baseOutput += `.${this.patch}`;
304
- }
305
- return VersionNumber.formatString(baseOutput, { omitPrefix: options?.omitPrefix });
306
- }
307
- /**
308
- * Increments the current version number by the given increment type, returning the result as a new reference in memory.
214
+ * @param errorFunction - The function expected to throw the error.
215
+ * @param options - Extra options to apply.
309
216
  *
310
- * @param incrementType - The type of increment. Can be one of the following:
311
- * - `"major"`: Change the major version `v1.2.3` `v2.0.0`
312
- * - `"minor"`: Change the minor version `v1.2.3` → `v1.3.0`
313
- * - `"patch"`: Change the patch version `v1.2.3` → `v1.2.4`
314
- * @param incrementAmount - The amount to increment by (defaults to 1).
217
+ * @throws {Error} Any other errors thrown by the `errorFunction` that are not a `DataError`.
218
+ * @throws {Error} If no `DataError` was thrown by the `errorFunction`
315
219
  *
316
- * @returns A new instance of `VersionNumber` with the increment applied.
220
+ * @returns The `DataError` that was thrown by the `errorFunction`
317
221
  */
318
- increment(incrementType, incrementAmount = 1) {
319
- const incrementBy = parseIntStrict(String(incrementAmount));
320
- const calculatedRawVersion = {
321
- major: [
322
- this.major + incrementBy,
323
- 0,
324
- 0
325
- ],
326
- minor: [
327
- this.major,
328
- this.minor + incrementBy,
329
- 0
330
- ],
331
- patch: [
332
- this.major,
333
- this.minor,
334
- this.patch + incrementBy
335
- ]
336
- }[incrementType];
337
- try {
338
- return new VersionNumber(calculatedRawVersion);
339
- } catch (error) {
340
- if (DataError.check(error) && error.code === "NEGATIVE_INPUTS") throw new DataError({
341
- currentVersion: this.toString(),
342
- calculatedRawVersion: `v${calculatedRawVersion.join(".")}`,
343
- incrementAmount
344
- }, "NEGATIVE_VERSION", "Cannot apply this increment amount as it would lead to a negative version number.");
345
- else throw error;
346
- }
222
+ static expectError(errorFunction, options) {
223
+ return super.expectError(errorFunction, options);
347
224
  }
348
225
  /**
349
- * Ensures that the VersionNumber behaves correctly when attempted to be coerced to a string.
350
- *
351
- * @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).
226
+ * 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.
352
227
  *
353
- * @returns A stringified representation of the current version number, prefixed with `v`.
354
- */
355
- [Symbol.toPrimitive](hint) {
356
- if (hint === "number") throw new DataError({ thisVersion: this.toString() }, "INVALID_COERCION", "VersionNumber cannot be coerced to a number type.");
357
- return this.toString();
358
- }
359
- /**
360
- * Ensures that the VersionNumber behaves correctly when attempted to be converted to JSON.
228
+ * @param errorFunction - The function expected to throw the error.
229
+ * @param options - Extra options to apply.
361
230
  *
362
- * @returns A stringified representation of the current version number, prefixed with `v`.
363
- */
364
- toJSON() {
365
- return this.toString();
366
- }
367
- /**
368
- * Get a string representation of the current version number.
231
+ * @throws {Error} Any other errors thrown by the `errorFunction` that are not a `DataError`.
232
+ * @throws {Error} If no `DataError` was thrown by the `errorFunction`
369
233
  *
370
- * @returns A stringified representation of the current version number with the prefix.
234
+ * @returns The `DataError` that was thrown by the `errorFunction`
371
235
  */
372
- toString() {
373
- const rawString = `${this.major}.${this.minor}.${this.patch}`;
374
- return VersionNumber.formatString(rawString, { omitPrefix: false });
236
+ static async expectErrorAsync(errorFunction, options) {
237
+ return await super.expectErrorAsync(errorFunction, options);
375
238
  }
376
239
  };
377
- const zodVersionNumber = zod.default.union([
378
- zod.default.string(),
379
- zod.default.tuple([
380
- zod.default.number(),
381
- zod.default.number(),
382
- zod.default.number()
383
- ]),
384
- zod.default.instanceof(VersionNumber)
385
- ]).transform((rawVersionNumber) => {
386
- return new VersionNumber(rawVersionNumber);
387
- });
388
240
  //#endregion
389
241
  //#region src/root/functions/parsers/parseIntStrict.ts
390
242
  /**
@@ -402,18 +254,18 @@ const zodVersionNumber = zod.default.union([
402
254
  function parseIntStrict(string, radix) {
403
255
  const trimmedString = string.trim();
404
256
  const maxAllowedAlphabeticalCharacter = radix && radix > 10 && radix <= 36 ? String.fromCharCode(87 + radix - 1) : void 0;
405
- if (!(radix && radix > 10 && radix <= 36 ? new RegExp(`^[+-]?[0-9a-${maxAllowedAlphabeticalCharacter}]+$`, "i") : /^[+-]?\d+$/).test(trimmedString)) throw new DataError(radix ? {
257
+ if (!(radix && radix > 10 && radix <= 36 ? new RegExp(`^[+-]?[0-9a-${maxAllowedAlphabeticalCharacter}]+$`, "i") : /^[+-]?\d+$/).test(trimmedString)) throw new DataError$1(radix ? {
406
258
  string,
407
259
  radix
408
260
  } : { string }, "INTEGER_PARSING_ERROR", `Only numeric values${radix && radix > 10 && radix <= 36 ? ` or character${radix !== 11 ? "s" : ""} A${radix !== 11 ? `-${maxAllowedAlphabeticalCharacter?.toUpperCase()} ` : " "}` : " "}are allowed.`);
409
261
  if (radix && radix < 10 && [...trimmedString.replace(/^[+-]/, "")].some((character) => {
410
262
  return parseInt(character) >= radix;
411
- })) throw new DataError({
263
+ })) throw new DataError$1({
412
264
  string,
413
265
  radix
414
266
  }, "INTEGER_PARSING_ERROR", "Value contains one or more digits outside of the range of the given radix.");
415
267
  const parseIntResult = parseInt(trimmedString, radix);
416
- if (isNaN(parseIntResult)) throw new DataError({ string }, "INTEGER_PARSING_ERROR", "Value is not a valid integer.");
268
+ if (isNaN(parseIntResult)) throw new DataError$1({ string }, "INTEGER_PARSING_ERROR", "Value is not a valid integer.");
417
269
  return parseIntResult;
418
270
  }
419
271
  //#endregion
@@ -476,16 +328,16 @@ function randomiseArray(array) {
476
328
  */
477
329
  function range(start, stop, step = 1) {
478
330
  const numbers = [];
479
- if (step === 0) throw new DataError({ step }, "ZERO_STEP_SIZE", "Step size cannot be zero.");
331
+ if (step === 0) throw new DataError$1({ step }, "ZERO_STEP_SIZE", "Step size cannot be zero.");
480
332
  else if (step > 0) {
481
- if (start > stop) throw new DataError({
333
+ if (start > stop) throw new DataError$1({
482
334
  start,
483
335
  stop,
484
336
  step
485
337
  }, "INVALID_BOUNDARIES", "The starting value cannot be bigger than the final value if step is positive");
486
338
  for (let i = start; i < stop; i += step) numbers.push(i);
487
339
  } else if (step < 0) {
488
- if (start < stop) throw new DataError({
340
+ if (start < stop) throw new DataError$1({
489
341
  start,
490
342
  stop,
491
343
  step
@@ -685,7 +537,7 @@ function convertFileToBase64(file) {
685
537
  reader.readAsDataURL(file);
686
538
  reader.onload = () => {
687
539
  if (reader.result === null) {
688
- reject(new DataError({ result: reader.result }, "FILE_CONVERSION_ERROR", "Could not convert the given file."));
540
+ reject(new DataError$1({ result: reader.result }, "FILE_CONVERSION_ERROR", "Could not convert the given file."));
689
541
  return;
690
542
  }
691
543
  resolve(reader.result);
@@ -752,10 +604,10 @@ function createFormData(data, options = {
752
604
  if (Array.isArray(value)) {
753
605
  if (value.some((item) => {
754
606
  return item instanceof Blob;
755
- }) && (options.arrayResolution === "stringify" || typeof options.arrayResolution === "object" && options.arrayResolution[key] === "stringify")) throw new DataError({ value }, "CANNOT_STRINGIFY_BLOB", "Files/blobs cannot be stringified. Please change the resolution option for this item to \"multiple\" instead.");
607
+ }) && (options.arrayResolution === "stringify" || typeof options.arrayResolution === "object" && options.arrayResolution[key] === "stringify")) throw new DataError$1({ value }, "CANNOT_STRINGIFY_BLOB", "Files/blobs cannot be stringified. Please change the resolution option for this item to \"multiple\" instead.");
756
608
  if (options.arrayResolution === "multiple" || typeof options.arrayResolution === "object" && options.arrayResolution[key] === "multiple") {
757
609
  for (const item of value) {
758
- if (!isPrimitive(item) && !(item instanceof Blob)) throw new DataError({ item }, "NON_PRIMITIVE_ARRAY_ITEMS_FOUND", "Cannot directly add non-primitive data to FormData. Please change the resolution option for this item to \"stringify\" instead.");
610
+ if (!isPrimitive(item) && !(item instanceof Blob)) throw new DataError$1({ item }, "NON_PRIMITIVE_ARRAY_ITEMS_FOUND", "Cannot directly add non-primitive data to FormData. Please change the resolution option for this item to \"stringify\" instead.");
759
611
  if (item instanceof Blob) formData.append(String(key), item);
760
612
  else formData.append(String(key), String(item));
761
613
  }
@@ -784,231 +636,116 @@ function isOrdered(array) {
784
636
  return true;
785
637
  }
786
638
  //#endregion
787
- //#region src/root/functions/taggedTemplate/interpolate.ts
639
+ //#region src/root/functions/miscellaneous/sayHello.ts
788
640
  /**
789
- * Returns the result of interpolating a template string when given the strings and interpolations separately.
641
+ * Returns a string representing the lyrics to the package's theme song, Commit To You
790
642
  *
791
- * You can pass a template string directly by doing:
792
- *
793
- * ```
794
- * interpolate`Template string here`;
795
- * ```
796
- *
797
- * In this case, it will be functionally the same as if you just wrote the template string by itself.
798
- *
799
- * @category Tagged Template
800
- *
801
- * @template InterpolationsType - The type of the interpolations.
802
- *
803
- * @param strings - The strings from the template to process.
804
- * @param interpolations - An array of all interpolations from the template.
805
- *
806
- * @returns A new string with the strings and interpolations from the template applied.
807
- */
808
- function interpolate(strings, ...interpolations) {
809
- let result = "";
810
- for (const [string, interpolation = ""] of paralleliseArrays(strings, interpolations)) result += string + interpolation;
811
- return result;
812
- }
813
- //#endregion
814
- //#region src/root/functions/taggedTemplate/normaliseIndents.ts
815
- function calculateTabSize(line, whitespaceLength) {
816
- const potentialWhitespacePart = line.slice(0, whitespaceLength);
817
- const trimmedString = line.trimStart();
818
- if (potentialWhitespacePart.trim() !== "") return 0;
819
- const tabSize = line.length - (trimmedString.length + whitespaceLength);
820
- return tabSize < 0 ? 0 : tabSize;
821
- }
822
- function getWhitespaceLength(lines) {
823
- const [firstNonEmptyLine] = lines.filter((line) => {
824
- return line.trim() !== "";
825
- });
826
- return firstNonEmptyLine.length - firstNonEmptyLine.trimStart().length;
827
- }
828
- function reduceLines(lines, { preserveTabs = true }) {
829
- const slicedLines = lines.slice(1);
830
- const isFirstLineEmpty = lines[0].trim() === "";
831
- const whitespaceLength = getWhitespaceLength(isFirstLineEmpty ? lines : slicedLines);
832
- return (isFirstLineEmpty ? slicedLines : lines).map((line) => {
833
- const tabSize = calculateTabSize(line, whitespaceLength);
834
- return (preserveTabs ? fillArray(() => {
835
- return " ";
836
- }, tabSize).join("") : "") + line.trimStart();
837
- }).join("\n");
838
- }
839
- /**
840
- * Applies any options if provided, then removes any extraneous indents from a multi-line template string.
841
- *
842
- * You can pass a template string directly by doing:
843
- *
844
- * ```typescript
845
- * normaliseIndents`Template string here
846
- * with a new line
847
- * and another new line`;
848
- * ```
849
- *
850
- * You may also pass the options first, then invoke the resulting function with a template string:
851
- *
852
- * ```typescript
853
- * normaliseIndents({ preserveTabs: false })`Template string here
854
- * with a new line
855
- * and another new line`;
856
- * ```
857
- *
858
- * @category Tagged Template
859
- *
860
- * @param first - The strings from the template to process, or the options to apply.
861
- * @param args - An array of all interpolations from the template.
862
- *
863
- * @returns An additional function to invoke, or a new string with the strings and interpolations from the template applied, and extraneous indents removed.
864
- */
865
- function normaliseIndents(first, ...args) {
866
- if (typeof first === "object" && first !== null && !Array.isArray(first)) {
867
- const options = first;
868
- return (strings, ...interpolations) => {
869
- return normaliseIndents(strings, ...interpolations, options);
870
- };
871
- }
872
- const strings = first;
873
- const options = typeof args[args.length - 1] === "object" && !Array.isArray(args[args.length - 1]) ? args.pop() : {};
874
- return reduceLines(interpolate(strings, ...[...args]).split("\n"), options);
875
- }
876
- /**
877
- * Applies any options if provided, then removes any extraneous indents from a multi-line template string.
878
- *
879
- * You can pass a template string directly by doing:
880
- *
881
- * ```typescript
882
- * normalizeIndents`Template string here
883
- * with a new line
884
- * and another new line`.
885
- * ```
886
- *
887
- * You may also pass the options first, then invoke the resulting function with a template string:
888
- *
889
- * ```typescript
890
- * normalizeIndents({ preserveTabs: false })`Template string here
891
- * with a new line
892
- * and another new line`.
893
- * ```
894
- *
895
- * @param first - The strings from the template to process, or the options to apply.
896
- * @param args - An array of all interpolations from the template.
897
- *
898
- * @returns An additional function to invoke, or a new string with the strings and interpolations from the template applied, and extraneous indents removed.
899
- */
900
- const normalizeIndents = normaliseIndents;
901
- //#endregion
902
- //#region src/root/functions/miscellaneous/sayHello.ts
903
- /**
904
- * Returns a string representing the lyrics to the package's theme song, Commit To You
905
- *
906
- * [Pls listen!](https://www.youtube.com/watch?v=mH-Sg-8EnxM)
643
+ * [Pls listen!](https://www.youtube.com/watch?v=mH-Sg-8EnxM)
907
644
  *
908
645
  * @returns The lyrics string in markdown format.
909
646
  */
910
647
  function sayHello() {
911
- return normaliseIndents`
912
- # Commit To You
648
+ return `
649
+ # Commit To You
913
650
 
914
- ### Verse 1
651
+ ### Verse 1
915
652
 
916
- I know you've been checking me out,
917
- Shall we take it to the next level now?
918
- 'Cause I really wanna be there all for you,
919
- All for you!
920
- Come on now, let's make a fresh start!
921
- Pin my number, then you can take me out!
922
- Can't you see I really do care about you,
923
- About you!
653
+ I know you've been checking me out,
654
+ Shall we take it to the next level now?
655
+ 'Cause I really wanna be there all for you,
656
+ All for you!
657
+ Come on now, let's make a fresh start!
658
+ Pin my number, then you can take me out!
659
+ Can't you see I really do care about you,
660
+ About you!
924
661
 
925
- ### Pre-chorus 1
926
- Although our calendars are imperfect, at best,
927
- I'd like to organise time with you! (with you!).
928
- Just tell me when and I'll make it clear,
929
- All clear for you,
930
- All clear for you!
931
- (One, two, three, go!)
662
+ ### Pre-chorus 1
663
+ Although our calendars are imperfect, at best,
664
+ I'd like to organise time with you! (with you!).
665
+ Just tell me when and I'll make it clear,
666
+ All clear for you,
667
+ All clear for you!
668
+ (One, two, three, go!)
932
669
 
933
- ### Chorus
934
- I wanna be of utility, I'll help you on the run!
935
- I'll be the one here in the back, while you go have some fun!
936
- Looking out for you tonight, I'll be the one you can rely on!
937
- Watch you go and watch me pass by,
938
- I'll be here!
939
- I'll commit to you!
670
+ ### Chorus
671
+ I wanna be of utility, I'll help you on the run!
672
+ I'll be the one here in the back, while you go have some fun!
673
+ Looking out for you tonight, I'll be the one you can rely on!
674
+ Watch you go and watch me pass by,
675
+ I'll be here!
676
+ I'll commit to you!
940
677
 
941
- ### Verse 2
942
- Though sometimes it won't be easy,
943
- You'll be here to bring out the best in me,
944
- And I'll hold myself to high standards for you!
945
- All for you!
946
- We'll grow as a pair, you and me,
947
- We'll build up a healthy dependency,
948
- You can build with me and I'll develop with you!
949
- I'm with you!
678
+ ### Verse 2
679
+ Though sometimes it won't be easy,
680
+ You'll be here to bring out the best in me,
681
+ And I'll hold myself to high standards for you!
682
+ All for you!
683
+ We'll grow as a pair, you and me,
684
+ We'll build up a healthy dependency,
685
+ You can build with me and I'll develop with you!
686
+ I'm with you!
950
687
 
951
- ### Pre-chorus 2
952
- I'll be with you when you're up or you're down,
953
- We'll deal with all our problems together (together!)
954
- Just tell me what you want, I'll make it clear,
955
- All clear for you,
956
- All clear for you!
957
- (One, three, one, go!)
688
+ ### Pre-chorus 2
689
+ I'll be with you when you're up or you're down,
690
+ We'll deal with all our problems together (together!)
691
+ Just tell me what you want, I'll make it clear,
692
+ All clear for you,
693
+ All clear for you!
694
+ (One, three, one, go!)
958
695
 
959
- ### Chorus
960
- I wanna be of utility, I'll help you on the run!
961
- (help you on the run!)
962
- I'll be the one here in the back, while you go have some fun!
963
- (you go have some fun!)
964
- Looking out for you tonight, I'll be the one you can rely on!
965
- Watch you go and watch me pass by,
966
- I'll be here!
967
- I'll commit to you!
696
+ ### Chorus
697
+ I wanna be of utility, I'll help you on the run!
698
+ (help you on the run!)
699
+ I'll be the one here in the back, while you go have some fun!
700
+ (you go have some fun!)
701
+ Looking out for you tonight, I'll be the one you can rely on!
702
+ Watch you go and watch me pass by,
703
+ I'll be here!
704
+ I'll commit to you!
968
705
 
969
- ### Bridge
970
- Looking into our stack!
971
- I'll commit to you!
972
- We've got a lot to unpack!
973
- I'll commit to you!
974
- The environment that we're in!
975
- I'll commit to you!
976
- Delicate as a string!
977
- I'll commit to you!
706
+ ### Bridge
707
+ Looking into our stack!
708
+ I'll commit to you!
709
+ We've got a lot to unpack!
710
+ I'll commit to you!
711
+ The environment that we're in!
712
+ I'll commit to you!
713
+ Delicate as a string!
714
+ I'll commit to you!
978
715
 
979
- But I think you're my type!
980
- I'll commit to you!
981
- Oh, this feels all so right!
982
- I'll commit to you!
983
- Nothing stopping us now!
984
- I'll commit to you!
985
- Let's show them what we're about!
986
- Two, three, four, go!
716
+ But I think you're my type!
717
+ I'll commit to you!
718
+ Oh, this feels all so right!
719
+ I'll commit to you!
720
+ Nothing stopping us now!
721
+ I'll commit to you!
722
+ Let's show them what we're about!
723
+ Two, three, four, go!
987
724
 
988
- ### Final Chorus
989
- I wanna be of utility, I'll help you on the run!
990
- (help you on the run!)
991
- I'll be the one here in the back, while you go have some fun!
992
- (you go have some fun!)
993
- Looking out for you tonight, I'll be the one you can rely on!
994
- Watch you go and watch me pass by,
995
- I'll be here!
996
- I'll commit to you!
725
+ ### Final Chorus
726
+ I wanna be of utility, I'll help you on the run!
727
+ (help you on the run!)
728
+ I'll be the one here in the back, while you go have some fun!
729
+ (you go have some fun!)
730
+ Looking out for you tonight, I'll be the one you can rely on!
731
+ Watch you go and watch me pass by,
732
+ I'll be here!
733
+ I'll commit to you!
997
734
 
998
- I wanna be of utility, I'll help you on the run!
999
- (I'll commit to you!)
1000
- I'll be the one here in the back, while you go have some fun!
1001
- (I'll commit to you!)
1002
- Looking out for you tonight, I'll be the one you can rely on!
1003
- (I'll commit to you!)
1004
- Watch you go and watch me pass by,
1005
- (I'll commit to you!)
1006
- I'll be here!
735
+ I wanna be of utility, I'll help you on the run!
736
+ (I'll commit to you!)
737
+ I'll be the one here in the back, while you go have some fun!
738
+ (I'll commit to you!)
739
+ Looking out for you tonight, I'll be the one you can rely on!
740
+ (I'll commit to you!)
741
+ Watch you go and watch me pass by,
742
+ (I'll commit to you!)
743
+ I'll be here!
1007
744
 
1008
- ### Outro
1009
- I'll commit to you!
1010
- I'll commit to you!
1011
- I'll commit to you!
745
+ ### Outro
746
+ I'll commit to you!
747
+ I'll commit to you!
748
+ I'll commit to you!
1012
749
  `;
1013
750
  }
1014
751
  //#endregion
@@ -1026,8 +763,8 @@ function stringifyDotenv(contents, options) {
1026
763
  const { quoteStyle = "double" } = options ?? {};
1027
764
  let result = "";
1028
765
  for (const key in contentsCopy) {
1029
- if (/[ \t\r\n]/.test(key)) throw new DataError({ [key]: contentsCopy[key] }, "INVALID_KEY", "Environment variables are not allowed to have whitespace.");
1030
- if (quoteStyle === "none") if (/[ \t\r\n]/.test(contentsCopy[key]) || contentsCopy[key].includes("#") || contentsCopy[key].includes("=") || contentsCopy[key].includes("\\")) throw new DataError({ [key]: contentsCopy[key] }, "INCOMPATIBLE_QUOTE_STYLE", "Cannot use `{ quoteStyle: \"none\" }` when value has whitespace, #, =, or \\");
766
+ if (/[ \t\r\n]/.test(key)) throw new DataError$1({ [key]: contentsCopy[key] }, "INVALID_KEY", "Environment variables are not allowed to have whitespace.");
767
+ if (quoteStyle === "none") if (/[ \t\r\n]/.test(contentsCopy[key]) || contentsCopy[key].includes("#") || contentsCopy[key].includes("=") || contentsCopy[key].includes("\\")) throw new DataError$1({ [key]: contentsCopy[key] }, "INCOMPATIBLE_QUOTE_STYLE", "Cannot use `{ quoteStyle: \"none\" }` when value has whitespace, #, =, or \\");
1031
768
  else {
1032
769
  result += `${key}=${contentsCopy[key]}\n`;
1033
770
  continue;
@@ -1152,7 +889,7 @@ function removeUndefinedFromObject(object) {
1152
889
  */
1153
890
  function parseBoolean(inputString) {
1154
891
  const normalisedString = inputString.toLowerCase();
1155
- if (!["true", "false"].includes(normalisedString)) throw new DataError({ inputString }, "INVALID_BOOLEAN_STRING", "The provided boolean string must be one of `true | false`");
892
+ if (!["true", "false"].includes(normalisedString)) throw new DataError$1({ inputString }, "INVALID_BOOLEAN_STRING", "The provided boolean string must be one of `true | false`");
1156
893
  return normalisedString === "true";
1157
894
  }
1158
895
  //#endregion
@@ -1171,7 +908,7 @@ function _parseZodSchema(parsedResult, input, onError) {
1171
908
  const code = issue.code.toUpperCase();
1172
909
  allErrorCodes[code] = (allErrorCodes[code] ?? 0) + 1;
1173
910
  }
1174
- throw new DataError({ input }, Object.entries(allErrorCodes).toSorted(([_, firstCount], [__, secondCount]) => {
911
+ throw new DataError$1({ input }, Object.entries(allErrorCodes).toSorted(([_, firstCount], [__, secondCount]) => {
1175
912
  return secondCount - firstCount;
1176
913
  }).map(([code, count], _, allErrorCodes) => {
1177
914
  return allErrorCodes.length === 1 && count === 1 ? code : `${code}×${count}`;
@@ -1195,7 +932,7 @@ function _parseZodSchema(parsedResult, input, onError) {
1195
932
  * @param input - The data to parse.
1196
933
  * @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.
1197
934
  *
1198
- * @throws {DataError} If the given data cannot be parsed according to the schema.
935
+ * @throws {DataErrorCode} If the given data cannot be parsed according to the schema.
1199
936
  *
1200
937
  * @returns The parsed data from the Zod schema.
1201
938
  */
@@ -1226,7 +963,7 @@ const Env = {
1226
963
  * @returns The specified environment if allowed.
1227
964
  */
1228
965
  function parseEnv(input) {
1229
- return parseZodSchema(zod.z.enum(Env), input, new DataError({ input }, "INVALID_ENV", "The provided environment type must be one of `test | development | production`"));
966
+ return parseZodSchema(zod.z.enum(Env), input, new DataError$1({ input }, "INVALID_ENV", "The provided environment type must be one of `test | development | production`"));
1230
967
  }
1231
968
  //#endregion
1232
969
  //#region src/root/functions/parsers/parseFormData.ts
@@ -1264,8 +1001,8 @@ function parseFormData(formData, dataParser) {
1264
1001
  * @returns The UUID again if successful.
1265
1002
  */
1266
1003
  function parseUUID(input) {
1267
- if (!(typeof input === "string")) throw new DataError({ input }, "INVALID_TYPE", "Invalid type - expected string.");
1268
- if (!UUID_REGEX.test(input)) throw new DataError({ input }, "INVALID_UUID", "The provided input does not match the expected shape for a UUID.");
1004
+ if (!(typeof input === "string")) throw new DataError$1({ input }, "INVALID_TYPE", "Invalid type - expected string.");
1005
+ if (!UUID_REGEX.test(input)) throw new DataError$1({ input }, "INVALID_UUID", "The provided input does not match the expected shape for a UUID.");
1269
1006
  return input;
1270
1007
  }
1271
1008
  //#endregion
@@ -1292,7 +1029,7 @@ const VersionType = {
1292
1029
  * @returns The given version type if allowed.
1293
1030
  */
1294
1031
  function parseVersionType(input) {
1295
- return parseZodSchema(zod.default.enum(VersionType), input, new DataError({ input }, "INVALID_VERSION_TYPE", "The provided version type must be one of `major | minor | patch`"));
1032
+ return parseZodSchema(zod.default.enum(VersionType), input, new DataError$1({ input }, "INVALID_VERSION_TYPE", "The provided version type must be one of `major | minor | patch`"));
1296
1033
  }
1297
1034
  //#endregion
1298
1035
  //#region src/root/functions/parsers/zod/parseZodSchemaAsync.ts
@@ -1379,7 +1116,7 @@ function deepFreeze(object) {
1379
1116
  * @returns A string with the semicolon appended.
1380
1117
  */
1381
1118
  function appendSemicolon(stringToAppendTo) {
1382
- if (stringToAppendTo.includes("\n")) throw new DataError({ stringToAppendTo }, "MULTIPLE_LINE_ERROR", "Cannot append semicolon to multi-line string.");
1119
+ if (stringToAppendTo.includes("\n")) throw new DataError$1({ stringToAppendTo }, "MULTIPLE_LINE_ERROR", "Cannot append semicolon to multi-line string.");
1383
1120
  const stringWithNoTrailingWhitespace = stringToAppendTo.trimEnd();
1384
1121
  if (stringWithNoTrailingWhitespace === "") return "";
1385
1122
  return stringWithNoTrailingWhitespace[stringWithNoTrailingWhitespace.length - 1] === ";" ? stringWithNoTrailingWhitespace : `${stringWithNoTrailingWhitespace};`;
@@ -1447,9 +1184,9 @@ function escapeRegexPattern(regexPattern) {
1447
1184
  * @returns The string converted to camelCase.
1448
1185
  */
1449
1186
  function kebabToCamel(input, options) {
1450
- if (input !== input.toLowerCase()) throw new DataError({ input }, "UPPERCASE_INPUT", "Kebab-case must be purely lowercase.");
1451
- if (input.startsWith("-") || input.endsWith("-")) throw new DataError({ input }, "TRAILING_DASHES", "Dashes at the start and/or end are not allowed.");
1452
- if (input.includes("--")) throw new DataError({ input }, "CONSECUTIVE_DASHES", "Consecutive dashes are not allowed.");
1187
+ if (input !== input.toLowerCase()) throw new DataError$1({ input }, "UPPERCASE_INPUT", "Kebab-case must be purely lowercase.");
1188
+ if (input.startsWith("-") || input.endsWith("-")) throw new DataError$1({ input }, "TRAILING_DASHES", "Dashes at the start and/or end are not allowed.");
1189
+ if (input.includes("--")) throw new DataError$1({ input }, "CONSECUTIVE_DASHES", "Consecutive dashes are not allowed.");
1453
1190
  let outputString = "";
1454
1191
  let skip = false;
1455
1192
  for (const stringIndex in [...input]) {
@@ -1562,7 +1299,7 @@ function createTemplateStringsArray(strings) {
1562
1299
  * ```
1563
1300
  */
1564
1301
  function getStringsAndInterpolations(strings, ...interpolations) {
1565
- if (strings.length !== interpolations.length + 1) throw new DataError({
1302
+ if (strings.length !== interpolations.length + 1) throw new DataError$1({
1566
1303
  stringsLength: strings.length,
1567
1304
  interpolationsLength: interpolations.length,
1568
1305
  strings,
@@ -1571,6 +1308,33 @@ function getStringsAndInterpolations(strings, ...interpolations) {
1571
1308
  return [createTemplateStringsArray(strings), ...interpolations];
1572
1309
  }
1573
1310
  //#endregion
1311
+ //#region src/root/functions/taggedTemplate/interpolate.ts
1312
+ /**
1313
+ * Returns the result of interpolating a template string when given the strings and interpolations separately.
1314
+ *
1315
+ * You can pass a template string directly by doing:
1316
+ *
1317
+ * ```
1318
+ * interpolate`Template string here`;
1319
+ * ```
1320
+ *
1321
+ * In this case, it will be functionally the same as if you just wrote the template string by itself.
1322
+ *
1323
+ * @category Tagged Template
1324
+ *
1325
+ * @template InterpolationsType - The type of the interpolations.
1326
+ *
1327
+ * @param strings - The strings from the template to process.
1328
+ * @param interpolations - An array of all interpolations from the template.
1329
+ *
1330
+ * @returns A new string with the strings and interpolations from the template applied.
1331
+ */
1332
+ function interpolate(strings, ...interpolations) {
1333
+ let result = "";
1334
+ for (const [string, interpolation = ""] of paralleliseArrays(strings, interpolations)) result += string + interpolation;
1335
+ return result;
1336
+ }
1337
+ //#endregion
1574
1338
  //#region src/root/functions/taggedTemplate/interpolateObjects.ts
1575
1339
  /**
1576
1340
  * Returns the result of interpolating a template string, also stringifying objects.
@@ -1613,6 +1377,395 @@ function isTemplateStringsArray(input) {
1613
1377
  return typeof input === "object" && input !== null && "raw" in input;
1614
1378
  }
1615
1379
  //#endregion
1380
+ //#region src/root/functions/taggedTemplate/normaliseIndents.ts
1381
+ function calculateTabSize(line, whitespaceLength) {
1382
+ const potentialWhitespacePart = line.slice(0, whitespaceLength);
1383
+ const trimmedString = line.trimStart();
1384
+ if (potentialWhitespacePart.trim() !== "") return 0;
1385
+ const tabSize = line.length - (trimmedString.length + whitespaceLength);
1386
+ return tabSize < 0 ? 0 : tabSize;
1387
+ }
1388
+ function getWhitespaceLength(lines) {
1389
+ const [firstNonEmptyLine] = lines.filter((line) => {
1390
+ return line.trim() !== "";
1391
+ });
1392
+ return firstNonEmptyLine.length - firstNonEmptyLine.trimStart().length;
1393
+ }
1394
+ function reduceLines(lines, { preserveTabs = true }) {
1395
+ const slicedLines = lines.slice(1);
1396
+ const isFirstLineEmpty = lines[0].trim() === "";
1397
+ const whitespaceLength = getWhitespaceLength(isFirstLineEmpty ? lines : slicedLines);
1398
+ return (isFirstLineEmpty ? slicedLines : lines).map((line) => {
1399
+ const tabSize = calculateTabSize(line, whitespaceLength);
1400
+ return (preserveTabs ? fillArray(() => {
1401
+ return " ";
1402
+ }, tabSize).join("") : "") + line.trimStart();
1403
+ }).join("\n");
1404
+ }
1405
+ /**
1406
+ * Applies any options if provided, then removes any extraneous indents from a multi-line template string.
1407
+ *
1408
+ * You can pass a template string directly by doing:
1409
+ *
1410
+ * ```typescript
1411
+ * normaliseIndents`Template string here
1412
+ * with a new line
1413
+ * and another new line`;
1414
+ * ```
1415
+ *
1416
+ * You may also pass the options first, then invoke the resulting function with a template string:
1417
+ *
1418
+ * ```typescript
1419
+ * normaliseIndents({ preserveTabs: false })`Template string here
1420
+ * with a new line
1421
+ * and another new line`;
1422
+ * ```
1423
+ *
1424
+ * @category Tagged Template
1425
+ *
1426
+ * @param first - The strings from the template to process, or the options to apply.
1427
+ * @param args - An array of all interpolations from the template.
1428
+ *
1429
+ * @returns An additional function to invoke, or a new string with the strings and interpolations from the template applied, and extraneous indents removed.
1430
+ */
1431
+ function normaliseIndents(first, ...args) {
1432
+ if (typeof first === "object" && first !== null && !Array.isArray(first)) {
1433
+ const options = first;
1434
+ return (strings, ...interpolations) => {
1435
+ return normaliseIndents(strings, ...interpolations, options);
1436
+ };
1437
+ }
1438
+ const strings = first;
1439
+ const options = typeof args[args.length - 1] === "object" && !Array.isArray(args[args.length - 1]) ? args.pop() : {};
1440
+ return reduceLines(interpolate(strings, ...[...args]).split("\n"), options);
1441
+ }
1442
+ /**
1443
+ * Applies any options if provided, then removes any extraneous indents from a multi-line template string.
1444
+ *
1445
+ * You can pass a template string directly by doing:
1446
+ *
1447
+ * ```typescript
1448
+ * normalizeIndents`Template string here
1449
+ * with a new line
1450
+ * and another new line`.
1451
+ * ```
1452
+ *
1453
+ * You may also pass the options first, then invoke the resulting function with a template string:
1454
+ *
1455
+ * ```typescript
1456
+ * normalizeIndents({ preserveTabs: false })`Template string here
1457
+ * with a new line
1458
+ * and another new line`.
1459
+ * ```
1460
+ *
1461
+ * @param first - The strings from the template to process, or the options to apply.
1462
+ * @param args - An array of all interpolations from the template.
1463
+ *
1464
+ * @returns An additional function to invoke, or a new string with the strings and interpolations from the template applied, and extraneous indents removed.
1465
+ */
1466
+ const normalizeIndents = normaliseIndents;
1467
+ //#endregion
1468
+ //#region src/root/deprecated/DataError.ts
1469
+ /**
1470
+ * Represents errors you may get that may've been caused by a specific piece of data.
1471
+ *
1472
+ * @category Types
1473
+ *
1474
+ * @deprecated Please use `DataError` from `@alextheman/utility/v6` instead.
1475
+ *
1476
+ * @template DataType - The type of the data that caused the error.
1477
+ */
1478
+ var DataError = class DataError extends Error {
1479
+ code;
1480
+ data;
1481
+ /**
1482
+ * @param data - The data that caused the error.
1483
+ * @param code - A standardised code (e.g. UNEXPECTED_DATA).
1484
+ * @param message - A human-readable error message (e.g. The data provided is invalid).
1485
+ * @param options - Extra options to pass to super Error constructor.
1486
+ */
1487
+ constructor(data, code = "INVALID_DATA", message = "The data provided is invalid", options) {
1488
+ super(message, options);
1489
+ if (Error.captureStackTrace) Error.captureStackTrace(this, new.target);
1490
+ this.name = new.target.name;
1491
+ this.code = code;
1492
+ this.data = data;
1493
+ Object.defineProperty(this, "message", { enumerable: true });
1494
+ Object.setPrototypeOf(this, new.target.prototype);
1495
+ }
1496
+ static checkCaughtError(error, options) {
1497
+ if (DataError.check(error)) {
1498
+ 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.
1499
+
1500
+ Expected: ${options.expectedCode}
1501
+ Received: ${error.code}
1502
+ `, { cause: error });
1503
+ return error;
1504
+ }
1505
+ throw error;
1506
+ }
1507
+ /**
1508
+ * Checks whether the given input may have been caused by a DataError.
1509
+ *
1510
+ * @param input - The input to check.
1511
+ *
1512
+ * @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`.
1513
+ */
1514
+ static check(input) {
1515
+ if (input instanceof DataError) return true;
1516
+ const data = input;
1517
+ return typeof data === "object" && data !== null && typeof data.message === "string" && typeof data.code === "string" && "data" in data;
1518
+ }
1519
+ /**
1520
+ * 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.
1521
+ *
1522
+ * @param errorFunction - The function expected to throw the error.
1523
+ * @param options - Extra options to apply.
1524
+ *
1525
+ * @throws {Error} Any other errors thrown by the `errorFunction` that are not a `DataError`.
1526
+ * @throws {Error} If no `DataError` was thrown by the `errorFunction`
1527
+ *
1528
+ * @returns The `DataError` that was thrown by the `errorFunction`
1529
+ */
1530
+ static expectError(errorFunction, options) {
1531
+ try {
1532
+ errorFunction();
1533
+ } catch (error) {
1534
+ return DataError.checkCaughtError(error, options);
1535
+ }
1536
+ throw new Error("Expected a DataError to be thrown but none was thrown");
1537
+ }
1538
+ /**
1539
+ * 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.
1540
+ *
1541
+ * @param errorFunction - The function expected to throw the error.
1542
+ * @param options - Extra options to apply.
1543
+ *
1544
+ * @throws {Error} Any other errors thrown by the `errorFunction` that are not a `DataError`.
1545
+ * @throws {Error} If no `DataError` was thrown by the `errorFunction`
1546
+ *
1547
+ * @returns The `DataError` that was thrown by the `errorFunction`
1548
+ */
1549
+ static async expectErrorAsync(errorFunction, options) {
1550
+ try {
1551
+ await errorFunction();
1552
+ } catch (error) {
1553
+ return DataError.checkCaughtError(error, options);
1554
+ }
1555
+ throw new Error("Expected a DataError to be thrown but none was thrown");
1556
+ }
1557
+ };
1558
+ //#endregion
1559
+ //#region src/root/types/APIError.ts
1560
+ const httpErrorCodeLookup = {
1561
+ 400: "BAD_REQUEST",
1562
+ 401: "UNAUTHORISED",
1563
+ 403: "FORBIDDEN",
1564
+ 404: "NOT_FOUND",
1565
+ 418: "I_AM_A_TEAPOT",
1566
+ 500: "INTERNAL_SERVER_ERROR"
1567
+ };
1568
+ /**
1569
+ * Represents common errors you may get from a HTTP API request.
1570
+ *
1571
+ * @category Types
1572
+ */
1573
+ var APIError = class APIError extends Error {
1574
+ status;
1575
+ /**
1576
+ * @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.
1577
+ * @param message - An error message to display alongside the status code.
1578
+ * @param options - Extra options to be passed to super Error constructor.
1579
+ */
1580
+ constructor(status = 500, message, options) {
1581
+ super(message, options);
1582
+ this.status = status;
1583
+ if (message) this.message = message;
1584
+ else this.message = httpErrorCodeLookup[this.status] ?? "API_ERROR";
1585
+ Object.defineProperty(this, "message", { enumerable: true });
1586
+ Object.setPrototypeOf(this, new.target.prototype);
1587
+ }
1588
+ /**
1589
+ * Checks whether the given input may have been caused by an APIError.
1590
+ *
1591
+ * @param input - The input to check.
1592
+ *
1593
+ * @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`.
1594
+ */
1595
+ static check(input) {
1596
+ if (input instanceof APIError) return true;
1597
+ const data = input;
1598
+ return typeof data === "object" && data !== null && typeof data?.status === "number" && typeof data?.message === "string";
1599
+ }
1600
+ };
1601
+ //#endregion
1602
+ //#region src/root/types/VersionNumber.ts
1603
+ /**
1604
+ * Represents a software version number, considered to be made up of a major, minor, and patch part.
1605
+ *
1606
+ * @category Types
1607
+ */
1608
+ var VersionNumber = class VersionNumber {
1609
+ static NON_NEGATIVE_TUPLE_ERROR = "Input array must be a tuple of three non-negative integers.";
1610
+ /** The major number. Increments when a feature is removed or changed in a way that is not backwards-compatible with the previous release. */
1611
+ major = 0;
1612
+ /** The minor number. Increments when a new feature is added/deprecated and is expected to be backwards-compatible with the previous release. */
1613
+ minor = 0;
1614
+ /** The patch number. Increments when the next release is fixing a bug or doing a small refactor that should not be noticeable in practice. */
1615
+ patch = 0;
1616
+ /**
1617
+ * @param input - The input to create a new instance of `VersionNumber` from.
1618
+ */
1619
+ constructor(input) {
1620
+ if (input instanceof VersionNumber) {
1621
+ this.major = input.major;
1622
+ this.minor = input.minor;
1623
+ this.patch = input.patch;
1624
+ } else if (typeof input === "string") {
1625
+ if (!VERSION_NUMBER_REGEX.test(input)) throw new DataError$1({ 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.`);
1626
+ const [major, minor, patch] = VersionNumber.formatString(input, { omitPrefix: true }).split(".").map((number) => {
1627
+ return parseIntStrict(number);
1628
+ });
1629
+ this.major = major;
1630
+ this.minor = minor;
1631
+ this.patch = patch;
1632
+ } else if (Array.isArray(input)) {
1633
+ if (input.length !== 3) throw new DataError$1({ input }, "INVALID_LENGTH", VersionNumber.NON_NEGATIVE_TUPLE_ERROR);
1634
+ const [major, minor, patch] = input.map((number) => {
1635
+ const parsedInteger = parseIntStrict(number?.toString());
1636
+ if (parsedInteger < 0) throw new DataError$1({ input }, "NEGATIVE_INPUTS", VersionNumber.NON_NEGATIVE_TUPLE_ERROR);
1637
+ return parsedInteger;
1638
+ });
1639
+ this.major = major;
1640
+ this.minor = minor;
1641
+ this.patch = patch;
1642
+ } else throw new DataError$1({ input }, "INVALID_INPUT", normaliseIndents`
1643
+ The provided input can not be parsed into a valid version number.
1644
+ Expected either a string of format X.Y.Z or vX.Y.Z, a tuple of three numbers, or another \`VersionNumber\` instance.
1645
+ `);
1646
+ }
1647
+ /**
1648
+ * Gets the current version type of the current instance of `VersionNumber`.
1649
+ *
1650
+ * @returns Either `"major"`, `"minor"`, or `"patch"`, depending on the version type.
1651
+ */
1652
+ get type() {
1653
+ if (this.minor === 0 && this.patch === 0) return VersionType.MAJOR;
1654
+ if (this.patch === 0) return VersionType.MINOR;
1655
+ return VersionType.PATCH;
1656
+ }
1657
+ static formatString(input, options) {
1658
+ if (options?.omitPrefix) return input.startsWith("v") ? input.slice(1) : input;
1659
+ return input.startsWith("v") ? input : `v${input}`;
1660
+ }
1661
+ /**
1662
+ * Checks if the provided version numbers have the exact same major, minor, and patch numbers.
1663
+ *
1664
+ * @param firstVersion - The first version number to compare.
1665
+ * @param secondVersion - The second version number to compare.
1666
+ *
1667
+ * @returns `true` if the provided version numbers have exactly the same major, minor, and patch numbers, and returns `false` otherwise.
1668
+ */
1669
+ static isEqual(firstVersion, secondVersion) {
1670
+ return firstVersion.major === secondVersion.major && firstVersion.minor === secondVersion.minor && firstVersion.patch === secondVersion.patch;
1671
+ }
1672
+ /**
1673
+ * Get a formatted string representation of the current version number
1674
+ *
1675
+ * @param options - Options to apply to the string formatting.
1676
+ *
1677
+ * @returns A formatted string representation of the current version number with the options applied.
1678
+ */
1679
+ format(options) {
1680
+ let baseOutput = `${this.major}`;
1681
+ if (!options?.omitMinor) {
1682
+ baseOutput += `.${this.minor}`;
1683
+ if (!options?.omitPatch) baseOutput += `.${this.patch}`;
1684
+ }
1685
+ return VersionNumber.formatString(baseOutput, { omitPrefix: options?.omitPrefix });
1686
+ }
1687
+ /**
1688
+ * Increments the current version number by the given increment type, returning the result as a new reference in memory.
1689
+ *
1690
+ * @param incrementType - The type of increment. Can be one of the following:
1691
+ * - `"major"`: Change the major version `v1.2.3` → `v2.0.0`
1692
+ * - `"minor"`: Change the minor version `v1.2.3` → `v1.3.0`
1693
+ * - `"patch"`: Change the patch version `v1.2.3` → `v1.2.4`
1694
+ * @param incrementAmount - The amount to increment by (defaults to 1).
1695
+ *
1696
+ * @returns A new instance of `VersionNumber` with the increment applied.
1697
+ */
1698
+ increment(incrementType, incrementAmount = 1) {
1699
+ const incrementBy = parseIntStrict(String(incrementAmount));
1700
+ const calculatedRawVersion = {
1701
+ major: [
1702
+ this.major + incrementBy,
1703
+ 0,
1704
+ 0
1705
+ ],
1706
+ minor: [
1707
+ this.major,
1708
+ this.minor + incrementBy,
1709
+ 0
1710
+ ],
1711
+ patch: [
1712
+ this.major,
1713
+ this.minor,
1714
+ this.patch + incrementBy
1715
+ ]
1716
+ }[incrementType];
1717
+ try {
1718
+ return new VersionNumber(calculatedRawVersion);
1719
+ } catch (error) {
1720
+ if (DataError$1.check(error) && error.code === "NEGATIVE_INPUTS") throw new DataError$1({
1721
+ currentVersion: this.toString(),
1722
+ calculatedRawVersion: `v${calculatedRawVersion.join(".")}`,
1723
+ incrementAmount
1724
+ }, "NEGATIVE_VERSION", "Cannot apply this increment amount as it would lead to a negative version number.");
1725
+ else throw error;
1726
+ }
1727
+ }
1728
+ /**
1729
+ * Ensures that the VersionNumber behaves correctly when attempted to be coerced to a string.
1730
+ *
1731
+ * @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).
1732
+ *
1733
+ * @returns A stringified representation of the current version number, prefixed with `v`.
1734
+ */
1735
+ [Symbol.toPrimitive](hint) {
1736
+ if (hint === "number") throw new DataError$1({ thisVersion: this.toString() }, "INVALID_COERCION", "VersionNumber cannot be coerced to a number type.");
1737
+ return this.toString();
1738
+ }
1739
+ /**
1740
+ * Ensures that the VersionNumber behaves correctly when attempted to be converted to JSON.
1741
+ *
1742
+ * @returns A stringified representation of the current version number, prefixed with `v`.
1743
+ */
1744
+ toJSON() {
1745
+ return this.toString();
1746
+ }
1747
+ /**
1748
+ * Get a string representation of the current version number.
1749
+ *
1750
+ * @returns A stringified representation of the current version number with the prefix.
1751
+ */
1752
+ toString() {
1753
+ const rawString = `${this.major}.${this.minor}.${this.patch}`;
1754
+ return VersionNumber.formatString(rawString, { omitPrefix: false });
1755
+ }
1756
+ };
1757
+ const zodVersionNumber = zod.default.union([
1758
+ zod.default.string(),
1759
+ zod.default.tuple([
1760
+ zod.default.number(),
1761
+ zod.default.number(),
1762
+ zod.default.number()
1763
+ ]),
1764
+ zod.default.instanceof(VersionNumber)
1765
+ ]).transform((rawVersionNumber) => {
1766
+ return new VersionNumber(rawVersionNumber);
1767
+ });
1768
+ //#endregion
1616
1769
  exports.APIError = APIError;
1617
1770
  exports.DataError = DataError;
1618
1771
  exports.Env = Env;