@md-oss/common 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +5 -0
- package/README.md +335 -0
- package/dist/api/errors.cjs +2 -0
- package/dist/api/errors.cjs.map +1 -0
- package/dist/api/errors.d.cts +53 -0
- package/dist/api/errors.d.mts +53 -0
- package/dist/api/errors.mjs +2 -0
- package/dist/api/errors.mjs.map +1 -0
- package/dist/api/index.cjs +2 -0
- package/dist/api/index.cjs.map +1 -0
- package/dist/api/index.d.cts +4 -0
- package/dist/api/index.d.mts +4 -0
- package/dist/api/index.mjs +2 -0
- package/dist/api/index.mjs.map +1 -0
- package/dist/api/requests.d.cts +36 -0
- package/dist/api/requests.d.mts +36 -0
- package/dist/api/status-codes.cjs +2 -0
- package/dist/api/status-codes.cjs.map +1 -0
- package/dist/api/status-codes.d.cts +72 -0
- package/dist/api/status-codes.d.mts +72 -0
- package/dist/api/status-codes.mjs +2 -0
- package/dist/api/status-codes.mjs.map +1 -0
- package/dist/arrays-BkHBzTDO.mjs +2 -0
- package/dist/arrays-BkHBzTDO.mjs.map +1 -0
- package/dist/arrays-CElcW69H.d.cts +43 -0
- package/dist/arrays-CElcW69H.d.mts +43 -0
- package/dist/arrays-DaB1Xn47.cjs +2 -0
- package/dist/arrays-DaB1Xn47.cjs.map +1 -0
- package/dist/constants/bytes.cjs +2 -0
- package/dist/constants/bytes.cjs.map +1 -0
- package/dist/constants/bytes.d.cts +19 -0
- package/dist/constants/bytes.d.mts +19 -0
- package/dist/constants/bytes.mjs +2 -0
- package/dist/constants/bytes.mjs.map +1 -0
- package/dist/constants/discord.cjs +2 -0
- package/dist/constants/discord.cjs.map +1 -0
- package/dist/constants/discord.d.cts +25 -0
- package/dist/constants/discord.d.mts +25 -0
- package/dist/constants/discord.mjs +2 -0
- package/dist/constants/discord.mjs.map +1 -0
- package/dist/constants/index.cjs +2 -0
- package/dist/constants/index.cjs.map +1 -0
- package/dist/constants/index.d.cts +3 -0
- package/dist/constants/index.d.mts +3 -0
- package/dist/constants/index.mjs +2 -0
- package/dist/constants/index.mjs.map +1 -0
- package/dist/constants/time.cjs +2 -0
- package/dist/constants/time.cjs.map +1 -0
- package/dist/constants/time.d.cts +65 -0
- package/dist/constants/time.d.mts +65 -0
- package/dist/constants/time.mjs +2 -0
- package/dist/constants/time.mjs.map +1 -0
- package/dist/index.cjs +2 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +17 -0
- package/dist/index.d.mts +17 -0
- package/dist/index.mjs +2 -0
- package/dist/index.mjs.map +1 -0
- package/dist/mime-types-Bjt4NvnE.d.cts +41 -0
- package/dist/mime-types-Bjt4NvnE.d.mts +41 -0
- package/dist/mime-types-BmvBrrZ7.cjs +2 -0
- package/dist/mime-types-BmvBrrZ7.cjs.map +1 -0
- package/dist/mime-types-D_K53zgk.mjs +2 -0
- package/dist/mime-types-D_K53zgk.mjs.map +1 -0
- package/dist/numbers-BmUSgeah.d.cts +46 -0
- package/dist/numbers-BmUSgeah.d.mts +46 -0
- package/dist/numbers-Bx9JTSQe.mjs +2 -0
- package/dist/numbers-Bx9JTSQe.mjs.map +1 -0
- package/dist/numbers-DuVx9Qiu.cjs +2 -0
- package/dist/numbers-DuVx9Qiu.cjs.map +1 -0
- package/dist/objects--cO_X3EV.mjs +2 -0
- package/dist/objects--cO_X3EV.mjs.map +1 -0
- package/dist/objects-CnGoVkh6.cjs +2 -0
- package/dist/objects-CnGoVkh6.cjs.map +1 -0
- package/dist/objects-cf8j7rS8.d.cts +26 -0
- package/dist/objects-cf8j7rS8.d.mts +26 -0
- package/dist/random-D1e1NTOt.cjs +2 -0
- package/dist/random-D1e1NTOt.cjs.map +1 -0
- package/dist/random-DbidGSrQ.mjs +2 -0
- package/dist/random-DbidGSrQ.mjs.map +1 -0
- package/dist/random-DpNyGGAZ.d.cts +28 -0
- package/dist/random-DpNyGGAZ.d.mts +28 -0
- package/dist/runtime-B-jA8ZxR.cjs +2 -0
- package/dist/runtime-B-jA8ZxR.cjs.map +1 -0
- package/dist/runtime-CbfL6Fgj.mjs +2 -0
- package/dist/runtime-CbfL6Fgj.mjs.map +1 -0
- package/dist/runtime-T8Kcf7Af.d.cts +28 -0
- package/dist/runtime-T8Kcf7Af.d.mts +28 -0
- package/dist/schemas.cjs +2 -0
- package/dist/schemas.cjs.map +1 -0
- package/dist/schemas.d.cts +36 -0
- package/dist/schemas.d.mts +36 -0
- package/dist/schemas.mjs +2 -0
- package/dist/schemas.mjs.map +1 -0
- package/dist/strings-B0BRwNX9.mjs +3 -0
- package/dist/strings-B0BRwNX9.mjs.map +1 -0
- package/dist/strings-DV446vv-.cjs +3 -0
- package/dist/strings-DV446vv-.cjs.map +1 -0
- package/dist/strings-DhPbrpq7.d.cts +47 -0
- package/dist/strings-DhPbrpq7.d.mts +47 -0
- package/dist/time-8KLG8kBO.d.cts +29 -0
- package/dist/time-D8Wfqzu6.d.mts +29 -0
- package/dist/time-DmJNWZeL.mjs +2 -0
- package/dist/time-DmJNWZeL.mjs.map +1 -0
- package/dist/time-yNkQSJnQ.cjs +2 -0
- package/dist/time-yNkQSJnQ.cjs.map +1 -0
- package/dist/utils/arrays.cjs +2 -0
- package/dist/utils/arrays.cjs.map +1 -0
- package/dist/utils/arrays.d.cts +1 -0
- package/dist/utils/arrays.d.mts +1 -0
- package/dist/utils/arrays.mjs +2 -0
- package/dist/utils/arrays.mjs.map +1 -0
- package/dist/utils/index.cjs +2 -0
- package/dist/utils/index.cjs.map +1 -0
- package/dist/utils/index.d.cts +9 -0
- package/dist/utils/index.d.mts +9 -0
- package/dist/utils/index.mjs +2 -0
- package/dist/utils/index.mjs.map +1 -0
- package/dist/utils/mime-types.cjs +2 -0
- package/dist/utils/mime-types.cjs.map +1 -0
- package/dist/utils/mime-types.d.cts +1 -0
- package/dist/utils/mime-types.d.mts +1 -0
- package/dist/utils/mime-types.mjs +2 -0
- package/dist/utils/mime-types.mjs.map +1 -0
- package/dist/utils/numbers.cjs +2 -0
- package/dist/utils/numbers.cjs.map +1 -0
- package/dist/utils/numbers.d.cts +1 -0
- package/dist/utils/numbers.d.mts +1 -0
- package/dist/utils/numbers.mjs +2 -0
- package/dist/utils/numbers.mjs.map +1 -0
- package/dist/utils/objects.cjs +2 -0
- package/dist/utils/objects.cjs.map +1 -0
- package/dist/utils/objects.d.cts +1 -0
- package/dist/utils/objects.d.mts +1 -0
- package/dist/utils/objects.mjs +2 -0
- package/dist/utils/objects.mjs.map +1 -0
- package/dist/utils/random.cjs +2 -0
- package/dist/utils/random.cjs.map +1 -0
- package/dist/utils/random.d.cts +1 -0
- package/dist/utils/random.d.mts +1 -0
- package/dist/utils/random.mjs +2 -0
- package/dist/utils/random.mjs.map +1 -0
- package/dist/utils/runtime.cjs +2 -0
- package/dist/utils/runtime.cjs.map +1 -0
- package/dist/utils/runtime.d.cts +1 -0
- package/dist/utils/runtime.d.mts +1 -0
- package/dist/utils/runtime.mjs +2 -0
- package/dist/utils/runtime.mjs.map +1 -0
- package/dist/utils/strings.cjs +2 -0
- package/dist/utils/strings.cjs.map +1 -0
- package/dist/utils/strings.d.cts +1 -0
- package/dist/utils/strings.d.mts +1 -0
- package/dist/utils/strings.mjs +2 -0
- package/dist/utils/strings.mjs.map +1 -0
- package/dist/utils/time.cjs +2 -0
- package/dist/utils/time.cjs.map +1 -0
- package/dist/utils/time.d.cts +2 -0
- package/dist/utils/time.d.mts +2 -0
- package/dist/utils/time.mjs +2 -0
- package/dist/utils/time.mjs.map +1 -0
- package/package.json +232 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
Copyright 2026 Mirasaki Development
|
|
2
|
+
|
|
3
|
+
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.
|
|
4
|
+
|
|
5
|
+
THE SOFTWARE IS PROVIDED “AS IS” AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,335 @@
|
|
|
1
|
+
# @md-oss/common
|
|
2
|
+
|
|
3
|
+
Constants, magic numbers, and utilities used commonly across projects.
|
|
4
|
+
|
|
5
|
+
## Table of Contents
|
|
6
|
+
|
|
7
|
+
- [Constants](#constants)
|
|
8
|
+
- [Utilities](#utilities)
|
|
9
|
+
- [API](#api)
|
|
10
|
+
- [Schemas](#schemas)
|
|
11
|
+
|
|
12
|
+
## Constants
|
|
13
|
+
|
|
14
|
+
### Byte Sizes
|
|
15
|
+
```typescript
|
|
16
|
+
import { ByteMagic } from '@md-oss/common';
|
|
17
|
+
|
|
18
|
+
console.log(ByteMagic.MEGABYTE); // 1048576
|
|
19
|
+
console.log(ByteMagic.GIGABYTE); // 1073741824
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
### Discord Limits
|
|
23
|
+
```typescript
|
|
24
|
+
import { DiscordMagic } from '@md-oss/common';
|
|
25
|
+
|
|
26
|
+
console.log(DiscordMagic.MESSAGE_CONTENT_MAX); // 2000
|
|
27
|
+
console.log(DiscordMagic.EMBED_DESCRIPTION_MAX); // 4096
|
|
28
|
+
console.log(DiscordMagic.FILE_SIZE_MAX); // 8388608 (8MB)
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### Time Conversions
|
|
32
|
+
```typescript
|
|
33
|
+
import { TimeMagic } from '@md-oss/common';
|
|
34
|
+
|
|
35
|
+
console.log(TimeMagic.SECOND); // 1000ms
|
|
36
|
+
console.log(TimeMagic.MINUTES_PER_HOUR); // 60
|
|
37
|
+
console.log(TimeMagic.MILLISECONDS_PER_DAY); // 86400000
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Utilities
|
|
41
|
+
|
|
42
|
+
### Array Utilities
|
|
43
|
+
```typescript
|
|
44
|
+
import { ArrayUtils } from '@md-oss/common';
|
|
45
|
+
|
|
46
|
+
// Chunk an array
|
|
47
|
+
ArrayUtils.chunk([1, 2, 3, 4, 5], 2); // [[1, 2], [3, 4], [5]]
|
|
48
|
+
|
|
49
|
+
// Join with max items
|
|
50
|
+
ArrayUtils.join(['a', 'b', 'c', 'd'], { maxItems: 2 });
|
|
51
|
+
// "a, b, and 2 more..."
|
|
52
|
+
|
|
53
|
+
// Type guards
|
|
54
|
+
ArrayUtils.isStringArray(['a', 'b']); // true
|
|
55
|
+
ArrayUtils.isNumberArray([1, 2, 3]); // true
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### String Utilities
|
|
59
|
+
```typescript
|
|
60
|
+
import { StringUtils } from '@md-oss/common';
|
|
61
|
+
|
|
62
|
+
// Case conversions
|
|
63
|
+
StringUtils.camelCase('hello world'); // 'helloWorld'
|
|
64
|
+
StringUtils.kebabCase('HelloWorld'); // 'hello-world'
|
|
65
|
+
StringUtils.snakeCase('HelloWorld'); // 'hello_world'
|
|
66
|
+
StringUtils.titleCase('hello world'); // 'Hello World'
|
|
67
|
+
StringUtils.pascalCase('hello world'); // 'HelloWorld'
|
|
68
|
+
|
|
69
|
+
// String manipulation
|
|
70
|
+
StringUtils.truncate('hello world', 8); // 'hello...'
|
|
71
|
+
StringUtils.pluralize('item', 2); // 'items'
|
|
72
|
+
StringUtils.replaceTags('Hello {name}', { name: 'John' }); // 'Hello John'
|
|
73
|
+
StringUtils.isUrl('https://example.com'); // true
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### Number Utilities
|
|
77
|
+
```typescript
|
|
78
|
+
import { NumberUtils } from '@md-oss/common';
|
|
79
|
+
|
|
80
|
+
// Type checks
|
|
81
|
+
NumberUtils.isInt(42); // true
|
|
82
|
+
NumberUtils.isFloat(42.5); // true
|
|
83
|
+
|
|
84
|
+
// Statistics
|
|
85
|
+
NumberUtils.calculateMean([1, 2, 3, 4]); // 2.5
|
|
86
|
+
NumberUtils.calculateMedian([1, 2, 3, 4, 5]); // 3
|
|
87
|
+
NumberUtils.calculateVariance([1, 2, 3, 4]); // 1.25
|
|
88
|
+
NumberUtils.calculateStandardDeviation([1, 2, 3, 4]); // ~1.118
|
|
89
|
+
|
|
90
|
+
// Constants
|
|
91
|
+
NumberUtils.INT32_MAX; // 2147483647
|
|
92
|
+
NumberUtils.INT64_MAX; // 9223372036854775807n
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### Object Utilities
|
|
96
|
+
```typescript
|
|
97
|
+
import { ObjectUtils } from '@md-oss/common';
|
|
98
|
+
|
|
99
|
+
// Type guards
|
|
100
|
+
ObjectUtils.isObject({ a: 1 }); // true
|
|
101
|
+
ObjectUtils.isEmptyObject({}); // true
|
|
102
|
+
|
|
103
|
+
// Deep operations
|
|
104
|
+
const cloned = ObjectUtils.deepClone({ a: { b: 1 } });
|
|
105
|
+
const merged = ObjectUtils.deepMerge({ a: 1 }, { b: 2 }); // { a: 1, b: 2 }
|
|
106
|
+
|
|
107
|
+
// Nested value access
|
|
108
|
+
ObjectUtils.getNestedValue({ a: { b: { c: 1 } } }, 'a.b.c'); // 1
|
|
109
|
+
ObjectUtils.getNestedValue({ user: { name: 'John' } }, 'user.name'); // 'John'
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### Random Utilities
|
|
113
|
+
```typescript
|
|
114
|
+
import { RandomUtils } from '@md-oss/common';
|
|
115
|
+
|
|
116
|
+
// Random numbers
|
|
117
|
+
RandomUtils.randomInt(1, 10); // random int between 1-10
|
|
118
|
+
RandomUtils.randomFloat(0, 1); // random float between 0-1
|
|
119
|
+
RandomUtils.randomBoolean(); // true or false
|
|
120
|
+
|
|
121
|
+
// Random selections
|
|
122
|
+
RandomUtils.randomItem([1, 2, 3, 4, 5]); // random item
|
|
123
|
+
RandomUtils.randomKey({ a: 1, b: 2 }); // 'a' or 'b'
|
|
124
|
+
RandomUtils.randomValue({ a: 1, b: 2 }); // 1 or 2
|
|
125
|
+
|
|
126
|
+
// Random strings
|
|
127
|
+
RandomUtils.randomString(8, { useUppercase: true, useNumeric: true });
|
|
128
|
+
// e.g., 'A3bF9xK1'
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### Time Utilities
|
|
132
|
+
```typescript
|
|
133
|
+
import { TimeUtils } from '@md-oss/common';
|
|
134
|
+
|
|
135
|
+
// Unix timestamps
|
|
136
|
+
TimeUtils.unix(Date.now()); // current unix timestamp
|
|
137
|
+
TimeUtils.unixNow(); // shorthand for unix(Date.now())
|
|
138
|
+
|
|
139
|
+
// Human-readable durations
|
|
140
|
+
TimeUtils.humanReadableMs(5000); // '5 seconds'
|
|
141
|
+
TimeUtils.humanReadableMs(125000, 3); // '2 minutes and 5 seconds'
|
|
142
|
+
|
|
143
|
+
// Async utilities
|
|
144
|
+
await TimeUtils.sleep(1000); // sleep for 1 second
|
|
145
|
+
|
|
146
|
+
// Performance tracking
|
|
147
|
+
TimeUtils.hrTimeToMs([0, 123456789]); // convert hrtime to milliseconds
|
|
148
|
+
TimeUtils.bigIntDurationToHumanReadable(startTime); // format duration
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
### Runtime Utilities
|
|
152
|
+
```typescript
|
|
153
|
+
import { RuntimeUtils } from '@md-oss/common';
|
|
154
|
+
|
|
155
|
+
// Sleep utilities
|
|
156
|
+
await RuntimeUtils.sleep(1000); // sleep for 1 second
|
|
157
|
+
await RuntimeUtils.sleepUntil(() => someCondition);
|
|
158
|
+
await RuntimeUtils.sleepUntilOrTimeout(() => someCondition, 5000);
|
|
159
|
+
|
|
160
|
+
// Await with timeout
|
|
161
|
+
const result = await RuntimeUtils.awaitOrTimeout(promise, 5000);
|
|
162
|
+
|
|
163
|
+
// Safe timeout/interval (handles values > INT32_MAX)
|
|
164
|
+
RuntimeUtils.safeSetTimeout(86400000, false, () => {
|
|
165
|
+
console.log('runs after 24 hours');
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
RuntimeUtils.safeSetInterval(1000, () => {
|
|
169
|
+
console.log('runs every second');
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
RuntimeUtils.safeSetAsyncInterval(1000, async () => {
|
|
173
|
+
await doAsyncWork();
|
|
174
|
+
});
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
### MIME Type Utilities
|
|
178
|
+
```typescript
|
|
179
|
+
import { MimeTypeUtils } from '@md-oss/common';
|
|
180
|
+
|
|
181
|
+
// Validation
|
|
182
|
+
MimeTypeUtils.isAllowedMimeType('image/png', 'IMAGE'); // true
|
|
183
|
+
MimeTypeUtils.isAllowedMimeType('application/pdf', 'SAFE'); // true
|
|
184
|
+
|
|
185
|
+
// HTML input accept strings
|
|
186
|
+
MimeTypeUtils.getInputAccept('IMAGE');
|
|
187
|
+
// 'image/png,image/jpeg,image/webp,...'
|
|
188
|
+
|
|
189
|
+
// Type resolution
|
|
190
|
+
MimeTypeUtils.mimeTypeResolver('video/mp4'); // 'VIDEO'
|
|
191
|
+
MimeTypeUtils.mimeTypeResolver('application/pdf'); // 'DOCUMENT'
|
|
192
|
+
|
|
193
|
+
// Content disposition
|
|
194
|
+
MimeTypeUtils.isInlineContentDisposition('image/png'); // true
|
|
195
|
+
MimeTypeUtils.isInlineContentDisposition('application/zip'); // false
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
## API
|
|
199
|
+
|
|
200
|
+
### Status Codes
|
|
201
|
+
```typescript
|
|
202
|
+
import { statusCodes, type StatusCode } from '@md-oss/common';
|
|
203
|
+
|
|
204
|
+
// Use named status codes
|
|
205
|
+
statusCodes.OK; // 200
|
|
206
|
+
statusCodes.CREATED; // 201
|
|
207
|
+
statusCodes.BAD_REQUEST; // 400
|
|
208
|
+
statusCodes.UNAUTHORIZED; // 401
|
|
209
|
+
statusCodes.NOT_FOUND; // 404
|
|
210
|
+
statusCodes.INTERNAL_SERVER_ERROR; // 500
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
### API Errors
|
|
214
|
+
```typescript
|
|
215
|
+
import { APIError, isAPIError, parseError } from '@md-oss/common';
|
|
216
|
+
|
|
217
|
+
// Create API errors
|
|
218
|
+
const error = new APIError(404, {
|
|
219
|
+
code: 'NOT_FOUND',
|
|
220
|
+
message: 'Resource not found',
|
|
221
|
+
details: { id: '123' }
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
// Alternative constructor syntax
|
|
225
|
+
const error2 = new APIError({
|
|
226
|
+
status: 400,
|
|
227
|
+
code: 'VALIDATION_ERROR',
|
|
228
|
+
message: 'Invalid input',
|
|
229
|
+
headers: { 'X-Custom': 'value' }
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
// Type guards
|
|
233
|
+
if (isAPIError(error)) {
|
|
234
|
+
console.log(error.statusCode, error.body.code);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// Parse unknown errors into APIError
|
|
238
|
+
try {
|
|
239
|
+
await someOperation();
|
|
240
|
+
} catch (err) {
|
|
241
|
+
const apiError = parseError(err, 'OPERATION_FAILED', 'Operation failed');
|
|
242
|
+
throw apiError;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// Serialize to JSON
|
|
246
|
+
const json = error.toJSON();
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
### Request/Response Types
|
|
250
|
+
```typescript
|
|
251
|
+
import type {
|
|
252
|
+
MinimalRequest,
|
|
253
|
+
MinimalResponse,
|
|
254
|
+
MinimalNextFunction,
|
|
255
|
+
MinimalRequestHandler
|
|
256
|
+
} from '@md-oss/common';
|
|
257
|
+
|
|
258
|
+
// Framework-agnostic request handler types
|
|
259
|
+
const handler: MinimalRequestHandler = async (req, res, next) => {
|
|
260
|
+
const userIp = req.ip;
|
|
261
|
+
const params = req.params;
|
|
262
|
+
const query = req.query;
|
|
263
|
+
|
|
264
|
+
res.status(200).json({ success: true });
|
|
265
|
+
};
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
## Schemas
|
|
269
|
+
|
|
270
|
+
### Zod Utilities
|
|
271
|
+
```typescript
|
|
272
|
+
import {
|
|
273
|
+
isFieldRequired,
|
|
274
|
+
requiredKeys,
|
|
275
|
+
zodEnumFromObjectKeys,
|
|
276
|
+
paginationQuerySchema
|
|
277
|
+
} from '@md-oss/common';
|
|
278
|
+
import { z } from 'zod';
|
|
279
|
+
|
|
280
|
+
// Check if a field is required
|
|
281
|
+
const schema = z.object({
|
|
282
|
+
name: z.string(),
|
|
283
|
+
age: z.number().optional(),
|
|
284
|
+
});
|
|
285
|
+
isFieldRequired(schema, 'name'); // true
|
|
286
|
+
isFieldRequired(schema, 'age'); // false
|
|
287
|
+
|
|
288
|
+
// Get all required keys
|
|
289
|
+
const required = requiredKeys(schema);
|
|
290
|
+
// ['name']
|
|
291
|
+
|
|
292
|
+
// Create enum from object keys
|
|
293
|
+
const roles = { ADMIN: 1, USER: 2, GUEST: 3 };
|
|
294
|
+
const roleEnum = zodEnumFromObjectKeys(roles);
|
|
295
|
+
// z.enum(['ADMIN', 'USER', 'GUEST'])
|
|
296
|
+
|
|
297
|
+
// Pagination schema
|
|
298
|
+
const paginationSchema = paginationQuerySchema({
|
|
299
|
+
min: 1,
|
|
300
|
+
max: 100,
|
|
301
|
+
defaultLimit: 25
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
// Parse pagination query
|
|
305
|
+
const { limit, offset } = paginationSchema.parse({
|
|
306
|
+
limit: '50',
|
|
307
|
+
offset: '0'
|
|
308
|
+
});
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
### Signed URL Schema
|
|
312
|
+
```typescript
|
|
313
|
+
import { signedUrlSchema, type SignedUrlSchema } from '@md-oss/common';
|
|
314
|
+
|
|
315
|
+
// Validate signed URL parameters
|
|
316
|
+
const params = signedUrlSchema.parse({
|
|
317
|
+
expires: 1640995200,
|
|
318
|
+
sig: 'abc123',
|
|
319
|
+
ref: 'user-123'
|
|
320
|
+
});
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
### Pagination Type
|
|
324
|
+
```typescript
|
|
325
|
+
import type { Pagination } from '@md-oss/common';
|
|
326
|
+
|
|
327
|
+
const pagination: Pagination = {
|
|
328
|
+
totalItems: 100,
|
|
329
|
+
currentPage: 1,
|
|
330
|
+
itemsPerPage: 25,
|
|
331
|
+
totalPages: 4,
|
|
332
|
+
limit: 25,
|
|
333
|
+
offset: 0
|
|
334
|
+
};
|
|
335
|
+
```
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";var u=Object.defineProperty;var o=(s,e)=>u(s,"name",{value:e,configurable:!0});const{NODE_ENV:d}=process.env;class t extends Error{static{o(this,"APIError")}ok=!1;statusCode;headers;body;constructor(e,i,b){super(),typeof e=="object"?(this.statusCode=e.status,this.headers=e.headers??{},this.body={code:e.code,message:e.message,details:e.details}):(this.statusCode=e,this.headers=b??{},this.body=i||{code:"INTERNAL_SERVER_ERROR",message:"An error occurred, but no additional information is available."}),this.message=`[${this.body.code}]: ${this.body.message}`}toJSON(){return{ok:this.ok,statusCode:this.statusCode,body:this.body,headers:this.headers}}}const n=o(s=>s instanceof t?!0:a(s)?(s=new t(s.statusCode,{code:s.body.code,message:s.body.message,details:s.body.details}),!0):!1,"isAPIError"),a=o(s=>typeof s=="object"&&s!==null&&"ok"in s&&s.ok===!1&&"statusCode"in s&&typeof s.statusCode=="number"&&"body"in s&&typeof s.body=="object"&&s.body!==null&&"code"in s.body&&typeof s.body.code=="string"&&"message"in s.body&&typeof s.body.message=="string","isAPIErrorResponse"),c=o((s,e,i)=>n(s)?new t(s.statusCode,{code:e,message:s.body.message,details:s.body.details}):a(s)?new t(s.statusCode,{code:e,message:s.body.message,details:s.body.details}):s instanceof Error?new t(500,{code:e,message:i,details:d==="production"?void 0:{message:s.message,stack:s.stack}}):new t(500,{code:e,message:i,details:d==="production"?void 0:JSON.stringify(s,null,2)}),"parseError");exports.APIError=t,exports.isAPIError=n,exports.isAPIErrorResponse=a,exports.parseError=c;
|
|
2
|
+
//# sourceMappingURL=errors.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.cjs","sources":["../../src/api/errors.ts"],"sourcesContent":["import type { StatusCode } from './status-codes';\n\nconst { NODE_ENV } = process.env;\n\ntype APIErrorResponse = {\n\tok: false;\n\tstatusCode: StatusCode;\n\tbody: {\n\t\tcode: string;\n\t\tmessage: string;\n\t\tdetails?: string | Record<string, unknown>;\n\t};\n\theaders?: HeadersInit;\n};\n\ntype HeadersInit = Record<string, string> | Array<[string, string]> | Headers;\n\nclass APIError extends Error {\n\treadonly ok = false;\n\treadonly statusCode: StatusCode;\n\treadonly headers: HeadersInit;\n\treadonly body: {\n\t\tcode: string;\n\t\tmessage: string;\n\t\tdetails?: string | Record<string, unknown>;\n\t};\n\n\tconstructor(body: {\n\t\tstatus: StatusCode;\n\t\tcode: string;\n\t\tmessage: string;\n\t\tdetails?: string | Record<string, unknown>;\n\t\theaders?: HeadersInit;\n\t});\n\tconstructor(\n\t\tstatusCode: StatusCode,\n\t\tbody: {\n\t\t\tcode: string;\n\t\t\tmessage: string;\n\t\t\tdetails?: string | Record<string, unknown>;\n\t\t},\n\t\theaders?: HeadersInit\n\t);\n\tconstructor(\n\t\tstatusCodeOrBody:\n\t\t\t| StatusCode\n\t\t\t| {\n\t\t\t\t\tstatus: StatusCode;\n\t\t\t\t\tcode: string;\n\t\t\t\t\tmessage: string;\n\t\t\t\t\tdetails?: string | Record<string, unknown>;\n\t\t\t\t\theaders?: HeadersInit;\n\t\t\t },\n\t\tbody?: {\n\t\t\tcode: string;\n\t\t\tmessage: string;\n\t\t\tdetails?: string | Record<string, unknown>;\n\t\t},\n\t\theaders?: HeadersInit\n\t) {\n\t\tsuper();\n\n\t\tif (typeof statusCodeOrBody === 'object') {\n\t\t\t// Single argument signature\n\t\t\tthis.statusCode = statusCodeOrBody.status;\n\t\t\tthis.headers = statusCodeOrBody.headers ?? {};\n\t\t\tthis.body = {\n\t\t\t\tcode: statusCodeOrBody.code,\n\t\t\t\tmessage: statusCodeOrBody.message,\n\t\t\t\tdetails: statusCodeOrBody.details,\n\t\t\t};\n\t\t} else {\n\t\t\t// Multi-argument signature\n\t\t\tthis.statusCode = statusCodeOrBody;\n\t\t\tthis.headers = headers ?? {};\n\t\t\tthis.body = body || {\n\t\t\t\tcode: 'INTERNAL_SERVER_ERROR',\n\t\t\t\tmessage:\n\t\t\t\t\t'An error occurred, but no additional information is available.',\n\t\t\t};\n\t\t}\n\n\t\tthis.message = `[${this.body.code}]: ${this.body.message}`;\n\t}\n\n\ttoJSON(): APIErrorResponse {\n\t\treturn {\n\t\t\tok: this.ok,\n\t\t\tstatusCode: this.statusCode,\n\t\t\tbody: this.body,\n\t\t\theaders: this.headers,\n\t\t} satisfies APIErrorResponse;\n\t}\n}\n\n//\n// Start Guards\n//\n\ninterface APISuccessResponse<T> {\n\tok: true;\n\tcode: number;\n\tmessage: string | null;\n\tdata: T;\n}\n\nconst isAPIError = (r: unknown): r is APIError => {\n\tif (r instanceof APIError) {\n\t\treturn true;\n\t}\n\n\tif (isAPIErrorResponse(r)) {\n\t\tr = new APIError(r.statusCode, {\n\t\t\tcode: r.body.code,\n\t\t\tmessage: r.body.message,\n\t\t\tdetails: r.body.details,\n\t\t});\n\t\treturn true;\n\t}\n\n\treturn false;\n};\n\nconst isAPIErrorResponse = (r: unknown): r is APIErrorResponse => {\n\treturn (\n\t\ttypeof r === 'object' &&\n\t\tr !== null &&\n\t\t'ok' in r &&\n\t\tr.ok === false &&\n\t\t'statusCode' in r &&\n\t\ttypeof r.statusCode === 'number' &&\n\t\t'body' in r &&\n\t\ttypeof r.body === 'object' &&\n\t\tr.body !== null &&\n\t\t'code' in r.body &&\n\t\ttypeof r.body.code === 'string' &&\n\t\t'message' in r.body &&\n\t\ttypeof r.body.message === 'string'\n\t);\n};\n\n/**\n * Parses an unknown error into a standardized APIError. The provided code and message\n * will be used for the resulting APIError, while details from the original error\n * will be included when not in production mode.\n */\nconst parseError = (\n\terror: unknown,\n\tcode: string,\n\tmessage: string\n): APIError => {\n\tif (isAPIError(error)) {\n\t\treturn new APIError(error.statusCode, {\n\t\t\tcode,\n\t\t\tmessage: error.body.message,\n\t\t\tdetails: error.body.details,\n\t\t});\n\t}\n\n\tif (isAPIErrorResponse(error)) {\n\t\treturn new APIError(error.statusCode, {\n\t\t\tcode,\n\t\t\tmessage: error.body.message,\n\t\t\tdetails: error.body.details,\n\t\t});\n\t}\n\n\tif (error instanceof Error) {\n\t\treturn new APIError(500, {\n\t\t\tcode,\n\t\t\tmessage,\n\t\t\tdetails:\n\t\t\t\tNODE_ENV === 'production'\n\t\t\t\t\t? undefined\n\t\t\t\t\t: {\n\t\t\t\t\t\t\tmessage: error.message,\n\t\t\t\t\t\t\tstack: error.stack,\n\t\t\t\t\t\t},\n\t\t});\n\t}\n\n\treturn new APIError(500, {\n\t\tcode,\n\t\tmessage,\n\t\tdetails:\n\t\t\tNODE_ENV === 'production' ? undefined : JSON.stringify(error, null, 2),\n\t});\n};\n\nexport {\n\tAPIError,\n\ttype APIErrorResponse,\n\ttype APISuccessResponse,\n\tisAPIError,\n\tisAPIErrorResponse,\n\tparseError,\n};\n"],"names":["NODE_ENV","APIError","__name","statusCodeOrBody","body","headers","isAPIError","r","isAPIErrorResponse","parseError","error","code","message"],"mappings":"4FAEA,KAAM,CAAE,SAAAA,CAAA,EAAa,QAAQ,IAe7B,MAAMC,UAAiB,KAAM,OAAA,CAAAC,EAAA,iBACnB,GAAK,GACL,WACA,QACA,KAsBT,YACCC,EASAC,EAKAC,EACC,CACD,MAAA,EAEI,OAAOF,GAAqB,UAE/B,KAAK,WAAaA,EAAiB,OACnC,KAAK,QAAUA,EAAiB,SAAW,CAAA,EAC3C,KAAK,KAAO,CACX,KAAMA,EAAiB,KACvB,QAASA,EAAiB,QAC1B,QAASA,EAAiB,OAAA,IAI3B,KAAK,WAAaA,EAClB,KAAK,QAAUE,GAAW,CAAA,EAC1B,KAAK,KAAOD,GAAQ,CACnB,KAAM,wBACN,QACC,gEAAA,GAIH,KAAK,QAAU,IAAI,KAAK,KAAK,IAAI,MAAM,KAAK,KAAK,OAAO,EACzD,CAEA,QAA2B,CAC1B,MAAO,CACN,GAAI,KAAK,GACT,WAAY,KAAK,WACjB,KAAM,KAAK,KACX,QAAS,KAAK,OAAA,CAEhB,CACD,CAaA,MAAME,EAAaJ,EAACK,GACfA,aAAaN,EACT,GAGJO,EAAmBD,CAAC,GACvBA,EAAI,IAAIN,EAASM,EAAE,WAAY,CAC9B,KAAMA,EAAE,KAAK,KACb,QAASA,EAAE,KAAK,QAChB,QAASA,EAAE,KAAK,OAAA,CAChB,EACM,IAGD,GAdW,cAiBbC,EAAqBN,EAACK,GAE1B,OAAOA,GAAM,UACbA,IAAM,MACN,OAAQA,GACRA,EAAE,KAAO,IACT,eAAgBA,GAChB,OAAOA,EAAE,YAAe,UACxB,SAAUA,GACV,OAAOA,EAAE,MAAS,UAClBA,EAAE,OAAS,MACX,SAAUA,EAAE,MACZ,OAAOA,EAAE,KAAK,MAAS,UACvB,YAAaA,EAAE,MACf,OAAOA,EAAE,KAAK,SAAY,SAdD,sBAuBrBE,EAAaP,EAAA,CAClBQ,EACAC,EACAC,IAEIN,EAAWI,CAAK,EACZ,IAAIT,EAASS,EAAM,WAAY,CACrC,KAAAC,EACA,QAASD,EAAM,KAAK,QACpB,QAASA,EAAM,KAAK,OAAA,CACpB,EAGEF,EAAmBE,CAAK,EACpB,IAAIT,EAASS,EAAM,WAAY,CACrC,KAAAC,EACA,QAASD,EAAM,KAAK,QACpB,QAASA,EAAM,KAAK,OAAA,CACpB,EAGEA,aAAiB,MACb,IAAIT,EAAS,IAAK,CACxB,KAAAU,EACA,QAAAC,EACA,QACCZ,IAAa,aACV,OACA,CACA,QAASU,EAAM,QACf,MAAOA,EAAM,KAAA,CACd,CACH,EAGK,IAAIT,EAAS,IAAK,CACxB,KAAAU,EACA,QAAAC,EACA,QACCZ,IAAa,aAAe,OAAY,KAAK,UAAUU,EAAO,KAAM,CAAC,CAAA,CACtE,EAxCiB"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { StatusCode } from './status-codes.cjs';
|
|
2
|
+
|
|
3
|
+
type APIErrorResponse = {
|
|
4
|
+
ok: false;
|
|
5
|
+
statusCode: StatusCode;
|
|
6
|
+
body: {
|
|
7
|
+
code: string;
|
|
8
|
+
message: string;
|
|
9
|
+
details?: string | Record<string, unknown>;
|
|
10
|
+
};
|
|
11
|
+
headers?: HeadersInit;
|
|
12
|
+
};
|
|
13
|
+
type HeadersInit = Record<string, string> | Array<[string, string]> | Headers;
|
|
14
|
+
declare class APIError extends Error {
|
|
15
|
+
readonly ok = false;
|
|
16
|
+
readonly statusCode: StatusCode;
|
|
17
|
+
readonly headers: HeadersInit;
|
|
18
|
+
readonly body: {
|
|
19
|
+
code: string;
|
|
20
|
+
message: string;
|
|
21
|
+
details?: string | Record<string, unknown>;
|
|
22
|
+
};
|
|
23
|
+
constructor(body: {
|
|
24
|
+
status: StatusCode;
|
|
25
|
+
code: string;
|
|
26
|
+
message: string;
|
|
27
|
+
details?: string | Record<string, unknown>;
|
|
28
|
+
headers?: HeadersInit;
|
|
29
|
+
});
|
|
30
|
+
constructor(statusCode: StatusCode, body: {
|
|
31
|
+
code: string;
|
|
32
|
+
message: string;
|
|
33
|
+
details?: string | Record<string, unknown>;
|
|
34
|
+
}, headers?: HeadersInit);
|
|
35
|
+
toJSON(): APIErrorResponse;
|
|
36
|
+
}
|
|
37
|
+
interface APISuccessResponse<T> {
|
|
38
|
+
ok: true;
|
|
39
|
+
code: number;
|
|
40
|
+
message: string | null;
|
|
41
|
+
data: T;
|
|
42
|
+
}
|
|
43
|
+
declare const isAPIError: (r: unknown) => r is APIError;
|
|
44
|
+
declare const isAPIErrorResponse: (r: unknown) => r is APIErrorResponse;
|
|
45
|
+
/**
|
|
46
|
+
* Parses an unknown error into a standardized APIError. The provided code and message
|
|
47
|
+
* will be used for the resulting APIError, while details from the original error
|
|
48
|
+
* will be included when not in production mode.
|
|
49
|
+
*/
|
|
50
|
+
declare const parseError: (error: unknown, code: string, message: string) => APIError;
|
|
51
|
+
|
|
52
|
+
export { APIError, isAPIError, isAPIErrorResponse, parseError };
|
|
53
|
+
export type { APIErrorResponse, APISuccessResponse };
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { StatusCode } from './status-codes.mjs';
|
|
2
|
+
|
|
3
|
+
type APIErrorResponse = {
|
|
4
|
+
ok: false;
|
|
5
|
+
statusCode: StatusCode;
|
|
6
|
+
body: {
|
|
7
|
+
code: string;
|
|
8
|
+
message: string;
|
|
9
|
+
details?: string | Record<string, unknown>;
|
|
10
|
+
};
|
|
11
|
+
headers?: HeadersInit;
|
|
12
|
+
};
|
|
13
|
+
type HeadersInit = Record<string, string> | Array<[string, string]> | Headers;
|
|
14
|
+
declare class APIError extends Error {
|
|
15
|
+
readonly ok = false;
|
|
16
|
+
readonly statusCode: StatusCode;
|
|
17
|
+
readonly headers: HeadersInit;
|
|
18
|
+
readonly body: {
|
|
19
|
+
code: string;
|
|
20
|
+
message: string;
|
|
21
|
+
details?: string | Record<string, unknown>;
|
|
22
|
+
};
|
|
23
|
+
constructor(body: {
|
|
24
|
+
status: StatusCode;
|
|
25
|
+
code: string;
|
|
26
|
+
message: string;
|
|
27
|
+
details?: string | Record<string, unknown>;
|
|
28
|
+
headers?: HeadersInit;
|
|
29
|
+
});
|
|
30
|
+
constructor(statusCode: StatusCode, body: {
|
|
31
|
+
code: string;
|
|
32
|
+
message: string;
|
|
33
|
+
details?: string | Record<string, unknown>;
|
|
34
|
+
}, headers?: HeadersInit);
|
|
35
|
+
toJSON(): APIErrorResponse;
|
|
36
|
+
}
|
|
37
|
+
interface APISuccessResponse<T> {
|
|
38
|
+
ok: true;
|
|
39
|
+
code: number;
|
|
40
|
+
message: string | null;
|
|
41
|
+
data: T;
|
|
42
|
+
}
|
|
43
|
+
declare const isAPIError: (r: unknown) => r is APIError;
|
|
44
|
+
declare const isAPIErrorResponse: (r: unknown) => r is APIErrorResponse;
|
|
45
|
+
/**
|
|
46
|
+
* Parses an unknown error into a standardized APIError. The provided code and message
|
|
47
|
+
* will be used for the resulting APIError, while details from the original error
|
|
48
|
+
* will be included when not in production mode.
|
|
49
|
+
*/
|
|
50
|
+
declare const parseError: (error: unknown, code: string, message: string) => APIError;
|
|
51
|
+
|
|
52
|
+
export { APIError, isAPIError, isAPIErrorResponse, parseError };
|
|
53
|
+
export type { APIErrorResponse, APISuccessResponse };
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
var u=Object.defineProperty;var o=(s,e)=>u(s,"name",{value:e,configurable:!0});const{NODE_ENV:d}=process.env;class t extends Error{static{o(this,"APIError")}ok=!1;statusCode;headers;body;constructor(e,i,b){super(),typeof e=="object"?(this.statusCode=e.status,this.headers=e.headers??{},this.body={code:e.code,message:e.message,details:e.details}):(this.statusCode=e,this.headers=b??{},this.body=i||{code:"INTERNAL_SERVER_ERROR",message:"An error occurred, but no additional information is available."}),this.message=`[${this.body.code}]: ${this.body.message}`}toJSON(){return{ok:this.ok,statusCode:this.statusCode,body:this.body,headers:this.headers}}}const n=o(s=>s instanceof t?!0:a(s)?(s=new t(s.statusCode,{code:s.body.code,message:s.body.message,details:s.body.details}),!0):!1,"isAPIError"),a=o(s=>typeof s=="object"&&s!==null&&"ok"in s&&s.ok===!1&&"statusCode"in s&&typeof s.statusCode=="number"&&"body"in s&&typeof s.body=="object"&&s.body!==null&&"code"in s.body&&typeof s.body.code=="string"&&"message"in s.body&&typeof s.body.message=="string","isAPIErrorResponse"),c=o((s,e,i)=>n(s)?new t(s.statusCode,{code:e,message:s.body.message,details:s.body.details}):a(s)?new t(s.statusCode,{code:e,message:s.body.message,details:s.body.details}):s instanceof Error?new t(500,{code:e,message:i,details:d==="production"?void 0:{message:s.message,stack:s.stack}}):new t(500,{code:e,message:i,details:d==="production"?void 0:JSON.stringify(s,null,2)}),"parseError");export{t as APIError,n as isAPIError,a as isAPIErrorResponse,c as parseError};
|
|
2
|
+
//# sourceMappingURL=errors.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.mjs","sources":["../../src/api/errors.ts"],"sourcesContent":["import type { StatusCode } from './status-codes';\n\nconst { NODE_ENV } = process.env;\n\ntype APIErrorResponse = {\n\tok: false;\n\tstatusCode: StatusCode;\n\tbody: {\n\t\tcode: string;\n\t\tmessage: string;\n\t\tdetails?: string | Record<string, unknown>;\n\t};\n\theaders?: HeadersInit;\n};\n\ntype HeadersInit = Record<string, string> | Array<[string, string]> | Headers;\n\nclass APIError extends Error {\n\treadonly ok = false;\n\treadonly statusCode: StatusCode;\n\treadonly headers: HeadersInit;\n\treadonly body: {\n\t\tcode: string;\n\t\tmessage: string;\n\t\tdetails?: string | Record<string, unknown>;\n\t};\n\n\tconstructor(body: {\n\t\tstatus: StatusCode;\n\t\tcode: string;\n\t\tmessage: string;\n\t\tdetails?: string | Record<string, unknown>;\n\t\theaders?: HeadersInit;\n\t});\n\tconstructor(\n\t\tstatusCode: StatusCode,\n\t\tbody: {\n\t\t\tcode: string;\n\t\t\tmessage: string;\n\t\t\tdetails?: string | Record<string, unknown>;\n\t\t},\n\t\theaders?: HeadersInit\n\t);\n\tconstructor(\n\t\tstatusCodeOrBody:\n\t\t\t| StatusCode\n\t\t\t| {\n\t\t\t\t\tstatus: StatusCode;\n\t\t\t\t\tcode: string;\n\t\t\t\t\tmessage: string;\n\t\t\t\t\tdetails?: string | Record<string, unknown>;\n\t\t\t\t\theaders?: HeadersInit;\n\t\t\t },\n\t\tbody?: {\n\t\t\tcode: string;\n\t\t\tmessage: string;\n\t\t\tdetails?: string | Record<string, unknown>;\n\t\t},\n\t\theaders?: HeadersInit\n\t) {\n\t\tsuper();\n\n\t\tif (typeof statusCodeOrBody === 'object') {\n\t\t\t// Single argument signature\n\t\t\tthis.statusCode = statusCodeOrBody.status;\n\t\t\tthis.headers = statusCodeOrBody.headers ?? {};\n\t\t\tthis.body = {\n\t\t\t\tcode: statusCodeOrBody.code,\n\t\t\t\tmessage: statusCodeOrBody.message,\n\t\t\t\tdetails: statusCodeOrBody.details,\n\t\t\t};\n\t\t} else {\n\t\t\t// Multi-argument signature\n\t\t\tthis.statusCode = statusCodeOrBody;\n\t\t\tthis.headers = headers ?? {};\n\t\t\tthis.body = body || {\n\t\t\t\tcode: 'INTERNAL_SERVER_ERROR',\n\t\t\t\tmessage:\n\t\t\t\t\t'An error occurred, but no additional information is available.',\n\t\t\t};\n\t\t}\n\n\t\tthis.message = `[${this.body.code}]: ${this.body.message}`;\n\t}\n\n\ttoJSON(): APIErrorResponse {\n\t\treturn {\n\t\t\tok: this.ok,\n\t\t\tstatusCode: this.statusCode,\n\t\t\tbody: this.body,\n\t\t\theaders: this.headers,\n\t\t} satisfies APIErrorResponse;\n\t}\n}\n\n//\n// Start Guards\n//\n\ninterface APISuccessResponse<T> {\n\tok: true;\n\tcode: number;\n\tmessage: string | null;\n\tdata: T;\n}\n\nconst isAPIError = (r: unknown): r is APIError => {\n\tif (r instanceof APIError) {\n\t\treturn true;\n\t}\n\n\tif (isAPIErrorResponse(r)) {\n\t\tr = new APIError(r.statusCode, {\n\t\t\tcode: r.body.code,\n\t\t\tmessage: r.body.message,\n\t\t\tdetails: r.body.details,\n\t\t});\n\t\treturn true;\n\t}\n\n\treturn false;\n};\n\nconst isAPIErrorResponse = (r: unknown): r is APIErrorResponse => {\n\treturn (\n\t\ttypeof r === 'object' &&\n\t\tr !== null &&\n\t\t'ok' in r &&\n\t\tr.ok === false &&\n\t\t'statusCode' in r &&\n\t\ttypeof r.statusCode === 'number' &&\n\t\t'body' in r &&\n\t\ttypeof r.body === 'object' &&\n\t\tr.body !== null &&\n\t\t'code' in r.body &&\n\t\ttypeof r.body.code === 'string' &&\n\t\t'message' in r.body &&\n\t\ttypeof r.body.message === 'string'\n\t);\n};\n\n/**\n * Parses an unknown error into a standardized APIError. The provided code and message\n * will be used for the resulting APIError, while details from the original error\n * will be included when not in production mode.\n */\nconst parseError = (\n\terror: unknown,\n\tcode: string,\n\tmessage: string\n): APIError => {\n\tif (isAPIError(error)) {\n\t\treturn new APIError(error.statusCode, {\n\t\t\tcode,\n\t\t\tmessage: error.body.message,\n\t\t\tdetails: error.body.details,\n\t\t});\n\t}\n\n\tif (isAPIErrorResponse(error)) {\n\t\treturn new APIError(error.statusCode, {\n\t\t\tcode,\n\t\t\tmessage: error.body.message,\n\t\t\tdetails: error.body.details,\n\t\t});\n\t}\n\n\tif (error instanceof Error) {\n\t\treturn new APIError(500, {\n\t\t\tcode,\n\t\t\tmessage,\n\t\t\tdetails:\n\t\t\t\tNODE_ENV === 'production'\n\t\t\t\t\t? undefined\n\t\t\t\t\t: {\n\t\t\t\t\t\t\tmessage: error.message,\n\t\t\t\t\t\t\tstack: error.stack,\n\t\t\t\t\t\t},\n\t\t});\n\t}\n\n\treturn new APIError(500, {\n\t\tcode,\n\t\tmessage,\n\t\tdetails:\n\t\t\tNODE_ENV === 'production' ? undefined : JSON.stringify(error, null, 2),\n\t});\n};\n\nexport {\n\tAPIError,\n\ttype APIErrorResponse,\n\ttype APISuccessResponse,\n\tisAPIError,\n\tisAPIErrorResponse,\n\tparseError,\n};\n"],"names":["NODE_ENV","APIError","__name","statusCodeOrBody","body","headers","isAPIError","r","isAPIErrorResponse","parseError","error","code","message"],"mappings":"+EAEA,KAAM,CAAE,SAAAA,CAAA,EAAa,QAAQ,IAe7B,MAAMC,UAAiB,KAAM,CAf7B,MAe6B,CAAAC,EAAA,iBACnB,GAAK,GACL,WACA,QACA,KAsBT,YACCC,EASAC,EAKAC,EACC,CACD,MAAA,EAEI,OAAOF,GAAqB,UAE/B,KAAK,WAAaA,EAAiB,OACnC,KAAK,QAAUA,EAAiB,SAAW,CAAA,EAC3C,KAAK,KAAO,CACX,KAAMA,EAAiB,KACvB,QAASA,EAAiB,QAC1B,QAASA,EAAiB,OAAA,IAI3B,KAAK,WAAaA,EAClB,KAAK,QAAUE,GAAW,CAAA,EAC1B,KAAK,KAAOD,GAAQ,CACnB,KAAM,wBACN,QACC,gEAAA,GAIH,KAAK,QAAU,IAAI,KAAK,KAAK,IAAI,MAAM,KAAK,KAAK,OAAO,EACzD,CAEA,QAA2B,CAC1B,MAAO,CACN,GAAI,KAAK,GACT,WAAY,KAAK,WACjB,KAAM,KAAK,KACX,QAAS,KAAK,OAAA,CAEhB,CACD,CAaA,MAAME,EAAaJ,EAACK,GACfA,aAAaN,EACT,GAGJO,EAAmBD,CAAC,GACvBA,EAAI,IAAIN,EAASM,EAAE,WAAY,CAC9B,KAAMA,EAAE,KAAK,KACb,QAASA,EAAE,KAAK,QAChB,QAASA,EAAE,KAAK,OAAA,CAChB,EACM,IAGD,GAdW,cAiBbC,EAAqBN,EAACK,GAE1B,OAAOA,GAAM,UACbA,IAAM,MACN,OAAQA,GACRA,EAAE,KAAO,IACT,eAAgBA,GAChB,OAAOA,EAAE,YAAe,UACxB,SAAUA,GACV,OAAOA,EAAE,MAAS,UAClBA,EAAE,OAAS,MACX,SAAUA,EAAE,MACZ,OAAOA,EAAE,KAAK,MAAS,UACvB,YAAaA,EAAE,MACf,OAAOA,EAAE,KAAK,SAAY,SAdD,sBAuBrBE,EAAaP,EAAA,CAClBQ,EACAC,EACAC,IAEIN,EAAWI,CAAK,EACZ,IAAIT,EAASS,EAAM,WAAY,CACrC,KAAAC,EACA,QAASD,EAAM,KAAK,QACpB,QAASA,EAAM,KAAK,OAAA,CACpB,EAGEF,EAAmBE,CAAK,EACpB,IAAIT,EAASS,EAAM,WAAY,CACrC,KAAAC,EACA,QAASD,EAAM,KAAK,QACpB,QAASA,EAAM,KAAK,OAAA,CACpB,EAGEA,aAAiB,MACb,IAAIT,EAAS,IAAK,CACxB,KAAAU,EACA,QAAAC,EACA,QACCZ,IAAa,aACV,OACA,CACA,QAASU,EAAM,QACf,MAAOA,EAAM,KAAA,CACd,CACH,EAGK,IAAIT,EAAS,IAAK,CACxB,KAAAU,EACA,QAAAC,EACA,QACCZ,IAAa,aAAe,OAAY,KAAK,UAAUU,EAAO,KAAM,CAAC,CAAA,CACtE,EAxCiB"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";var r=require("./errors.cjs"),s=require("./status-codes.cjs");exports.APIError=r.APIError,exports.isAPIError=r.isAPIError,exports.isAPIErrorResponse=r.isAPIErrorResponse,exports.parseError=r.parseError,exports.isStatusCodeText=s.isStatusCodeText,exports.resolveStatusCode=s.resolveStatusCode,exports.statusCodes=s.statusCodes;
|
|
2
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":""}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { APIError, APIErrorResponse, APISuccessResponse, isAPIError, isAPIErrorResponse, parseError } from './errors.cjs';
|
|
2
|
+
export { MinimalNextFunction, MinimalRequest, MinimalRequestHandler, MinimalResponse, Params, ParamsDictionary, ParamsFlatDictionary } from './requests.cjs';
|
|
3
|
+
export { StatusCode, StatusCodeText, isStatusCodeText, resolveStatusCode, statusCodes } from './status-codes.cjs';
|
|
4
|
+
import 'qs';
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { APIError, APIErrorResponse, APISuccessResponse, isAPIError, isAPIErrorResponse, parseError } from './errors.mjs';
|
|
2
|
+
export { MinimalNextFunction, MinimalRequest, MinimalRequestHandler, MinimalResponse, Params, ParamsDictionary, ParamsFlatDictionary } from './requests.mjs';
|
|
3
|
+
export { StatusCode, StatusCodeText, isStatusCodeText, resolveStatusCode, statusCodes } from './status-codes.mjs';
|
|
4
|
+
import 'qs';
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{APIError as s,isAPIError as e,isAPIErrorResponse as t,parseError as a}from"./errors.mjs";import{isStatusCodeText as E,resolveStatusCode as d,statusCodes as i}from"./status-codes.mjs";export{s as APIError,e as isAPIError,t as isAPIErrorResponse,E as isStatusCodeText,a as parseError,d as resolveStatusCode,i as statusCodes};
|
|
2
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.mjs","sources":[],"sourcesContent":[],"names":[],"mappings":""}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { ParsedQs } from 'qs';
|
|
2
|
+
|
|
3
|
+
interface ParamsDictionary {
|
|
4
|
+
[key: string]: string | string[];
|
|
5
|
+
[key: number]: string;
|
|
6
|
+
}
|
|
7
|
+
interface ParamsFlatDictionary {
|
|
8
|
+
[key: string | number]: string;
|
|
9
|
+
}
|
|
10
|
+
type Params = Record<string, string | string[]> | ParamsDictionary | ParamsFlatDictionary;
|
|
11
|
+
interface MinimalRequest {
|
|
12
|
+
protocol: string;
|
|
13
|
+
headers: Record<string, string | string[] | undefined>;
|
|
14
|
+
get(header: string): string | undefined;
|
|
15
|
+
originalUrl?: string;
|
|
16
|
+
params?: Params;
|
|
17
|
+
query?: string | ParsedQs | (string | ParsedQs)[] | undefined;
|
|
18
|
+
body?: Record<string, unknown>;
|
|
19
|
+
ip: string | undefined;
|
|
20
|
+
connection?: {
|
|
21
|
+
remoteAddress?: string;
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
interface MinimalResponse {
|
|
25
|
+
locals: Record<string, unknown>;
|
|
26
|
+
statusCode: number;
|
|
27
|
+
setHeader(name: string, value: string): void;
|
|
28
|
+
status(code: number): MinimalResponse;
|
|
29
|
+
send(...args: unknown[]): void;
|
|
30
|
+
json(data: unknown): void;
|
|
31
|
+
end(...args: unknown[]): void;
|
|
32
|
+
}
|
|
33
|
+
type MinimalNextFunction = (err?: unknown) => void;
|
|
34
|
+
type MinimalRequestHandler = (req: MinimalRequest, res: MinimalResponse, next: MinimalNextFunction) => Promise<void> | void;
|
|
35
|
+
|
|
36
|
+
export type { MinimalNextFunction, MinimalRequest, MinimalRequestHandler, MinimalResponse, Params, ParamsDictionary, ParamsFlatDictionary };
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { ParsedQs } from 'qs';
|
|
2
|
+
|
|
3
|
+
interface ParamsDictionary {
|
|
4
|
+
[key: string]: string | string[];
|
|
5
|
+
[key: number]: string;
|
|
6
|
+
}
|
|
7
|
+
interface ParamsFlatDictionary {
|
|
8
|
+
[key: string | number]: string;
|
|
9
|
+
}
|
|
10
|
+
type Params = Record<string, string | string[]> | ParamsDictionary | ParamsFlatDictionary;
|
|
11
|
+
interface MinimalRequest {
|
|
12
|
+
protocol: string;
|
|
13
|
+
headers: Record<string, string | string[] | undefined>;
|
|
14
|
+
get(header: string): string | undefined;
|
|
15
|
+
originalUrl?: string;
|
|
16
|
+
params?: Params;
|
|
17
|
+
query?: string | ParsedQs | (string | ParsedQs)[] | undefined;
|
|
18
|
+
body?: Record<string, unknown>;
|
|
19
|
+
ip: string | undefined;
|
|
20
|
+
connection?: {
|
|
21
|
+
remoteAddress?: string;
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
interface MinimalResponse {
|
|
25
|
+
locals: Record<string, unknown>;
|
|
26
|
+
statusCode: number;
|
|
27
|
+
setHeader(name: string, value: string): void;
|
|
28
|
+
status(code: number): MinimalResponse;
|
|
29
|
+
send(...args: unknown[]): void;
|
|
30
|
+
json(data: unknown): void;
|
|
31
|
+
end(...args: unknown[]): void;
|
|
32
|
+
}
|
|
33
|
+
type MinimalNextFunction = (err?: unknown) => void;
|
|
34
|
+
type MinimalRequestHandler = (req: MinimalRequest, res: MinimalResponse, next: MinimalNextFunction) => Promise<void> | void;
|
|
35
|
+
|
|
36
|
+
export type { MinimalNextFunction, MinimalRequest, MinimalRequestHandler, MinimalResponse, Params, ParamsDictionary, ParamsFlatDictionary };
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";var _=Object.defineProperty;var O=(E,R)=>_(E,"name",{value:R,configurable:!0});const T={CONTINUE:100,SWITCHING_PROTOCOLS:101,PROCESSING:102,EARLY_HINTS:103,OK:200,CREATED:201,ACCEPTED:202,NON_AUTHORITATIVE_INFORMATION:203,NO_CONTENT:204,RESET_CONTENT:205,PARTIAL_CONTENT:206,MULTI_STATUS:207,ALREADY_REPORTED:208,IM_USED:226,MULTIPLE_CHOICES:300,MOVED_PERMANENTLY:301,FOUND:302,SEE_OTHER:303,NOT_MODIFIED:304,USE_PROXY:305,UNUSED:306,TEMPORARY_REDIRECT:307,PERMANENT_REDIRECT:308,BAD_REQUEST:400,UNAUTHORIZED:401,PAYMENT_REQUIRED:402,FORBIDDEN:403,NOT_FOUND:404,METHOD_NOT_ALLOWED:405,NOT_ACCEPTABLE:406,PROXY_AUTHENTICATION_REQUIRED:407,REQUEST_TIMEOUT:408,CONFLICT:409,GONE:410,LENGTH_REQUIRED:411,PRECONDITION_FAILED:412,PAYLOAD_TOO_LARGE:413,URI_TOO_LONG:414,UNSUPPORTED_MEDIA_TYPE:415,RANGE_NOT_SATISFIABLE:416,EXPECTATION_FAILED:417,"I'M_A_TEAPOT":418,MISDIRECTED_REQUEST:421,UNPROCESSABLE_ENTITY:422,LOCKED:423,FAILED_DEPENDENCY:424,TOO_EARLY:425,UPGRADE_REQUIRED:426,PRECONDITION_REQUIRED:428,TOO_MANY_REQUESTS:429,REQUEST_HEADER_FIELDS_TOO_LARGE:431,UNAVAILABLE_FOR_LEGAL_REASONS:451,INTERNAL_SERVER_ERROR:500,NOT_IMPLEMENTED:501,BAD_GATEWAY:502,SERVICE_UNAVAILABLE:503,GATEWAY_TIMEOUT:504,HTTP_VERSION_NOT_SUPPORTED:505,VARIANT_ALSO_NEGOTIATES:506,INSUFFICIENT_STORAGE:507,LOOP_DETECTED:508,NOT_EXTENDED:510,NETWORK_AUTHENTICATION_REQUIRED:511},N=O(E=>typeof E=="string"&&E in T,"isStatusCodeText"),A=O(E=>typeof E=="number"?E:T[E]??T.INTERNAL_SERVER_ERROR,"resolveStatusCode");exports.isStatusCodeText=N,exports.resolveStatusCode=A,exports.statusCodes=T;
|
|
2
|
+
//# sourceMappingURL=status-codes.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"status-codes.cjs","sources":["../../src/api/status-codes.ts"],"sourcesContent":["const statusCodes = {\n\t// 1xx Informational\n\tCONTINUE: 100,\n\tSWITCHING_PROTOCOLS: 101,\n\tPROCESSING: 102,\n\tEARLY_HINTS: 103,\n\n\t// 2xx Success\n\tOK: 200,\n\tCREATED: 201,\n\tACCEPTED: 202,\n\tNON_AUTHORITATIVE_INFORMATION: 203,\n\tNO_CONTENT: 204,\n\tRESET_CONTENT: 205,\n\tPARTIAL_CONTENT: 206,\n\tMULTI_STATUS: 207,\n\tALREADY_REPORTED: 208,\n\tIM_USED: 226,\n\n\t// 3xx Redirection\n\tMULTIPLE_CHOICES: 300,\n\tMOVED_PERMANENTLY: 301,\n\tFOUND: 302,\n\tSEE_OTHER: 303,\n\tNOT_MODIFIED: 304,\n\tUSE_PROXY: 305,\n\tUNUSED: 306,\n\tTEMPORARY_REDIRECT: 307,\n\tPERMANENT_REDIRECT: 308,\n\n\t// 4xx Client Errors\n\tBAD_REQUEST: 400,\n\tUNAUTHORIZED: 401,\n\tPAYMENT_REQUIRED: 402,\n\tFORBIDDEN: 403,\n\tNOT_FOUND: 404,\n\tMETHOD_NOT_ALLOWED: 405,\n\tNOT_ACCEPTABLE: 406,\n\tPROXY_AUTHENTICATION_REQUIRED: 407,\n\tREQUEST_TIMEOUT: 408,\n\tCONFLICT: 409,\n\tGONE: 410,\n\tLENGTH_REQUIRED: 411,\n\tPRECONDITION_FAILED: 412,\n\tPAYLOAD_TOO_LARGE: 413,\n\tURI_TOO_LONG: 414,\n\tUNSUPPORTED_MEDIA_TYPE: 415,\n\tRANGE_NOT_SATISFIABLE: 416,\n\tEXPECTATION_FAILED: 417,\n\t\"I'M_A_TEAPOT\": 418,\n\tMISDIRECTED_REQUEST: 421,\n\tUNPROCESSABLE_ENTITY: 422,\n\tLOCKED: 423,\n\tFAILED_DEPENDENCY: 424,\n\tTOO_EARLY: 425,\n\tUPGRADE_REQUIRED: 426,\n\tPRECONDITION_REQUIRED: 428,\n\tTOO_MANY_REQUESTS: 429,\n\tREQUEST_HEADER_FIELDS_TOO_LARGE: 431,\n\tUNAVAILABLE_FOR_LEGAL_REASONS: 451,\n\n\t// 5xx Server Errors\n\tINTERNAL_SERVER_ERROR: 500,\n\tNOT_IMPLEMENTED: 501,\n\tBAD_GATEWAY: 502,\n\tSERVICE_UNAVAILABLE: 503,\n\tGATEWAY_TIMEOUT: 504,\n\tHTTP_VERSION_NOT_SUPPORTED: 505,\n\tVARIANT_ALSO_NEGOTIATES: 506,\n\tINSUFFICIENT_STORAGE: 507,\n\tLOOP_DETECTED: 508,\n\tNOT_EXTENDED: 510,\n\tNETWORK_AUTHENTICATION_REQUIRED: 511,\n} as const;\n\ntype StatusCodeText = keyof typeof statusCodes;\ntype StatusCode = (typeof statusCodes)[StatusCodeText];\n\nconst isStatusCodeText = (value: unknown): value is StatusCodeText => {\n\treturn typeof value === 'string' && value in statusCodes;\n};\n\nconst resolveStatusCode = (status: StatusCodeText | StatusCode): StatusCode => {\n\tif (typeof status === 'number') {\n\t\treturn status;\n\t}\n\n\treturn statusCodes[status] ?? statusCodes.INTERNAL_SERVER_ERROR;\n};\n\nexport {\n\tstatusCodes,\n\ttype StatusCode,\n\ttype StatusCodeText,\n\tisStatusCodeText,\n\tresolveStatusCode,\n};\n"],"names":["statusCodes","isStatusCodeText","__name","value","resolveStatusCode","status"],"mappings":"4FAAA,MAAMA,EAAc,CAEnB,SAAU,IACV,oBAAqB,IACrB,WAAY,IACZ,YAAa,IAGb,GAAI,IACJ,QAAS,IACT,SAAU,IACV,8BAA+B,IAC/B,WAAY,IACZ,cAAe,IACf,gBAAiB,IACjB,aAAc,IACd,iBAAkB,IAClB,QAAS,IAGT,iBAAkB,IAClB,kBAAmB,IACnB,MAAO,IACP,UAAW,IACX,aAAc,IACd,UAAW,IACX,OAAQ,IACR,mBAAoB,IACpB,mBAAoB,IAGpB,YAAa,IACb,aAAc,IACd,iBAAkB,IAClB,UAAW,IACX,UAAW,IACX,mBAAoB,IACpB,eAAgB,IAChB,8BAA+B,IAC/B,gBAAiB,IACjB,SAAU,IACV,KAAM,IACN,gBAAiB,IACjB,oBAAqB,IACrB,kBAAmB,IACnB,aAAc,IACd,uBAAwB,IACxB,sBAAuB,IACvB,mBAAoB,IACpB,eAAgB,IAChB,oBAAqB,IACrB,qBAAsB,IACtB,OAAQ,IACR,kBAAmB,IACnB,UAAW,IACX,iBAAkB,IAClB,sBAAuB,IACvB,kBAAmB,IACnB,gCAAiC,IACjC,8BAA+B,IAG/B,sBAAuB,IACvB,gBAAiB,IACjB,YAAa,IACb,oBAAqB,IACrB,gBAAiB,IACjB,2BAA4B,IAC5B,wBAAyB,IACzB,qBAAsB,IACtB,cAAe,IACf,aAAc,IACd,gCAAiC,GAClC,EAKMC,EAAmBC,EAACC,GAClB,OAAOA,GAAU,UAAYA,KAASH,EADrB,oBAInBI,EAAoBF,EAACG,GACtB,OAAOA,GAAW,SACdA,EAGDL,EAAYK,CAAM,GAAKL,EAAY,sBALjB"}
|