@alextheman/utility 2.11.0 → 2.12.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.cjs CHANGED
@@ -122,15 +122,19 @@ function convertFileToBase64(file) {
122
122
  var convertFileToBase64_default = convertFileToBase64;
123
123
 
124
124
  // src/functions/createFormData.ts
125
- function createFormData(data, options = { nullableResolution: "empty" }) {
125
+ function getNullableResolutionStrategy(key, strategy) {
126
+ var _a;
127
+ return (_a = typeof strategy === "object" ? strategy[key] : strategy) != null ? _a : "empty";
128
+ }
129
+ function createFormData(data, options = { arrayResolution: "stringify", nullableResolution: "empty" }) {
126
130
  const formData = new FormData();
127
- function resolveByStrategy(key, value, resolutionStrategy) {
131
+ function resolveNullablesByStrategy(key, value, resolutionStrategy) {
128
132
  switch (resolutionStrategy) {
129
133
  case "empty":
130
- formData.append(key, "");
134
+ formData.append(String(key), "");
131
135
  break;
132
136
  case "stringify":
133
- formData.append(key, JSON.stringify(value));
137
+ formData.append(String(key), JSON.stringify(value));
134
138
  break;
135
139
  case "omit":
136
140
  break;
@@ -138,28 +142,52 @@ function createFormData(data, options = { nullableResolution: "empty" }) {
138
142
  throw new TypeError("SLOPPY_PURE_JAVASCRIPT_USER_ERROR");
139
143
  }
140
144
  }
141
- for (const key in data) {
142
- if (data[key] instanceof Blob) {
143
- formData.append(key, data[key]);
144
- } else if (data[key] === void 0 || data[key] === null) {
145
- if (options.nullableResolution) {
146
- resolveByStrategy(key, data[key], options.nullableResolution);
147
- continue;
145
+ function resolveNullables(key, value, options2) {
146
+ if (options2.nullableResolution) {
147
+ resolveNullablesByStrategy(
148
+ key,
149
+ value,
150
+ getNullableResolutionStrategy(key, options2.nullableResolution)
151
+ );
152
+ return;
153
+ }
154
+ if (options2.undefinedResolution || options2.nullResolution) {
155
+ if (data[key] === void 0 && options2.undefinedResolution) {
156
+ resolveNullablesByStrategy(
157
+ key,
158
+ value,
159
+ getNullableResolutionStrategy(key, options2.undefinedResolution)
160
+ );
161
+ return;
148
162
  }
149
- if (options.undefinedResolution || options.nullResolution) {
150
- if (data[key] === void 0 && options.undefinedResolution) {
151
- resolveByStrategy(key, data[key], options.undefinedResolution);
152
- continue;
153
- }
154
- if (data[key] === null && options.nullResolution) {
155
- resolveByStrategy(key, data[key], options.nullResolution);
156
- continue;
163
+ if (data[key] === null && options2.nullResolution) {
164
+ resolveNullablesByStrategy(
165
+ key,
166
+ value,
167
+ getNullableResolutionStrategy(key, options2.nullResolution)
168
+ );
169
+ }
170
+ }
171
+ }
172
+ const entries = Object.entries(data);
173
+ for (const [key, value] of entries) {
174
+ if (value instanceof Blob) {
175
+ formData.append(String(key), value);
176
+ } else if (value === void 0 || value === null) {
177
+ resolveNullables(key, value, options);
178
+ } else if (typeof value === "object") {
179
+ if (Array.isArray(value) && (options.arrayResolution === "multiple" || typeof options.arrayResolution === "object" && options.arrayResolution[key] === "multiple")) {
180
+ for (const item of value) {
181
+ if ((typeof item === "object" || !item) && !(item instanceof Blob)) {
182
+ throw new TypeError("NON_PRIMITIVE_ARRAY_ITEMS_FOUND");
183
+ }
184
+ formData.append(String(key), String(item));
157
185
  }
186
+ continue;
158
187
  }
159
- } else if (typeof data[key] === "object") {
160
- formData.append(key, JSON.stringify(data[key]));
188
+ formData.append(String(key), JSON.stringify(value));
161
189
  } else {
162
- formData.append(key, String(data[key]));
190
+ formData.append(String(key), String(value));
163
191
  }
164
192
  }
165
193
  return formData;
package/dist/index.d.cts CHANGED
@@ -8,21 +8,60 @@ declare function camelToKebab(string: string): string;
8
8
 
9
9
  declare function convertFileToBase64(file: File): Promise<string>;
10
10
 
11
- type FormDataResolutionStrategy = "stringify" | "empty" | "omit";
12
- interface CreateFormDataOptionsUndefinedOrNullResolution {
13
- undefinedResolution?: FormDataResolutionStrategy;
14
- nullResolution?: FormDataResolutionStrategy;
11
+ type HTTPErrorCode = 400 | 401 | 403 | 404 | 418 | 500;
12
+ /** @deprecated This type has been renamed to HTTPErrorCode (singular) */
13
+ type HTTPErrorCodes = 400 | 401 | 403 | 404 | 418 | 500;
14
+ declare const httpErrorCodeLookup: Record<HTTPErrorCode, string>;
15
+ declare class APIError extends Error {
16
+ status: number;
17
+ constructor(status?: HTTPErrorCode | number, message?: string, options?: ErrorOptions);
18
+ static check(input: unknown): input is APIError;
19
+ }
20
+
21
+ declare const emailSchema: z.core.$ZodBranded<z.ZodEmail, "Email">;
22
+ type Email = z.infer<typeof emailSchema>;
23
+ declare function parseEmail(data: unknown): Email;
24
+
25
+ declare const envSchema: z$1.ZodEnum<{
26
+ test: "test";
27
+ development: "development";
28
+ production: "production";
29
+ }>;
30
+ type Env = z$1.infer<typeof envSchema>;
31
+ declare function parseEnv(data?: unknown): Env;
32
+
33
+ declare const uuidSchema: z.core.$ZodBranded<z.ZodUUID, "UUID">;
34
+ type UUID = z.infer<typeof uuidSchema>;
35
+ declare function parseUUID(UUID: unknown): UUID;
36
+
37
+ type DisallowUndefined<T> = undefined extends T ? ["Error: Generic type cannot include undefined"] : T;
38
+
39
+ type NonUndefined<T> = T extends undefined ? never : T;
40
+
41
+ type OptionalOnCondition<Condition extends boolean, T> = Condition extends true ? T : T | undefined;
42
+
43
+ type RecordKey = string | number | symbol;
44
+
45
+ type FormDataNullableResolutionStrategy = "stringify" | "empty" | "omit";
46
+ type FormDataArrayResolutionStrategy = "stringify" | "multiple";
47
+ interface CreateFormDataOptionsBase<K extends RecordKey> {
48
+ arrayResolution?: FormDataArrayResolutionStrategy | Partial<Record<K, FormDataArrayResolutionStrategy>>;
49
+ }
50
+ interface CreateFormDataOptionsUndefinedOrNullResolution<K extends RecordKey> extends CreateFormDataOptionsBase<K> {
51
+ undefinedResolution?: FormDataNullableResolutionStrategy | Partial<Record<K, FormDataNullableResolutionStrategy>>;
52
+ nullResolution?: FormDataNullableResolutionStrategy | Partial<Record<K, FormDataNullableResolutionStrategy>>;
15
53
  nullableResolution?: never;
16
54
  }
17
- interface CreateFormDataOptionsNullableResolution {
55
+ interface CreateFormDataOptionsNullableResolution<K extends RecordKey> extends CreateFormDataOptionsBase<K> {
18
56
  undefinedResolution?: never;
19
57
  nullResolution?: never;
20
- nullableResolution: FormDataResolutionStrategy;
58
+ nullableResolution: FormDataNullableResolutionStrategy | Partial<Record<K, FormDataNullableResolutionStrategy>>;
21
59
  }
22
- type CreateFormDataOptions = CreateFormDataOptionsUndefinedOrNullResolution | CreateFormDataOptionsNullableResolution;
23
- declare function createFormData<T extends Record<string, unknown>>(data: T, options?: CreateFormDataOptions): FormData;
60
+ type CreateFormDataOptions<K extends RecordKey> = CreateFormDataOptionsUndefinedOrNullResolution<K> | CreateFormDataOptionsNullableResolution<K>;
61
+ declare function createFormData<T extends Record<RecordKey, unknown>, K extends keyof T>(data: T, options?: CreateFormDataOptions<K>): FormData;
24
62
 
25
- declare function fillArray<T>(callback: (index: number) => T | Promise<T>, length?: number): T[] | Promise<T[]>;
63
+ declare function fillArray<T>(callback: (index: number) => Promise<T>, length?: number): Promise<T[]>;
64
+ declare function fillArray<T>(callback: (index: number) => T, length?: number): T[];
26
65
 
27
66
  declare function formatDateAndTime(inputDate: Date): string;
28
67
 
@@ -60,36 +99,4 @@ declare function wait(seconds: number): Promise<void>;
60
99
 
61
100
  declare function interpolateObjects(strings: TemplateStringsArray, ...values: unknown[]): string;
62
101
 
63
- type HTTPErrorCode = 400 | 401 | 403 | 404 | 418 | 500;
64
- /** @deprecated This type has been renamed to HTTPErrorCode (singular) */
65
- type HTTPErrorCodes = 400 | 401 | 403 | 404 | 418 | 500;
66
- declare const httpErrorCodeLookup: Record<HTTPErrorCode, string>;
67
- declare class APIError extends Error {
68
- status: number;
69
- constructor(status?: HTTPErrorCode | number, message?: string, options?: ErrorOptions);
70
- static check(input: unknown): input is APIError;
71
- }
72
-
73
- declare const emailSchema: z.core.$ZodBranded<z.ZodEmail, "Email">;
74
- type Email = z.infer<typeof emailSchema>;
75
- declare function parseEmail(data: unknown): Email;
76
-
77
- declare const envSchema: z$1.ZodEnum<{
78
- test: "test";
79
- development: "development";
80
- production: "production";
81
- }>;
82
- type Env = z$1.infer<typeof envSchema>;
83
- declare function parseEnv(data?: unknown): Env;
84
-
85
- declare const uuidSchema: z.core.$ZodBranded<z.ZodUUID, "UUID">;
86
- type UUID = z.infer<typeof uuidSchema>;
87
- declare function parseUUID(UUID: unknown): UUID;
88
-
89
- type DisallowUndefined<T> = undefined extends T ? ["Error: Generic type cannot include undefined"] : T;
90
-
91
- type NonUndefined<T> = T extends undefined ? never : T;
92
-
93
- type OptionalOnCondition<Condition extends boolean, T> = Condition extends true ? T : T | undefined;
94
-
95
- export { APIError, type CreateFormDataOptions, type CreateFormDataOptionsNullableResolution, type CreateFormDataOptionsUndefinedOrNullResolution, type DisallowUndefined, type Email, type Env, type FormDataResolutionStrategy, type HTTPErrorCode, type HTTPErrorCodes, type NonUndefined, type OptionalOnCondition, type StringListToArrayOptions, type UUID, addDaysToDate, appendSemicolon, camelToKebab, convertFileToBase64, createFormData, fillArray, formatDateAndTime, getRandomNumber, httpErrorCodeLookup, interpolateObjects, isLeapYear, isMonthlyMultiple, isOrdered, isSameDate, omitProperties, parseEmail, parseEnv, parseIntStrict, parseUUID, randomiseArray, range, removeDuplicates, stringListToArray, stringToBoolean, truncate, wait };
102
+ export { APIError, type CreateFormDataOptions, type CreateFormDataOptionsNullableResolution, type CreateFormDataOptionsUndefinedOrNullResolution, type DisallowUndefined, type Email, type Env, type FormDataNullableResolutionStrategy as FormDataResolutionStrategy, type HTTPErrorCode, type HTTPErrorCodes, type NonUndefined, type OptionalOnCondition, type RecordKey, type StringListToArrayOptions, type UUID, addDaysToDate, appendSemicolon, camelToKebab, convertFileToBase64, createFormData, fillArray, formatDateAndTime, getRandomNumber, httpErrorCodeLookup, interpolateObjects, isLeapYear, isMonthlyMultiple, isOrdered, isSameDate, omitProperties, parseEmail, parseEnv, parseIntStrict, parseUUID, randomiseArray, range, removeDuplicates, stringListToArray, stringToBoolean, truncate, wait };
package/dist/index.d.ts CHANGED
@@ -8,21 +8,60 @@ declare function camelToKebab(string: string): string;
8
8
 
9
9
  declare function convertFileToBase64(file: File): Promise<string>;
10
10
 
11
- type FormDataResolutionStrategy = "stringify" | "empty" | "omit";
12
- interface CreateFormDataOptionsUndefinedOrNullResolution {
13
- undefinedResolution?: FormDataResolutionStrategy;
14
- nullResolution?: FormDataResolutionStrategy;
11
+ type HTTPErrorCode = 400 | 401 | 403 | 404 | 418 | 500;
12
+ /** @deprecated This type has been renamed to HTTPErrorCode (singular) */
13
+ type HTTPErrorCodes = 400 | 401 | 403 | 404 | 418 | 500;
14
+ declare const httpErrorCodeLookup: Record<HTTPErrorCode, string>;
15
+ declare class APIError extends Error {
16
+ status: number;
17
+ constructor(status?: HTTPErrorCode | number, message?: string, options?: ErrorOptions);
18
+ static check(input: unknown): input is APIError;
19
+ }
20
+
21
+ declare const emailSchema: z.core.$ZodBranded<z.ZodEmail, "Email">;
22
+ type Email = z.infer<typeof emailSchema>;
23
+ declare function parseEmail(data: unknown): Email;
24
+
25
+ declare const envSchema: z$1.ZodEnum<{
26
+ test: "test";
27
+ development: "development";
28
+ production: "production";
29
+ }>;
30
+ type Env = z$1.infer<typeof envSchema>;
31
+ declare function parseEnv(data?: unknown): Env;
32
+
33
+ declare const uuidSchema: z.core.$ZodBranded<z.ZodUUID, "UUID">;
34
+ type UUID = z.infer<typeof uuidSchema>;
35
+ declare function parseUUID(UUID: unknown): UUID;
36
+
37
+ type DisallowUndefined<T> = undefined extends T ? ["Error: Generic type cannot include undefined"] : T;
38
+
39
+ type NonUndefined<T> = T extends undefined ? never : T;
40
+
41
+ type OptionalOnCondition<Condition extends boolean, T> = Condition extends true ? T : T | undefined;
42
+
43
+ type RecordKey = string | number | symbol;
44
+
45
+ type FormDataNullableResolutionStrategy = "stringify" | "empty" | "omit";
46
+ type FormDataArrayResolutionStrategy = "stringify" | "multiple";
47
+ interface CreateFormDataOptionsBase<K extends RecordKey> {
48
+ arrayResolution?: FormDataArrayResolutionStrategy | Partial<Record<K, FormDataArrayResolutionStrategy>>;
49
+ }
50
+ interface CreateFormDataOptionsUndefinedOrNullResolution<K extends RecordKey> extends CreateFormDataOptionsBase<K> {
51
+ undefinedResolution?: FormDataNullableResolutionStrategy | Partial<Record<K, FormDataNullableResolutionStrategy>>;
52
+ nullResolution?: FormDataNullableResolutionStrategy | Partial<Record<K, FormDataNullableResolutionStrategy>>;
15
53
  nullableResolution?: never;
16
54
  }
17
- interface CreateFormDataOptionsNullableResolution {
55
+ interface CreateFormDataOptionsNullableResolution<K extends RecordKey> extends CreateFormDataOptionsBase<K> {
18
56
  undefinedResolution?: never;
19
57
  nullResolution?: never;
20
- nullableResolution: FormDataResolutionStrategy;
58
+ nullableResolution: FormDataNullableResolutionStrategy | Partial<Record<K, FormDataNullableResolutionStrategy>>;
21
59
  }
22
- type CreateFormDataOptions = CreateFormDataOptionsUndefinedOrNullResolution | CreateFormDataOptionsNullableResolution;
23
- declare function createFormData<T extends Record<string, unknown>>(data: T, options?: CreateFormDataOptions): FormData;
60
+ type CreateFormDataOptions<K extends RecordKey> = CreateFormDataOptionsUndefinedOrNullResolution<K> | CreateFormDataOptionsNullableResolution<K>;
61
+ declare function createFormData<T extends Record<RecordKey, unknown>, K extends keyof T>(data: T, options?: CreateFormDataOptions<K>): FormData;
24
62
 
25
- declare function fillArray<T>(callback: (index: number) => T | Promise<T>, length?: number): T[] | Promise<T[]>;
63
+ declare function fillArray<T>(callback: (index: number) => Promise<T>, length?: number): Promise<T[]>;
64
+ declare function fillArray<T>(callback: (index: number) => T, length?: number): T[];
26
65
 
27
66
  declare function formatDateAndTime(inputDate: Date): string;
28
67
 
@@ -60,36 +99,4 @@ declare function wait(seconds: number): Promise<void>;
60
99
 
61
100
  declare function interpolateObjects(strings: TemplateStringsArray, ...values: unknown[]): string;
62
101
 
63
- type HTTPErrorCode = 400 | 401 | 403 | 404 | 418 | 500;
64
- /** @deprecated This type has been renamed to HTTPErrorCode (singular) */
65
- type HTTPErrorCodes = 400 | 401 | 403 | 404 | 418 | 500;
66
- declare const httpErrorCodeLookup: Record<HTTPErrorCode, string>;
67
- declare class APIError extends Error {
68
- status: number;
69
- constructor(status?: HTTPErrorCode | number, message?: string, options?: ErrorOptions);
70
- static check(input: unknown): input is APIError;
71
- }
72
-
73
- declare const emailSchema: z.core.$ZodBranded<z.ZodEmail, "Email">;
74
- type Email = z.infer<typeof emailSchema>;
75
- declare function parseEmail(data: unknown): Email;
76
-
77
- declare const envSchema: z$1.ZodEnum<{
78
- test: "test";
79
- development: "development";
80
- production: "production";
81
- }>;
82
- type Env = z$1.infer<typeof envSchema>;
83
- declare function parseEnv(data?: unknown): Env;
84
-
85
- declare const uuidSchema: z.core.$ZodBranded<z.ZodUUID, "UUID">;
86
- type UUID = z.infer<typeof uuidSchema>;
87
- declare function parseUUID(UUID: unknown): UUID;
88
-
89
- type DisallowUndefined<T> = undefined extends T ? ["Error: Generic type cannot include undefined"] : T;
90
-
91
- type NonUndefined<T> = T extends undefined ? never : T;
92
-
93
- type OptionalOnCondition<Condition extends boolean, T> = Condition extends true ? T : T | undefined;
94
-
95
- export { APIError, type CreateFormDataOptions, type CreateFormDataOptionsNullableResolution, type CreateFormDataOptionsUndefinedOrNullResolution, type DisallowUndefined, type Email, type Env, type FormDataResolutionStrategy, type HTTPErrorCode, type HTTPErrorCodes, type NonUndefined, type OptionalOnCondition, type StringListToArrayOptions, type UUID, addDaysToDate, appendSemicolon, camelToKebab, convertFileToBase64, createFormData, fillArray, formatDateAndTime, getRandomNumber, httpErrorCodeLookup, interpolateObjects, isLeapYear, isMonthlyMultiple, isOrdered, isSameDate, omitProperties, parseEmail, parseEnv, parseIntStrict, parseUUID, randomiseArray, range, removeDuplicates, stringListToArray, stringToBoolean, truncate, wait };
102
+ export { APIError, type CreateFormDataOptions, type CreateFormDataOptionsNullableResolution, type CreateFormDataOptionsUndefinedOrNullResolution, type DisallowUndefined, type Email, type Env, type FormDataNullableResolutionStrategy as FormDataResolutionStrategy, type HTTPErrorCode, type HTTPErrorCodes, type NonUndefined, type OptionalOnCondition, type RecordKey, type StringListToArrayOptions, type UUID, addDaysToDate, appendSemicolon, camelToKebab, convertFileToBase64, createFormData, fillArray, formatDateAndTime, getRandomNumber, httpErrorCodeLookup, interpolateObjects, isLeapYear, isMonthlyMultiple, isOrdered, isSameDate, omitProperties, parseEmail, parseEnv, parseIntStrict, parseUUID, randomiseArray, range, removeDuplicates, stringListToArray, stringToBoolean, truncate, wait };
package/dist/index.js CHANGED
@@ -63,15 +63,19 @@ function convertFileToBase64(file) {
63
63
  var convertFileToBase64_default = convertFileToBase64;
64
64
 
65
65
  // src/functions/createFormData.ts
66
- function createFormData(data, options = { nullableResolution: "empty" }) {
66
+ function getNullableResolutionStrategy(key, strategy) {
67
+ var _a;
68
+ return (_a = typeof strategy === "object" ? strategy[key] : strategy) != null ? _a : "empty";
69
+ }
70
+ function createFormData(data, options = { arrayResolution: "stringify", nullableResolution: "empty" }) {
67
71
  const formData = new FormData();
68
- function resolveByStrategy(key, value, resolutionStrategy) {
72
+ function resolveNullablesByStrategy(key, value, resolutionStrategy) {
69
73
  switch (resolutionStrategy) {
70
74
  case "empty":
71
- formData.append(key, "");
75
+ formData.append(String(key), "");
72
76
  break;
73
77
  case "stringify":
74
- formData.append(key, JSON.stringify(value));
78
+ formData.append(String(key), JSON.stringify(value));
75
79
  break;
76
80
  case "omit":
77
81
  break;
@@ -79,28 +83,52 @@ function createFormData(data, options = { nullableResolution: "empty" }) {
79
83
  throw new TypeError("SLOPPY_PURE_JAVASCRIPT_USER_ERROR");
80
84
  }
81
85
  }
82
- for (const key in data) {
83
- if (data[key] instanceof Blob) {
84
- formData.append(key, data[key]);
85
- } else if (data[key] === void 0 || data[key] === null) {
86
- if (options.nullableResolution) {
87
- resolveByStrategy(key, data[key], options.nullableResolution);
88
- continue;
86
+ function resolveNullables(key, value, options2) {
87
+ if (options2.nullableResolution) {
88
+ resolveNullablesByStrategy(
89
+ key,
90
+ value,
91
+ getNullableResolutionStrategy(key, options2.nullableResolution)
92
+ );
93
+ return;
94
+ }
95
+ if (options2.undefinedResolution || options2.nullResolution) {
96
+ if (data[key] === void 0 && options2.undefinedResolution) {
97
+ resolveNullablesByStrategy(
98
+ key,
99
+ value,
100
+ getNullableResolutionStrategy(key, options2.undefinedResolution)
101
+ );
102
+ return;
89
103
  }
90
- if (options.undefinedResolution || options.nullResolution) {
91
- if (data[key] === void 0 && options.undefinedResolution) {
92
- resolveByStrategy(key, data[key], options.undefinedResolution);
93
- continue;
94
- }
95
- if (data[key] === null && options.nullResolution) {
96
- resolveByStrategy(key, data[key], options.nullResolution);
97
- continue;
104
+ if (data[key] === null && options2.nullResolution) {
105
+ resolveNullablesByStrategy(
106
+ key,
107
+ value,
108
+ getNullableResolutionStrategy(key, options2.nullResolution)
109
+ );
110
+ }
111
+ }
112
+ }
113
+ const entries = Object.entries(data);
114
+ for (const [key, value] of entries) {
115
+ if (value instanceof Blob) {
116
+ formData.append(String(key), value);
117
+ } else if (value === void 0 || value === null) {
118
+ resolveNullables(key, value, options);
119
+ } else if (typeof value === "object") {
120
+ if (Array.isArray(value) && (options.arrayResolution === "multiple" || typeof options.arrayResolution === "object" && options.arrayResolution[key] === "multiple")) {
121
+ for (const item of value) {
122
+ if ((typeof item === "object" || !item) && !(item instanceof Blob)) {
123
+ throw new TypeError("NON_PRIMITIVE_ARRAY_ITEMS_FOUND");
124
+ }
125
+ formData.append(String(key), String(item));
98
126
  }
127
+ continue;
99
128
  }
100
- } else if (typeof data[key] === "object") {
101
- formData.append(key, JSON.stringify(data[key]));
129
+ formData.append(String(key), JSON.stringify(value));
102
130
  } else {
103
- formData.append(key, String(data[key]));
131
+ formData.append(String(key), String(value));
104
132
  }
105
133
  }
106
134
  return formData;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alextheman/utility",
3
- "version": "2.11.0",
3
+ "version": "2.12.0",
4
4
  "description": "Helpful utility functions",
5
5
  "license": "ISC",
6
6
  "author": "alextheman",