cpeak 2.7.0 → 2.9.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/README.md +120 -72
- package/dist/index.d.ts +59 -48
- package/dist/index.js +484 -174
- package/dist/index.js.map +1 -1
- package/lib/index.ts +132 -121
- package/lib/internal/errors.ts +51 -0
- package/lib/internal/mimeTypes.ts +31 -0
- package/lib/internal/router.ts +259 -0
- package/lib/types.ts +29 -25
- package/lib/utils/render.ts +142 -59
- package/lib/utils/serveStatic.ts +35 -27
- package/package.json +1 -1
package/lib/index.ts
CHANGED
|
@@ -2,6 +2,8 @@ import http from "node:http";
|
|
|
2
2
|
import fs from "node:fs/promises";
|
|
3
3
|
import { createReadStream } from "node:fs";
|
|
4
4
|
import { pipeline } from "node:stream/promises";
|
|
5
|
+
|
|
6
|
+
import type net from "node:net";
|
|
5
7
|
import type { Readable } from "node:stream";
|
|
6
8
|
import type { Buffer } from "node:buffer";
|
|
7
9
|
|
|
@@ -9,52 +11,29 @@ import {
|
|
|
9
11
|
resolveCompressionOptions,
|
|
10
12
|
compressAndSend
|
|
11
13
|
} from "./internal/compression";
|
|
14
|
+
import { MIME_TYPES } from "./internal/mimeTypes";
|
|
15
|
+
import { Router } from "./internal/router";
|
|
16
|
+
import {
|
|
17
|
+
frameworkError,
|
|
18
|
+
ErrorCode,
|
|
19
|
+
isClientDisconnect
|
|
20
|
+
} from "./internal/errors";
|
|
21
|
+
|
|
22
|
+
export { frameworkError, ErrorCode, isClientDisconnect };
|
|
12
23
|
|
|
13
24
|
import type {
|
|
14
25
|
StringMap,
|
|
26
|
+
CpeakHttpServer,
|
|
15
27
|
CpeakOptions,
|
|
16
28
|
CpeakRequest,
|
|
17
29
|
CpeakResponse,
|
|
18
30
|
Middleware,
|
|
19
31
|
RouteMiddleware,
|
|
20
|
-
Handler
|
|
21
|
-
RoutesMap
|
|
32
|
+
Handler
|
|
22
33
|
} from "./types";
|
|
23
34
|
|
|
24
35
|
import type { ResolvedCompressionConfig } from "./internal/types";
|
|
25
36
|
|
|
26
|
-
// A utility function to create an error with a custom stack trace
|
|
27
|
-
export function frameworkError(
|
|
28
|
-
message: string,
|
|
29
|
-
skipFn: Function,
|
|
30
|
-
code?: string,
|
|
31
|
-
status?: number
|
|
32
|
-
) {
|
|
33
|
-
const err = new Error(message) as Error & {
|
|
34
|
-
code?: string;
|
|
35
|
-
cpeak_err?: boolean;
|
|
36
|
-
};
|
|
37
|
-
Error.captureStackTrace(err, skipFn);
|
|
38
|
-
|
|
39
|
-
err.cpeak_err = true;
|
|
40
|
-
|
|
41
|
-
if (code) err.code = code;
|
|
42
|
-
if (status) (err as any).status = status;
|
|
43
|
-
|
|
44
|
-
return err;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
export enum ErrorCode {
|
|
48
|
-
MISSING_MIME = "CPEAK_ERR_MISSING_MIME",
|
|
49
|
-
FILE_NOT_FOUND = "CPEAK_ERR_FILE_NOT_FOUND",
|
|
50
|
-
NOT_A_FILE = "CPEAK_ERR_NOT_A_FILE",
|
|
51
|
-
SEND_FILE_FAIL = "CPEAK_ERR_SEND_FILE_FAIL",
|
|
52
|
-
INVALID_JSON = "CPEAK_ERR_INVALID_JSON",
|
|
53
|
-
PAYLOAD_TOO_LARGE = "CPEAK_ERR_PAYLOAD_TOO_LARGE",
|
|
54
|
-
WEAK_SECRET = "CPEAK_ERR_WEAK_SECRET",
|
|
55
|
-
COMPRESSION_NOT_ENABLED = "CPEAK_ERR_COMPRESSION_NOT_ENABLED"
|
|
56
|
-
}
|
|
57
|
-
|
|
58
37
|
export class CpeakIncomingMessage extends http.IncomingMessage {
|
|
59
38
|
// We define body and params here for better V8 optimization (not changing the shape of the object at runtime)
|
|
60
39
|
public body: any = undefined;
|
|
@@ -88,13 +67,19 @@ export class CpeakServerResponse extends http.ServerResponse<CpeakIncomingMessag
|
|
|
88
67
|
_compression?: ResolvedCompressionConfig;
|
|
89
68
|
|
|
90
69
|
// Send a file back to the client
|
|
91
|
-
async sendFile(path: string, mime
|
|
70
|
+
async sendFile(path: string, mime?: string) {
|
|
71
|
+
if (this.headersSent) return;
|
|
92
72
|
if (!mime) {
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
73
|
+
const dotIndex = path.lastIndexOf(".");
|
|
74
|
+
const fileExtension = dotIndex >= 0 ? path.slice(dotIndex + 1) : "";
|
|
75
|
+
mime = MIME_TYPES[fileExtension];
|
|
76
|
+
if (!mime) {
|
|
77
|
+
throw frameworkError(
|
|
78
|
+
`MIME type is missing for "${path}". Pass it as the second argument or register the extension via cpeak({ mimeTypes: { ${fileExtension || "ext"}: "..." } }).`,
|
|
79
|
+
this.sendFile,
|
|
80
|
+
ErrorCode.MISSING_MIME
|
|
81
|
+
);
|
|
82
|
+
}
|
|
98
83
|
}
|
|
99
84
|
|
|
100
85
|
try {
|
|
@@ -135,7 +120,9 @@ export class CpeakServerResponse extends http.ServerResponse<CpeakIncomingMessag
|
|
|
135
120
|
throw frameworkError(
|
|
136
121
|
`Failed to send file: ${path}`,
|
|
137
122
|
this.sendFile,
|
|
138
|
-
ErrorCode.SEND_FILE_FAIL
|
|
123
|
+
ErrorCode.SEND_FILE_FAIL,
|
|
124
|
+
undefined,
|
|
125
|
+
isClientDisconnect(err)
|
|
139
126
|
);
|
|
140
127
|
}
|
|
141
128
|
}
|
|
@@ -163,14 +150,23 @@ export class CpeakServerResponse extends http.ServerResponse<CpeakIncomingMessag
|
|
|
163
150
|
|
|
164
151
|
// Send a json data back to the client.
|
|
165
152
|
// This is only good for bodies that their size is less than the highWaterMark value.
|
|
166
|
-
|
|
167
|
-
|
|
153
|
+
json(data: any): Promise<void> {
|
|
154
|
+
if (this.headersSent) return Promise.resolve();
|
|
168
155
|
const body = JSON.stringify(data);
|
|
169
156
|
if (this._compression) {
|
|
170
157
|
return compressAndSend(this, "application/json", body, this._compression);
|
|
171
158
|
}
|
|
172
159
|
this.setHeader("Content-Type", "application/json");
|
|
173
160
|
this.end(body);
|
|
161
|
+
return Promise.resolve();
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
render(): Promise<void> {
|
|
165
|
+
throw frameworkError(
|
|
166
|
+
"render middleware not registered. Add render() via app.beforeEach(render()) to use res.render.",
|
|
167
|
+
this.render,
|
|
168
|
+
ErrorCode.RENDER_NOT_ENABLED
|
|
169
|
+
);
|
|
174
170
|
}
|
|
175
171
|
|
|
176
172
|
// Explicit compression entry point. A developer can use this in any custom handler to compress arbitrary responses
|
|
@@ -179,6 +175,7 @@ export class CpeakServerResponse extends http.ServerResponse<CpeakIncomingMessag
|
|
|
179
175
|
body: Buffer | string | Readable,
|
|
180
176
|
size?: number
|
|
181
177
|
): Promise<void> {
|
|
178
|
+
if (this.headersSent) return Promise.resolve();
|
|
182
179
|
if (!this._compression) {
|
|
183
180
|
throw frameworkError(
|
|
184
181
|
"compression is not enabled. Pass `compression` to cpeak({ compression: true | { ... } }) to use res.compress.",
|
|
@@ -191,10 +188,11 @@ export class CpeakServerResponse extends http.ServerResponse<CpeakIncomingMessag
|
|
|
191
188
|
}
|
|
192
189
|
|
|
193
190
|
export class Cpeak {
|
|
194
|
-
#server:
|
|
195
|
-
#
|
|
191
|
+
#server: CpeakHttpServer;
|
|
192
|
+
#router: Router;
|
|
196
193
|
#middleware: Middleware[];
|
|
197
194
|
#handleErr?: (err: unknown, req: CpeakRequest, res: CpeakResponse) => void;
|
|
195
|
+
#fallback?: Handler;
|
|
198
196
|
#compression?: ResolvedCompressionConfig;
|
|
199
197
|
|
|
200
198
|
constructor(options: CpeakOptions = {}) {
|
|
@@ -202,7 +200,7 @@ export class Cpeak {
|
|
|
202
200
|
IncomingMessage: CpeakIncomingMessage,
|
|
203
201
|
ServerResponse: CpeakServerResponse
|
|
204
202
|
});
|
|
205
|
-
this.#
|
|
203
|
+
this.#router = new Router();
|
|
206
204
|
this.#middleware = [];
|
|
207
205
|
|
|
208
206
|
// Resolve compression options once at app startup.
|
|
@@ -210,6 +208,9 @@ export class Cpeak {
|
|
|
210
208
|
this.#compression = resolveCompressionOptions(options.compression);
|
|
211
209
|
}
|
|
212
210
|
|
|
211
|
+
// Merge developer-supplied mime types with the defaults once at startup
|
|
212
|
+
if (options.mimeTypes) Object.assign(MIME_TYPES, options.mimeTypes);
|
|
213
|
+
|
|
213
214
|
this.#server.on(
|
|
214
215
|
"request",
|
|
215
216
|
async (req: CpeakRequest, res: CpeakResponse) => {
|
|
@@ -220,13 +221,37 @@ export class Cpeak {
|
|
|
220
221
|
const urlWithoutQueries =
|
|
221
222
|
qIndex === -1 ? req.url || "" : req.url?.substring(0, qIndex);
|
|
222
223
|
|
|
223
|
-
|
|
224
|
+
// Routes every error path through the registered handleErr. Awaits
|
|
225
|
+
// handleErr so its own async work (or a rejecting res.json under
|
|
226
|
+
// compression) is caught. If handleErr itself fails, we log and send a
|
|
227
|
+
// bare 500 so the client never gets a hung socket. Returns a Promise
|
|
228
|
+
// that never rejects to avoid unhandled promise rejections in case of errors in handleErr.
|
|
229
|
+
const dispatchError = async (error: unknown) => {
|
|
224
230
|
if (res.headersSent) {
|
|
225
231
|
req.socket?.destroy();
|
|
226
|
-
|
|
232
|
+
} else {
|
|
233
|
+
res.setHeader("Connection", "close");
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
if (isClientDisconnect(error) && !(error as any).clientDisconnect) {
|
|
237
|
+
(error as any).clientDisconnect = true;
|
|
238
|
+
}
|
|
239
|
+
try {
|
|
240
|
+
await this.#handleErr?.(error, req, res);
|
|
241
|
+
} catch (handlerFailure) {
|
|
242
|
+
console.error(
|
|
243
|
+
"[cpeak] handleErr failed while processing:",
|
|
244
|
+
error,
|
|
245
|
+
"\nReason:",
|
|
246
|
+
handlerFailure
|
|
247
|
+
);
|
|
248
|
+
if (!res.headersSent) {
|
|
249
|
+
try {
|
|
250
|
+
res.statusCode = 500;
|
|
251
|
+
res.end();
|
|
252
|
+
} catch {}
|
|
253
|
+
}
|
|
227
254
|
}
|
|
228
|
-
res.setHeader("Connection", "close");
|
|
229
|
-
this.#handleErr?.(error, req, res);
|
|
230
255
|
};
|
|
231
256
|
|
|
232
257
|
// Run all the specific middleware functions for that router only and then run the handler
|
|
@@ -240,29 +265,22 @@ export class Cpeak {
|
|
|
240
265
|
// Our exit point...
|
|
241
266
|
if (index === middleware.length) {
|
|
242
267
|
// Call the route handler with the modified req and res objects.
|
|
243
|
-
// Also handle the promise errors by passing them to
|
|
268
|
+
// Also handle the promise errors by passing them to handleErr to save developers from having to manually wrap every handler in try/catch.
|
|
244
269
|
try {
|
|
245
|
-
await cb(req, res
|
|
270
|
+
await cb(req, res);
|
|
246
271
|
} catch (error) {
|
|
247
272
|
dispatchError(error);
|
|
248
273
|
}
|
|
249
274
|
} else {
|
|
250
|
-
// Handle the promise errors by passing them to
|
|
275
|
+
// Handle the promise errors by passing them to handleErr to save developers from having to manually wrap every route middleware in try/catch.
|
|
251
276
|
try {
|
|
252
|
-
await middleware[index](
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
return dispatchError(error);
|
|
260
|
-
}
|
|
261
|
-
await runHandler(req, res, middleware, cb, index + 1);
|
|
262
|
-
},
|
|
263
|
-
// Error handler for a route middleware
|
|
264
|
-
dispatchError
|
|
265
|
-
);
|
|
277
|
+
await middleware[index](req, res, async (error?: unknown) => {
|
|
278
|
+
// this function only accepts an error argument to be more compatible with NPM modules that are built for express
|
|
279
|
+
if (error) {
|
|
280
|
+
return dispatchError(error);
|
|
281
|
+
}
|
|
282
|
+
await runHandler(req, res, middleware, cb, index + 1);
|
|
283
|
+
});
|
|
266
284
|
} catch (error) {
|
|
267
285
|
dispatchError(error);
|
|
268
286
|
}
|
|
@@ -278,32 +296,30 @@ export class Cpeak {
|
|
|
278
296
|
) => {
|
|
279
297
|
// Our exit point...
|
|
280
298
|
if (index === middleware.length) {
|
|
281
|
-
const
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
0
|
|
302
|
-
);
|
|
303
|
-
}
|
|
299
|
+
const method = req.method?.toLowerCase() || "";
|
|
300
|
+
const found = this.#router.find(method, urlWithoutQueries || "");
|
|
301
|
+
|
|
302
|
+
if (found) {
|
|
303
|
+
req.params = found.params;
|
|
304
|
+
return await runHandler(
|
|
305
|
+
req,
|
|
306
|
+
res,
|
|
307
|
+
found.middleware,
|
|
308
|
+
found.handler,
|
|
309
|
+
0
|
|
310
|
+
);
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// If a fallback handler is registered, run it before falling back to the default 404
|
|
314
|
+
if (this.#fallback) {
|
|
315
|
+
try {
|
|
316
|
+
return await this.#fallback(req, res);
|
|
317
|
+
} catch (error) {
|
|
318
|
+
return dispatchError(error);
|
|
304
319
|
}
|
|
320
|
+
}
|
|
305
321
|
|
|
306
|
-
// If the requested route dose not exist, return 404
|
|
322
|
+
// If the requested route dose not exist, and developer has not registered the fallback handler, return 404
|
|
307
323
|
return res
|
|
308
324
|
.status(404)
|
|
309
325
|
.json({ error: `Cannot ${req.method} ${urlWithoutQueries}` });
|
|
@@ -327,8 +343,6 @@ export class Cpeak {
|
|
|
327
343
|
}
|
|
328
344
|
|
|
329
345
|
route(method: string, path: string, ...args: (RouteMiddleware | Handler)[]) {
|
|
330
|
-
if (!this.#routes[method]) this.#routes[method] = [];
|
|
331
|
-
|
|
332
346
|
// The last argument should always be our handler
|
|
333
347
|
const cb = args.pop() as Handler;
|
|
334
348
|
|
|
@@ -339,8 +353,7 @@ export class Cpeak {
|
|
|
339
353
|
// Rest will be our middleware functions
|
|
340
354
|
const middleware = args.flat() as RouteMiddleware[];
|
|
341
355
|
|
|
342
|
-
|
|
343
|
-
this.#routes[method].push({ path, regex, middleware, cb });
|
|
356
|
+
this.#router.add(method, path, middleware, cb);
|
|
344
357
|
}
|
|
345
358
|
|
|
346
359
|
beforeEach(cb: Middleware) {
|
|
@@ -351,8 +364,24 @@ export class Cpeak {
|
|
|
351
364
|
this.#handleErr = cb;
|
|
352
365
|
}
|
|
353
366
|
|
|
354
|
-
|
|
355
|
-
|
|
367
|
+
// This will handle any request that doesn't match any of the routes and middleware functions
|
|
368
|
+
fallback(cb: Handler) {
|
|
369
|
+
if (this.#fallback) {
|
|
370
|
+
throw frameworkError(
|
|
371
|
+
"Fallback handler is already registered. Only one fallback can be set per app.",
|
|
372
|
+
this.fallback,
|
|
373
|
+
ErrorCode.DUPLICATE_FALLBACK
|
|
374
|
+
);
|
|
375
|
+
}
|
|
376
|
+
this.#fallback = cb;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
// The first 3 listens are just TS overloads for better type inference and editor autocompletion. The last one is the actual implementation.
|
|
380
|
+
listen(port: number, cb?: () => void): CpeakHttpServer;
|
|
381
|
+
listen(port: number, host: string, cb?: () => void): CpeakHttpServer;
|
|
382
|
+
listen(options: net.ListenOptions, cb?: () => void): CpeakHttpServer;
|
|
383
|
+
listen(...args: any[]) {
|
|
384
|
+
return this.#server.listen(...args);
|
|
356
385
|
}
|
|
357
386
|
|
|
358
387
|
address() {
|
|
@@ -360,29 +389,12 @@ export class Cpeak {
|
|
|
360
389
|
}
|
|
361
390
|
|
|
362
391
|
close(cb?: (err?: Error) => void) {
|
|
363
|
-
this.#server.close(cb);
|
|
392
|
+
return this.#server.close(cb);
|
|
364
393
|
}
|
|
365
394
|
|
|
366
|
-
//
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
#pathToRegex(path: string) {
|
|
370
|
-
const regexString =
|
|
371
|
-
"^" + path.replace(/:\w+/g, "([^/]+)").replace(/\*/g, ".*") + "$";
|
|
372
|
-
|
|
373
|
-
return new RegExp(regexString);
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
#extractPathVariables(path: string, match: RegExpMatchArray) {
|
|
377
|
-
// Extract path url variable values from the matched route
|
|
378
|
-
const paramNames = (path.match(/:\w+/g) || []).map((param) =>
|
|
379
|
-
param.slice(1)
|
|
380
|
-
);
|
|
381
|
-
const params: StringMap = {};
|
|
382
|
-
paramNames.forEach((name, index) => {
|
|
383
|
-
params[name] = match[index + 1];
|
|
384
|
-
});
|
|
385
|
-
return params;
|
|
395
|
+
// A getter for developers who want to access the underlying http server instance for advanced use cases that aren't covered by Cpeak
|
|
396
|
+
get server() {
|
|
397
|
+
return this.#server;
|
|
386
398
|
}
|
|
387
399
|
}
|
|
388
400
|
|
|
@@ -409,15 +421,14 @@ export type {
|
|
|
409
421
|
export type { CompressionOptions } from "./internal/types";
|
|
410
422
|
|
|
411
423
|
export type {
|
|
424
|
+
CpeakHttpServer,
|
|
412
425
|
CpeakOptions,
|
|
413
426
|
CpeakRequest,
|
|
414
427
|
CpeakResponse,
|
|
415
428
|
Next,
|
|
416
|
-
HandleErr,
|
|
417
429
|
Middleware,
|
|
418
430
|
RouteMiddleware,
|
|
419
|
-
Handler
|
|
420
|
-
RoutesMap
|
|
431
|
+
Handler
|
|
421
432
|
} from "./types";
|
|
422
433
|
|
|
423
434
|
export default function cpeak(options?: CpeakOptions): Cpeak {
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
// A utility function to create an error with a custom stack trace
|
|
2
|
+
export function frameworkError(
|
|
3
|
+
message: string,
|
|
4
|
+
skipFn: Function,
|
|
5
|
+
code?: string,
|
|
6
|
+
status?: number,
|
|
7
|
+
clientDisconnect?: boolean
|
|
8
|
+
) {
|
|
9
|
+
const err = new Error(message) as Error & {
|
|
10
|
+
code?: string;
|
|
11
|
+
cpeak_err?: boolean;
|
|
12
|
+
clientDisconnect?: boolean;
|
|
13
|
+
};
|
|
14
|
+
Error.captureStackTrace(err, skipFn);
|
|
15
|
+
|
|
16
|
+
err.cpeak_err = true;
|
|
17
|
+
|
|
18
|
+
if (code) err.code = code;
|
|
19
|
+
if (status) (err as any).status = status;
|
|
20
|
+
if (clientDisconnect) err.clientDisconnect = true;
|
|
21
|
+
|
|
22
|
+
return err;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const CLIENT_DISCONNECT_CODES = new Set([
|
|
26
|
+
"ERR_STREAM_PREMATURE_CLOSE",
|
|
27
|
+
"ERR_STREAM_DESTROYED",
|
|
28
|
+
"ECONNRESET",
|
|
29
|
+
"EPIPE"
|
|
30
|
+
]);
|
|
31
|
+
|
|
32
|
+
export function isClientDisconnect(err: unknown): boolean {
|
|
33
|
+
return CLIENT_DISCONNECT_CODES.has((err as any)?.code);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export enum ErrorCode {
|
|
37
|
+
MISSING_MIME = "CPEAK_ERR_MISSING_MIME",
|
|
38
|
+
FILE_NOT_FOUND = "CPEAK_ERR_FILE_NOT_FOUND",
|
|
39
|
+
NOT_A_FILE = "CPEAK_ERR_NOT_A_FILE",
|
|
40
|
+
SEND_FILE_FAIL = "CPEAK_ERR_SEND_FILE_FAIL",
|
|
41
|
+
INVALID_JSON = "CPEAK_ERR_INVALID_JSON",
|
|
42
|
+
PAYLOAD_TOO_LARGE = "CPEAK_ERR_PAYLOAD_TOO_LARGE",
|
|
43
|
+
WEAK_SECRET = "CPEAK_ERR_WEAK_SECRET",
|
|
44
|
+
COMPRESSION_NOT_ENABLED = "CPEAK_ERR_COMPRESSION_NOT_ENABLED",
|
|
45
|
+
RENDER_NOT_ENABLED = "CPEAK_ERR_RENDER_NOT_ENABLED",
|
|
46
|
+
// For router:
|
|
47
|
+
DUPLICATE_ROUTE = "CPEAK_ERR_DUPLICATE_ROUTE",
|
|
48
|
+
INVALID_ROUTE = "CPEAK_ERR_INVALID_ROUTE",
|
|
49
|
+
DUPLICATE_FALLBACK = "CPEAK_ERR_DUPLICATE_FALLBACK",
|
|
50
|
+
RENDER_FAIL = "CPEAK_ERR_RENDER_FAIL"
|
|
51
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { StringMap } from "../types";
|
|
2
|
+
|
|
3
|
+
// Developers can expand this if needed in the cpeak() constructor
|
|
4
|
+
export const MIME_TYPES: StringMap = {
|
|
5
|
+
html: "text/html",
|
|
6
|
+
css: "text/css",
|
|
7
|
+
js: "application/javascript",
|
|
8
|
+
jpg: "image/jpeg",
|
|
9
|
+
jpeg: "image/jpeg",
|
|
10
|
+
png: "image/png",
|
|
11
|
+
svg: "image/svg+xml",
|
|
12
|
+
txt: "text/plain",
|
|
13
|
+
eot: "application/vnd.ms-fontobject",
|
|
14
|
+
otf: "font/otf",
|
|
15
|
+
ttf: "font/ttf",
|
|
16
|
+
woff: "font/woff",
|
|
17
|
+
woff2: "font/woff2",
|
|
18
|
+
gif: "image/gif",
|
|
19
|
+
ico: "image/x-icon",
|
|
20
|
+
json: "application/json",
|
|
21
|
+
map: "application/json",
|
|
22
|
+
webmanifest: "application/manifest+json",
|
|
23
|
+
xml: "application/xml",
|
|
24
|
+
pdf: "application/pdf",
|
|
25
|
+
mp4: "video/mp4",
|
|
26
|
+
webm: "video/webm",
|
|
27
|
+
mp3: "audio/mpeg",
|
|
28
|
+
wav: "audio/wav",
|
|
29
|
+
webp: "image/webp",
|
|
30
|
+
avif: "image/avif"
|
|
31
|
+
};
|