@discordkit/core 3.1.0 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (173) hide show
  1. package/CHANGELOG.md +416 -0
  2. package/README.md +52 -0
  3. package/dist/index.d.mts +23 -0
  4. package/dist/index.mjs +23 -0
  5. package/dist/requests/DiscordSession.d.mts +30 -0
  6. package/dist/requests/DiscordSession.mjs +202 -0
  7. package/dist/requests/addParams.d.mts +15 -0
  8. package/dist/requests/addParams.mjs +24 -0
  9. package/dist/requests/buildURL.d.mts +7 -0
  10. package/dist/requests/buildURL.mjs +7 -0
  11. package/dist/requests/getAsset.d.mts +7 -0
  12. package/dist/requests/getAsset.mjs +6 -0
  13. package/dist/requests/index.d.mts +9 -0
  14. package/dist/requests/index.mjs +9 -0
  15. package/dist/requests/methods.d.mts +63 -0
  16. package/dist/requests/methods.mjs +10 -0
  17. package/dist/requests/request.d.mts +27 -0
  18. package/dist/requests/request.mjs +29 -0
  19. package/dist/requests/toProcedure.d.mts +40 -0
  20. package/dist/requests/toProcedure.mjs +27 -0
  21. package/dist/requests/toQuery.d.mts +36 -0
  22. package/dist/requests/toQuery.mjs +17 -0
  23. package/dist/requests/toValidated.d.mts +16 -0
  24. package/dist/requests/toValidated.mjs +25 -0
  25. package/dist/requests/verifyKey.d.mts +13 -0
  26. package/dist/requests/verifyKey.mjs +63 -0
  27. package/dist/utils/isBetween.d.mts +4 -0
  28. package/dist/utils/isBetween.mjs +4 -0
  29. package/dist/utils/{isNonNullable.d.ts → isNonNullable.d.mts} +5 -2
  30. package/dist/utils/isNonNullable.mjs +22 -0
  31. package/dist/utils/isNumericString.d.mts +4 -0
  32. package/dist/utils/isNumericString.mjs +4 -0
  33. package/dist/utils/isObject.d.mts +4 -0
  34. package/dist/utils/isObject.mjs +4 -0
  35. package/dist/utils/sleep.d.mts +7 -0
  36. package/dist/utils/sleep.mjs +7 -0
  37. package/dist/utils/toCamelCase.d.mts +4 -0
  38. package/dist/utils/toCamelCase.mjs +4 -0
  39. package/dist/utils/toCamelKeys.d.mts +6 -0
  40. package/dist/utils/toCamelKeys.mjs +13 -0
  41. package/dist/utils/toSnakeCase.d.mts +4 -0
  42. package/dist/utils/toSnakeCase.mjs +4 -0
  43. package/dist/utils/toSnakeKeys.d.mts +6 -0
  44. package/dist/utils/toSnakeKeys.mjs +13 -0
  45. package/dist/validations/asDigits.d.mts +12 -0
  46. package/dist/validations/asDigits.mjs +10 -0
  47. package/dist/validations/asInteger.d.mts +12 -0
  48. package/dist/validations/asInteger.mjs +10 -0
  49. package/dist/validations/bitfield.d.mts +23 -0
  50. package/dist/validations/bitfield.mjs +20 -0
  51. package/dist/validations/boundedArray.d.mts +13 -0
  52. package/dist/validations/boundedArray.mjs +9 -0
  53. package/dist/validations/boundedInteger.d.mts +13 -0
  54. package/dist/validations/boundedInteger.mjs +9 -0
  55. package/dist/validations/boundedString.d.mts +14 -0
  56. package/dist/validations/boundedString.mjs +10 -0
  57. package/dist/validations/datauri.d.mts +24 -0
  58. package/dist/validations/datauri.mjs +17 -0
  59. package/dist/validations/fileUpload.d.mts +129 -0
  60. package/dist/validations/fileUpload.mjs +114 -0
  61. package/dist/validations/hasMimeType.d.mts +16 -0
  62. package/dist/validations/hasMimeType.mjs +16 -0
  63. package/dist/validations/hasSize.d.mts +10 -0
  64. package/dist/validations/hasSize.mjs +12 -0
  65. package/dist/validations/index.d.mts +15 -0
  66. package/dist/validations/index.mjs +15 -0
  67. package/dist/validations/schema.d.mts +102 -0
  68. package/dist/validations/schema.mjs +109 -0
  69. package/dist/validations/{snowflake.d.ts → snowflake.d.mts} +9 -7
  70. package/dist/validations/snowflake.mjs +28 -0
  71. package/dist/validations/{timestamp.d.ts → timestamp.d.mts} +5 -1
  72. package/dist/validations/timestamp.mjs +6 -0
  73. package/dist/validations/toBlob.d.mts +9 -0
  74. package/dist/validations/toBlob.mjs +17 -0
  75. package/dist/validations/url.d.mts +6 -0
  76. package/dist/validations/url.mjs +5 -0
  77. package/package.json +13 -23
  78. package/dist/index.d.ts +0 -2
  79. package/dist/index.js +0 -3
  80. package/dist/index.js.map +0 -1
  81. package/dist/requests/DiscordSession.d.ts +0 -16
  82. package/dist/requests/DiscordSession.js +0 -42
  83. package/dist/requests/DiscordSession.js.map +0 -1
  84. package/dist/requests/addParams.d.ts +0 -2
  85. package/dist/requests/addParams.js +0 -11
  86. package/dist/requests/addParams.js.map +0 -1
  87. package/dist/requests/buildURL.d.ts +0 -2
  88. package/dist/requests/buildURL.js +0 -4
  89. package/dist/requests/buildURL.js.map +0 -1
  90. package/dist/requests/getAsset.d.ts +0 -2
  91. package/dist/requests/getAsset.js +0 -3
  92. package/dist/requests/getAsset.js.map +0 -1
  93. package/dist/requests/index.d.ts +0 -7
  94. package/dist/requests/index.js +0 -8
  95. package/dist/requests/index.js.map +0 -1
  96. package/dist/requests/methods.d.ts +0 -13
  97. package/dist/requests/methods.js +0 -8
  98. package/dist/requests/methods.js.map +0 -1
  99. package/dist/requests/request.d.ts +0 -2
  100. package/dist/requests/request.js +0 -35
  101. package/dist/requests/request.js.map +0 -1
  102. package/dist/requests/toProcedure.d.ts +0 -31
  103. package/dist/requests/toProcedure.js +0 -36
  104. package/dist/requests/toProcedure.js.map +0 -1
  105. package/dist/requests/toQuery.d.ts +0 -28
  106. package/dist/requests/toQuery.js +0 -9
  107. package/dist/requests/toQuery.js.map +0 -1
  108. package/dist/requests/toValidated.d.ts +0 -13
  109. package/dist/requests/toValidated.js +0 -29
  110. package/dist/requests/toValidated.js.map +0 -1
  111. package/dist/utils/isBetween.d.ts +0 -1
  112. package/dist/utils/isBetween.js +0 -2
  113. package/dist/utils/isBetween.js.map +0 -1
  114. package/dist/utils/isNonNullable.js +0 -20
  115. package/dist/utils/isNonNullable.js.map +0 -1
  116. package/dist/utils/isNumericString.d.ts +0 -1
  117. package/dist/utils/isNumericString.js +0 -2
  118. package/dist/utils/isNumericString.js.map +0 -1
  119. package/dist/utils/isObject.d.ts +0 -1
  120. package/dist/utils/isObject.js +0 -2
  121. package/dist/utils/isObject.js.map +0 -1
  122. package/dist/utils/toCamelCase.d.ts +0 -1
  123. package/dist/utils/toCamelCase.js +0 -2
  124. package/dist/utils/toCamelCase.js.map +0 -1
  125. package/dist/utils/toCamelKeys.d.ts +0 -2
  126. package/dist/utils/toCamelKeys.js +0 -16
  127. package/dist/utils/toCamelKeys.js.map +0 -1
  128. package/dist/utils/toSnakeCase.d.ts +0 -1
  129. package/dist/utils/toSnakeCase.js +0 -4
  130. package/dist/utils/toSnakeCase.js.map +0 -1
  131. package/dist/utils/toSnakeKeys.d.ts +0 -2
  132. package/dist/utils/toSnakeKeys.js +0 -16
  133. package/dist/utils/toSnakeKeys.js.map +0 -1
  134. package/dist/validations/asDigits.d.ts +0 -6
  135. package/dist/validations/asDigits.js +0 -6
  136. package/dist/validations/asDigits.js.map +0 -1
  137. package/dist/validations/asInteger.d.ts +0 -6
  138. package/dist/validations/asInteger.js +0 -6
  139. package/dist/validations/asInteger.js.map +0 -1
  140. package/dist/validations/bitfield.d.ts +0 -17
  141. package/dist/validations/bitfield.js +0 -37
  142. package/dist/validations/bitfield.js.map +0 -1
  143. package/dist/validations/boundedArray.d.ts +0 -6
  144. package/dist/validations/boundedArray.js +0 -8
  145. package/dist/validations/boundedArray.js.map +0 -1
  146. package/dist/validations/boundedInteger.d.ts +0 -6
  147. package/dist/validations/boundedInteger.js +0 -8
  148. package/dist/validations/boundedInteger.js.map +0 -1
  149. package/dist/validations/boundedString.d.ts +0 -6
  150. package/dist/validations/boundedString.js +0 -8
  151. package/dist/validations/boundedString.js.map +0 -1
  152. package/dist/validations/datauri.d.ts +0 -20
  153. package/dist/validations/datauri.js +0 -20
  154. package/dist/validations/datauri.js.map +0 -1
  155. package/dist/validations/hasMimeType.d.ts +0 -10
  156. package/dist/validations/hasMimeType.js +0 -18
  157. package/dist/validations/hasMimeType.js.map +0 -1
  158. package/dist/validations/hasSize.d.ts +0 -5
  159. package/dist/validations/hasSize.js +0 -13
  160. package/dist/validations/hasSize.js.map +0 -1
  161. package/dist/validations/index.d.ts +0 -12
  162. package/dist/validations/index.js +0 -13
  163. package/dist/validations/index.js.map +0 -1
  164. package/dist/validations/snowflake.js +0 -39
  165. package/dist/validations/snowflake.js.map +0 -1
  166. package/dist/validations/timestamp.js +0 -4
  167. package/dist/validations/timestamp.js.map +0 -1
  168. package/dist/validations/toBlob.d.ts +0 -4
  169. package/dist/validations/toBlob.js +0 -19
  170. package/dist/validations/toBlob.js.map +0 -1
  171. package/dist/validations/url.d.ts +0 -2
  172. package/dist/validations/url.js +0 -3
  173. package/dist/validations/url.js.map +0 -1
@@ -0,0 +1,114 @@
1
+ import { isObject } from "../utils/isObject.mjs";
2
+ import * as v from "valibot";
3
+ //#region src/validations/fileUpload.ts
4
+ /** Type guard — true if `val` looks like a {@link FileUpload}. */
5
+ const isFileUpload = (val) => {
6
+ if (!isObject(val)) return false;
7
+ const record = val;
8
+ return typeof record.filename === `string` && typeof record.content?.arrayBuffer === `function`;
9
+ };
10
+ /**
11
+ * Valibot schema for a single file upload. Validates the shape and
12
+ * surfaces a clean TypeScript type to consumers.
13
+ */
14
+ const fileUpload = v.pipe(v.custom(isFileUpload, `Expected a FileUpload object`), v.title(`fileUpload`));
15
+ /**
16
+ * Walks a value and returns all {@link FileUpload}s within it, along
17
+ * with the path each one occupies.
18
+ */
19
+ const collectFileUploads = (val, path = []) => {
20
+ if (isFileUpload(val)) return [{
21
+ path,
22
+ file: val
23
+ }];
24
+ if (Array.isArray(val)) return val.flatMap((item, i) => collectFileUploads(item, [...path, i]));
25
+ if (isObject(val)) return Object.entries(val).flatMap(([key, item]) => collectFileUploads(item, [...path, key]));
26
+ return [];
27
+ };
28
+ /**
29
+ * Returns a deep copy of `body` with the value at `path` replaced by
30
+ * `replacement`. Pure: never mutates `body` or any of its children.
31
+ */
32
+ const replaceAtPath = (body, path, replacement) => {
33
+ if (path.length === 0) return replacement;
34
+ const [head, ...rest] = path;
35
+ if (Array.isArray(body)) return body.map((item, index) => index === head ? replaceAtPath(item, rest, replacement) : item);
36
+ if (isObject(body)) return Object.fromEntries(Object.entries(body).map(([key, item]) => [key, key === head ? replaceAtPath(item, rest, replacement) : item]));
37
+ return body;
38
+ };
39
+ /**
40
+ * Given a body that contains one or more {@link FileUpload}s, produce
41
+ * a `FormData` payload Discord can consume.
42
+ *
43
+ * Files are appended as `files[n]` parts; the rest of the body is
44
+ * snake_cased and JSON-stringified into a `payload_json` part.
45
+ *
46
+ * Each FileUpload in the body is replaced by its attachment placeholder
47
+ * `{ id: n }` so that an `attachments` array (if present) can reference
48
+ * uploads by index. Discord matches these placeholders to the
49
+ * corresponding `files[n]` parts.
50
+ */
51
+ const toMultipartBody = (body, toSnakeKeys) => {
52
+ const uploads = collectFileUploads(body);
53
+ if (uploads.length === 0) throw new Error(`toMultipartBody called with a body containing no FileUploads`);
54
+ const payload = uploads.reduce((acc, { path }, index) => replaceAtPath(acc, path, { id: index }), body);
55
+ const fileEntries = uploads.map(({ file }, index) => {
56
+ const blob = file.contentType && file.content.type !== file.contentType ? new Blob([file.content], { type: file.contentType }) : file.content;
57
+ return [
58
+ `files[${index}]`,
59
+ blob,
60
+ file.filename
61
+ ];
62
+ });
63
+ const payloadJson = new Blob([JSON.stringify(toSnakeKeys(payload))], { type: `application/json` });
64
+ const form = new FormData();
65
+ fileEntries.forEach(([name, blob, filename]) => form.append(name, blob, filename));
66
+ form.append(`payload_json`, payloadJson);
67
+ return form;
68
+ };
69
+ /**
70
+ * Sentinel symbol used to mark a validated body as multipart-eligible.
71
+ * Read by the request layer at serialization time.
72
+ *
73
+ * The marker is attached as a non-enumerable property on the parsed
74
+ * body so it doesn't leak into JSON serialization or the TypeScript
75
+ * output type.
76
+ */
77
+ const MULTIPART_MARKER = Symbol.for(`@discordkit/core/multipart`);
78
+ /**
79
+ * Returns true if `body` should be serialized as `multipart/form-data`.
80
+ *
81
+ * Two signals trigger multipart serialization:
82
+ *
83
+ * 1. **Schema-tagged** — the body was parsed by a {@link multipart}-wrapped
84
+ * schema and at least one {@link FileUpload} was present. The wrapper
85
+ * stamps a non-enumerable {@link MULTIPART_MARKER} on the parsed value.
86
+ * This is the fast path for the validated flow (e.g., `toValidated`).
87
+ *
88
+ * 2. **Value-detected** — the body contains a {@link FileUpload} anywhere
89
+ * in its shape, regardless of whether it was validated. This safety net
90
+ * keeps consumers who bypass validation from silently dropping their
91
+ * files into a JSON body where `Blob` would serialize as `{}`.
92
+ *
93
+ * Either signal switches the request to multipart.
94
+ */
95
+ const shouldSerializeAsMultipart = (body) => {
96
+ if (typeof body !== `object` || body === null) return false;
97
+ if (body[MULTIPART_MARKER] === true) return true;
98
+ return collectFileUploads(body).length > 0;
99
+ };
100
+ function multipart(entries, options) {
101
+ const base = v.object(entries);
102
+ const inner = options?.partial ? v.partial(base) : base;
103
+ return v.pipe(inner, v.transform((parsed) => {
104
+ if (!(collectFileUploads(parsed).length > 0)) return parsed;
105
+ return Object.defineProperty(parsed, MULTIPART_MARKER, {
106
+ value: true,
107
+ enumerable: false,
108
+ configurable: false,
109
+ writable: false
110
+ });
111
+ }));
112
+ }
113
+ //#endregion
114
+ export { MULTIPART_MARKER, collectFileUploads, fileUpload, isFileUpload, multipart, shouldSerializeAsMultipart, toMultipartBody };
@@ -0,0 +1,16 @@
1
+ import { CheckAction } from "valibot";
2
+
3
+ //#region src/validations/hasMimeType.d.ts
4
+ /**
5
+ * Validation action to check if the given `datauri` string
6
+ * has one of the provided [MIME types](https://en.wikipedia.org/wiki/Media_type)
7
+ *
8
+ * @__NO_SIDE_EFFECTS__
9
+ */
10
+ declare const hasMimeType: (/** an array of MIME types to validate against */
11
+
12
+ requirement: Array<`${string}/${string}`>, /** an optional error message on failed validation */
13
+
14
+ message?: "Received badly formatted Data URI") => CheckAction<string, typeof message>;
15
+ //#endregion
16
+ export { hasMimeType };
@@ -0,0 +1,16 @@
1
+ import { extractDataURIMetadata } from "./datauri.mjs";
2
+ import { check } from "valibot";
3
+ //#region src/validations/hasMimeType.ts
4
+ /**
5
+ * Validation action to check if the given `datauri` string
6
+ * has one of the provided [MIME types](https://en.wikipedia.org/wiki/Media_type)
7
+ *
8
+ * @__NO_SIDE_EFFECTS__
9
+ */
10
+ const hasMimeType = (requirement, message = `Received badly formatted Data URI`) => check((input) => {
11
+ const { mimeType } = extractDataURIMetadata(input);
12
+ if (typeof mimeType === `undefined`) return false;
13
+ return requirement.includes(mimeType);
14
+ }, message);
15
+ //#endregion
16
+ export { hasMimeType };
@@ -0,0 +1,10 @@
1
+ import { CheckAction } from "valibot";
2
+
3
+ //#region src/validations/hasSize.d.ts
4
+ /** @__NO_SIDE_EFFECTS__ */
5
+ declare const hasSize: (size: number | {
6
+ min?: number;
7
+ max?: number;
8
+ }, message?: "Data URI is the incorrect size") => CheckAction<string, typeof message>;
9
+ //#endregion
10
+ export { hasSize };
@@ -0,0 +1,12 @@
1
+ import { extractDataURIMetadata, toBase64 } from "./datauri.mjs";
2
+ import { check } from "valibot";
3
+ //#region src/validations/hasSize.ts
4
+ /** @__NO_SIDE_EFFECTS__ */
5
+ const hasSize = (size, message = `Data URI is the incorrect size`) => check((dataURI) => {
6
+ const { data } = extractDataURIMetadata(dataURI);
7
+ if (typeof data === `undefined`) throw new Error(`Received badly formatted Data URI`);
8
+ const actual = toBase64(data).length;
9
+ return typeof size === `number` ? size === actual : actual >= (size.min ?? 0) && actual <= (size.max ?? Infinity);
10
+ }, message);
11
+ //#endregion
12
+ export { hasSize };
@@ -0,0 +1,15 @@
1
+ import { Flags, bitfield } from "./bitfield.mjs";
2
+ import { asDigits } from "./asDigits.mjs";
3
+ import { asInteger } from "./asInteger.mjs";
4
+ import { boundedArray } from "./boundedArray.mjs";
5
+ import { boundedInteger } from "./boundedInteger.mjs";
6
+ import { boundedString } from "./boundedString.mjs";
7
+ import { datauri, datauriRegex, extractDataURIMetadata, toBase64 } from "./datauri.mjs";
8
+ import { FileUpload, MULTIPART_MARKER, collectFileUploads, fileUpload, isFileUpload, multipart, shouldSerializeAsMultipart, toMultipartBody } from "./fileUpload.mjs";
9
+ import { hasMimeType } from "./hasMimeType.mjs";
10
+ import { omitFields, partialSchema, pickFields, requiredFields, schema, variantSchema } from "./schema.mjs";
11
+ import { toBlob } from "./toBlob.mjs";
12
+ import { DISCORD_EPOCH, snowflake, snowflakeToDate } from "./snowflake.mjs";
13
+ import { timestamp } from "./timestamp.mjs";
14
+ import { url } from "./url.mjs";
15
+ export { DISCORD_EPOCH, FileUpload, Flags, MULTIPART_MARKER, asDigits, asInteger, bitfield, boundedArray, boundedInteger, boundedString, collectFileUploads, datauri, datauriRegex, extractDataURIMetadata, fileUpload, hasMimeType, isFileUpload, multipart, omitFields, partialSchema, pickFields, requiredFields, schema, shouldSerializeAsMultipart, snowflake, snowflakeToDate, timestamp, toBase64, toBlob, toMultipartBody, url, variantSchema };
@@ -0,0 +1,15 @@
1
+ import { MULTIPART_MARKER, collectFileUploads, fileUpload, isFileUpload, multipart, shouldSerializeAsMultipart, toMultipartBody } from "./fileUpload.mjs";
2
+ import { asDigits } from "./asDigits.mjs";
3
+ import { asInteger } from "./asInteger.mjs";
4
+ import { bitfield } from "./bitfield.mjs";
5
+ import { boundedArray } from "./boundedArray.mjs";
6
+ import { boundedInteger } from "./boundedInteger.mjs";
7
+ import { boundedString } from "./boundedString.mjs";
8
+ import { datauri, datauriRegex, extractDataURIMetadata, toBase64 } from "./datauri.mjs";
9
+ import { hasMimeType } from "./hasMimeType.mjs";
10
+ import { omitFields, partialSchema, pickFields, requiredFields, schema, variantSchema } from "./schema.mjs";
11
+ import { toBlob } from "./toBlob.mjs";
12
+ import { DISCORD_EPOCH, snowflake, snowflakeToDate } from "./snowflake.mjs";
13
+ import { timestamp } from "./timestamp.mjs";
14
+ import { url } from "./url.mjs";
15
+ export { DISCORD_EPOCH, MULTIPART_MARKER, asDigits, asInteger, bitfield, boundedArray, boundedInteger, boundedString, collectFileUploads, datauri, datauriRegex, extractDataURIMetadata, fileUpload, hasMimeType, isFileUpload, multipart, omitFields, partialSchema, pickFields, requiredFields, schema, shouldSerializeAsMultipart, snowflake, snowflakeToDate, timestamp, toBase64, toBlob, toMultipartBody, url, variantSchema };
@@ -0,0 +1,102 @@
1
+ import * as v from "valibot";
2
+
3
+ //#region src/validations/schema.d.ts
4
+ /**
5
+ * Annotate a schema's *published* type as `v.GenericSchema<T>` so
6
+ * downstream `.d.ts` emit stops inlining the inner `ObjectSchema`'s
7
+ * entries map and instead references `T` by name. The runtime value
8
+ * is unchanged.
9
+ *
10
+ * Use at every type-defining schema export:
11
+ *
12
+ * ```ts
13
+ * const _userSchema = v.object({...});
14
+ * export interface User extends v.InferOutput<typeof _userSchema> {}
15
+ * export const userSchema = schema<User>(_userSchema);
16
+ * ```
17
+ *
18
+ * **Why this exists**
19
+ *
20
+ * Without annotation, `typeof userSchema` is the full
21
+ * `v.ObjectSchema<{ id: v.GenericSchema<string>, ... }>` shape. Every
22
+ * downstream file that references `userSchema` (Guild, Application,
23
+ * Member, …) re-inlines that entries map into its own emitted
24
+ * declarations. This causes:
25
+ *
26
+ * - dramatic duplication of nested object shapes in `.d.ts` output
27
+ * - slow IDE hover (tsserver expands the full shape on every
28
+ * reference)
29
+ * - occasional TS2502 / type-too-complex errors on deeply circular
30
+ * schemas (Channel, Message, …)
31
+ *
32
+ * After annotation, downstream `.d.ts` files reference `User` by
33
+ * name from `./user/types/User.d.mts` and never inline its body.
34
+ *
35
+ * **Trade-off**
36
+ *
37
+ * `v.partial(userSchema)`, `v.pick(userSchema, [...])`, `v.omit(...)`,
38
+ * and `userSchema.entries.id` stop type-checking because the constraint
39
+ * is `ObjectSchema<...>` not `GenericSchema<T>`. Use the
40
+ * {@link partialSchema}, {@link pickFields}, {@link omitFields},
41
+ * {@link requiredFields}, and {@link variantSchema} helpers below for
42
+ * the common cases.
43
+ *
44
+ * @__NO_SIDE_EFFECTS__
45
+ */
46
+ declare const schema: <T>(s: v.GenericSchema<unknown>) => v.GenericSchema<T>;
47
+ /**
48
+ * Make every key of `T` optional, both at the type level and at
49
+ * runtime. Equivalent to Valibot's `v.partial`, but accepts an
50
+ * annotated `GenericSchema<T>` (which `v.partial` would reject because
51
+ * its constraint is `ObjectSchema<...>`).
52
+ *
53
+ * @__NO_SIDE_EFFECTS__
54
+ */
55
+ declare const partialSchema: <T>(s: v.GenericSchema<T>) => v.GenericSchema<Partial<T>>;
56
+ /**
57
+ * Pick a subset of fields from `T` at both type and runtime levels.
58
+ * Equivalent to Valibot's `v.pick`. See {@link partialSchema}.
59
+ *
60
+ * @__NO_SIDE_EFFECTS__
61
+ */
62
+ declare const pickFields: <T, K extends keyof T & string>(s: v.GenericSchema<T>, keys: readonly K[]) => v.GenericSchema<Pick<T, K>>;
63
+ /**
64
+ * Omit a subset of fields from `T` at both type and runtime levels.
65
+ * Equivalent to Valibot's `v.omit`. See {@link partialSchema}.
66
+ *
67
+ * @__NO_SIDE_EFFECTS__
68
+ */
69
+ declare const omitFields: <T, K extends keyof T & string>(s: v.GenericSchema<T>, keys: readonly K[]) => v.GenericSchema<Omit<T, K>>;
70
+ /**
71
+ * Mark a subset of fields on `T` as required at both type and runtime
72
+ * levels. Equivalent to Valibot's `v.required`. See {@link partialSchema}.
73
+ *
74
+ * @__NO_SIDE_EFFECTS__
75
+ */
76
+ declare const requiredFields: <T, K extends keyof T & string>(s: v.GenericSchema<T>, keys: readonly K[]) => v.GenericSchema<T & Required<Pick<T, K>>>;
77
+ /**
78
+ * Build a discriminated union schema. Equivalent to Valibot's
79
+ * `v.variant`, but accepts annotated `GenericSchema<T>` variants
80
+ * (which `v.variant` would reject because its constraint is
81
+ * `ObjectSchema<...>`).
82
+ *
83
+ * Prefer `v.variant` whenever variants share a literal discriminator
84
+ * field — it dispatches on that field at runtime, producing focused
85
+ * error messages and O(1) validation, vs. {@link v.union}'s O(n)
86
+ * try-each behavior.
87
+ *
88
+ * @example
89
+ * ```ts
90
+ * export const channelSchema = variantSchema<Channel>(`type`, [
91
+ * guildTextChannelSchema,
92
+ * guildVoiceChannelSchema,
93
+ * threadChannelSchema,
94
+ * // ...
95
+ * ]);
96
+ * ```
97
+ *
98
+ * @__NO_SIDE_EFFECTS__
99
+ */
100
+ declare const variantSchema: <T>(key: keyof T & string, schemas: ReadonlyArray<v.GenericSchema<unknown>>) => v.GenericSchema<T>;
101
+ //#endregion
102
+ export { omitFields, partialSchema, pickFields, requiredFields, schema, variantSchema };
@@ -0,0 +1,109 @@
1
+ import * as v from "valibot";
2
+ //#region src/validations/schema.ts
3
+ /**
4
+ * Annotate a schema's *published* type as `v.GenericSchema<T>` so
5
+ * downstream `.d.ts` emit stops inlining the inner `ObjectSchema`'s
6
+ * entries map and instead references `T` by name. The runtime value
7
+ * is unchanged.
8
+ *
9
+ * Use at every type-defining schema export:
10
+ *
11
+ * ```ts
12
+ * const _userSchema = v.object({...});
13
+ * export interface User extends v.InferOutput<typeof _userSchema> {}
14
+ * export const userSchema = schema<User>(_userSchema);
15
+ * ```
16
+ *
17
+ * **Why this exists**
18
+ *
19
+ * Without annotation, `typeof userSchema` is the full
20
+ * `v.ObjectSchema<{ id: v.GenericSchema<string>, ... }>` shape. Every
21
+ * downstream file that references `userSchema` (Guild, Application,
22
+ * Member, …) re-inlines that entries map into its own emitted
23
+ * declarations. This causes:
24
+ *
25
+ * - dramatic duplication of nested object shapes in `.d.ts` output
26
+ * - slow IDE hover (tsserver expands the full shape on every
27
+ * reference)
28
+ * - occasional TS2502 / type-too-complex errors on deeply circular
29
+ * schemas (Channel, Message, …)
30
+ *
31
+ * After annotation, downstream `.d.ts` files reference `User` by
32
+ * name from `./user/types/User.d.mts` and never inline its body.
33
+ *
34
+ * **Trade-off**
35
+ *
36
+ * `v.partial(userSchema)`, `v.pick(userSchema, [...])`, `v.omit(...)`,
37
+ * and `userSchema.entries.id` stop type-checking because the constraint
38
+ * is `ObjectSchema<...>` not `GenericSchema<T>`. Use the
39
+ * {@link partialSchema}, {@link pickFields}, {@link omitFields},
40
+ * {@link requiredFields}, and {@link variantSchema} helpers below for
41
+ * the common cases.
42
+ *
43
+ * @__NO_SIDE_EFFECTS__
44
+ */
45
+ const schema = (s) => s;
46
+ /**
47
+ * Make every key of `T` optional, both at the type level and at
48
+ * runtime. Equivalent to Valibot's `v.partial`, but accepts an
49
+ * annotated `GenericSchema<T>` (which `v.partial` would reject because
50
+ * its constraint is `ObjectSchema<...>`).
51
+ *
52
+ * @__NO_SIDE_EFFECTS__
53
+ */
54
+ const partialSchema = (s) => v.partial(s);
55
+ /**
56
+ * Pick a subset of fields from `T` at both type and runtime levels.
57
+ * Equivalent to Valibot's `v.pick`. See {@link partialSchema}.
58
+ *
59
+ * @__NO_SIDE_EFFECTS__
60
+ */
61
+ const pickFields = (s, keys) => {
62
+ return v.pick(s, keys);
63
+ };
64
+ /**
65
+ * Omit a subset of fields from `T` at both type and runtime levels.
66
+ * Equivalent to Valibot's `v.omit`. See {@link partialSchema}.
67
+ *
68
+ * @__NO_SIDE_EFFECTS__
69
+ */
70
+ const omitFields = (s, keys) => {
71
+ return v.omit(s, keys);
72
+ };
73
+ /**
74
+ * Mark a subset of fields on `T` as required at both type and runtime
75
+ * levels. Equivalent to Valibot's `v.required`. See {@link partialSchema}.
76
+ *
77
+ * @__NO_SIDE_EFFECTS__
78
+ */
79
+ const requiredFields = (s, keys) => {
80
+ return v.required(s, keys);
81
+ };
82
+ /**
83
+ * Build a discriminated union schema. Equivalent to Valibot's
84
+ * `v.variant`, but accepts annotated `GenericSchema<T>` variants
85
+ * (which `v.variant` would reject because its constraint is
86
+ * `ObjectSchema<...>`).
87
+ *
88
+ * Prefer `v.variant` whenever variants share a literal discriminator
89
+ * field — it dispatches on that field at runtime, producing focused
90
+ * error messages and O(1) validation, vs. {@link v.union}'s O(n)
91
+ * try-each behavior.
92
+ *
93
+ * @example
94
+ * ```ts
95
+ * export const channelSchema = variantSchema<Channel>(`type`, [
96
+ * guildTextChannelSchema,
97
+ * guildVoiceChannelSchema,
98
+ * threadChannelSchema,
99
+ * // ...
100
+ * ]);
101
+ * ```
102
+ *
103
+ * @__NO_SIDE_EFFECTS__
104
+ */
105
+ const variantSchema = (key, schemas) => {
106
+ return v.variant(key, schemas);
107
+ };
108
+ //#endregion
109
+ export { omitFields, partialSchema, pickFields, requiredFields, schema, variantSchema };
@@ -1,18 +1,18 @@
1
1
  import * as v from "valibot";
2
+
3
+ //#region src/validations/snowflake.d.ts
2
4
  /** UNIX timestamp representing the first second of 2015 */
3
- export declare const DISCORD_EPOCH = 1420070400000n;
5
+ declare const DISCORD_EPOCH = 1420070400000n;
4
6
  /**
5
7
  * Converts a `snowflake` string to a Date relative to the given epoch
6
8
  *
7
9
  * Uses Discord's epoch by default
8
10
  *
9
11
  * https://discord.com/developers/docs/reference#snowflakes
12
+ *
13
+ * @__NO_SIDE_EFFECTS__
10
14
  */
11
- export declare const snowflakeToDate: (
12
- /** A snowflake string to convert */
13
- val: string | number | bigint,
14
- /** time in milliseconds to use as the epoch to derive a Date from */
15
- epoch?: bigint) => Date;
15
+ declare const snowflakeToDate: (/** A snowflake string to convert */val: string | number | bigint, /** time in milliseconds to use as the epoch to derive a Date from */epoch?: bigint) => Date;
16
16
  /**
17
17
  * An up to 64-bit unsigned numeric value derived from a timestamp which
18
18
  * serves as a unique identifier within Discord.
@@ -22,4 +22,6 @@ epoch?: bigint) => Date;
22
22
  * by checking if it's derived timestamp is a valid time at or after
23
23
  * the Discord epoch (the first second of 2015, ie: `1420070400000`).
24
24
  */
25
- export declare const snowflake: v.GenericSchema<string>;
25
+ declare const snowflake: v.GenericSchema<string>;
26
+ //#endregion
27
+ export { DISCORD_EPOCH, snowflake, snowflakeToDate };
@@ -0,0 +1,28 @@
1
+ import { isNonNullable } from "../utils/isNonNullable.mjs";
2
+ import { isNumericString } from "../utils/isNumericString.mjs";
3
+ import * as v from "valibot";
4
+ //#region src/validations/snowflake.ts
5
+ /** UNIX timestamp representing the first second of 2015 */
6
+ const DISCORD_EPOCH = 1420070400000n;
7
+ /**
8
+ * Converts a `snowflake` string to a Date relative to the given epoch
9
+ *
10
+ * Uses Discord's epoch by default
11
+ *
12
+ * https://discord.com/developers/docs/reference#snowflakes
13
+ *
14
+ * @__NO_SIDE_EFFECTS__
15
+ */
16
+ const snowflakeToDate = (val, epoch = DISCORD_EPOCH) => new Date(Number((BigInt(val) >> 22n) + epoch));
17
+ /**
18
+ * An up to 64-bit unsigned numeric value derived from a timestamp which
19
+ * serves as a unique identifier within Discord.
20
+ *
21
+ * Validates whether a given `number`, `bigint`, or numeric `string` is
22
+ * a valid [Snowflake](https://discord.com/developers/docs/reference#snowflakes)
23
+ * by checking if it's derived timestamp is a valid time at or after
24
+ * the Discord epoch (the first second of 2015, ie: `1420070400000`).
25
+ */
26
+ const snowflake = v.pipe(v.custom((val) => isNonNullable(val) && (typeof val === `bigint` || typeof val === `number` || isNumericString(val)) && snowflakeToDate(val).getTime() >= 1420070400000n, `Invalid Snowflake`), v.title(`snowflake`));
27
+ //#endregion
28
+ export { DISCORD_EPOCH, snowflake, snowflakeToDate };
@@ -1,3 +1,7 @@
1
1
  import * as v from "valibot";
2
+
3
+ //#region src/validations/timestamp.d.ts
2
4
  /** An [ISO8601](https://www.loc.gov/standards/datetime/iso-tc154-wg5_n0038_iso_wd_8601-1_2016-02-16.pdf) timestamp */
3
- export declare const timestamp: v.GenericSchema<string>;
5
+ declare const timestamp: v.GenericSchema<string>;
6
+ //#endregion
7
+ export { timestamp };
@@ -0,0 +1,6 @@
1
+ import * as v from "valibot";
2
+ //#region src/validations/timestamp.ts
3
+ /** An [ISO8601](https://www.loc.gov/standards/datetime/iso-tc154-wg5_n0038_iso_wd_8601-1_2016-02-16.pdf) timestamp */
4
+ const timestamp = v.message(v.pipe(v.string(), v.isoTimestamp()), (issue) => `Expected a valid timestamp, received: ${issue.received}`);
5
+ //#endregion
6
+ export { timestamp };
@@ -0,0 +1,9 @@
1
+ import * as _$valibot from "valibot";
2
+
3
+ //#region src/validations/toBlob.d.ts
4
+ /**
5
+ * Transforms a `datauri` string to a Blob
6
+ */
7
+ declare const toBlob: _$valibot.TransformAction<string, Blob>;
8
+ //#endregion
9
+ export { toBlob };
@@ -0,0 +1,17 @@
1
+ import { extractDataURIMetadata, toBase64 } from "./datauri.mjs";
2
+ import { transform } from "valibot";
3
+ //#region src/validations/toBlob.ts
4
+ /**
5
+ * Transforms a `datauri` string to a Blob
6
+ */
7
+ const toBlob = transform((dataURI) => {
8
+ const { mimeType, data } = extractDataURIMetadata(dataURI);
9
+ if (typeof mimeType === `undefined` || typeof data === `undefined`) throw new Error(`Received badly formatted Data URI`);
10
+ const base64 = toBase64(data);
11
+ const ab = new ArrayBuffer(base64.length);
12
+ const ia = new Uint8Array(ab);
13
+ for (let i = 0; i < base64.length; i++) ia[i] = base64.charCodeAt(i);
14
+ return new Blob([ab], { type: mimeType });
15
+ });
16
+ //#endregion
17
+ export { toBlob };
@@ -0,0 +1,6 @@
1
+ import * as v from "valibot";
2
+
3
+ //#region src/validations/url.d.ts
4
+ declare const url: v.GenericSchema<string>;
5
+ //#endregion
6
+ export { url };
@@ -0,0 +1,5 @@
1
+ import * as v from "valibot";
2
+ //#region src/validations/url.ts
3
+ const url = v.message(v.pipe(v.string(), v.url()), (issue) => `Expected a valid URL, received: ${issue.received}`);
4
+ //#endregion
5
+ export { url };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@discordkit/core",
3
3
  "description": "Core utility functions for Discordkit",
4
- "version": "3.1.0",
4
+ "version": "4.0.0",
5
5
  "license": "MIT",
6
6
  "author": "Drake Costa <drake@saeris.gg> (https://saeris.gg)",
7
7
  "keywords": [
@@ -20,21 +20,16 @@
20
20
  "registry": "https://registry.npmjs.org"
21
21
  },
22
22
  "type": "module",
23
- "main": "./dist/index.js",
24
- "module": "./dist/index.js",
25
- "types": "./dist/index.d.ts",
26
- "imports": {
27
- "#mock-utils": "../../scripts/mock-utils.ts",
28
- "#test-utils": "../../scripts/test-utils.ts"
29
- },
30
23
  "exports": {
31
24
  ".": {
32
- "types": "./dist/index.d.ts",
33
- "default": "./dist/index.js"
25
+ "development": "./src/index.ts",
26
+ "types": "./dist/index.d.mts",
27
+ "import": "./dist/index.mjs"
34
28
  },
35
29
  "./*": {
36
- "types": "./dist/*.d.ts",
37
- "default": "./dist/*.js"
30
+ "development": "./src/*.ts",
31
+ "types": "./dist/*.d.mts",
32
+ "import": "./dist/*.mjs"
38
33
  },
39
34
  "./package.json": "./package.json"
40
35
  },
@@ -42,18 +37,13 @@
42
37
  "dist/**/*"
43
38
  ],
44
39
  "sideEffects": false,
45
- "scripts": {
46
- "dev": "yarn build --watch --declarationmap",
47
- "build": "tsc",
48
- "lint": "eslint --cache"
49
- },
50
40
  "peerDependencies": {
51
- "valibot": ">= 1.0.0"
52
- },
53
- "devDependencies": {
54
- "typescript": "^5.8.3"
41
+ "valibot": ">= 1.4.0"
55
42
  },
56
43
  "dependencies": {
57
- "type-fest": "^4.41.0"
44
+ "type-fest": "^5.6.0"
45
+ },
46
+ "devDependencies": {
47
+ "@discordkit/test-utils": "^0.0.0"
58
48
  }
59
- }
49
+ }
package/dist/index.d.ts DELETED
@@ -1,2 +0,0 @@
1
- export * from "./requests/index.js";
2
- export * from "./validations/index.js";
package/dist/index.js DELETED
@@ -1,3 +0,0 @@
1
- export * from "./requests/index.js";
2
- export * from "./validations/index.js";
3
- //# sourceMappingURL=index.js.map
package/dist/index.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,qBAAqB,CAAC;AACpC,cAAc,wBAAwB,CAAC"}
@@ -1,16 +0,0 @@
1
- export declare const endpoint = "https://discord.com/api/v10/";
2
- /** @internal */
3
- export declare class DiscordSession {
4
- #private;
5
- endpoint: string;
6
- get ready(): boolean;
7
- constructor(authToken?: `${`Bot` | `Bearer`} ${string}` | null);
8
- /**
9
- * Clears the current session details, then attempts to set
10
- * new session values
11
- */
12
- setToken: (token: `${`Bot` | `Bearer`} ${string}`) => this;
13
- clearSession: () => this;
14
- getSession: () => string;
15
- }
16
- export declare const discord: DiscordSession;