tspace-spear 1.2.3 → 1.2.5-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +268 -19
- package/dist/lib/core/client/index.d.ts +23 -0
- package/dist/lib/core/client/index.js +45 -0
- package/dist/lib/core/client/index.js.map +1 -0
- package/dist/lib/core/compiler/generator.d.ts +18 -0
- package/dist/lib/core/compiler/generator.js +142 -0
- package/dist/lib/core/compiler/generator.js.map +1 -0
- package/dist/lib/core/compiler/index.d.ts +14 -0
- package/dist/lib/core/compiler/index.js +11 -0
- package/dist/lib/core/compiler/index.js.map +1 -0
- package/dist/lib/core/compiler/pre-routes.d.ts +3 -0
- package/dist/lib/core/compiler/pre-routes.js +5 -0
- package/dist/lib/core/compiler/pre-routes.js.map +1 -0
- package/dist/lib/core/compiler/types.d.ts +12 -0
- package/dist/lib/core/compiler/types.js +3 -0
- package/dist/lib/core/compiler/types.js.map +1 -0
- package/dist/lib/core/const/index.d.ts +153 -0
- package/dist/lib/core/const/index.js +105 -0
- package/dist/lib/core/const/index.js.map +1 -0
- package/dist/lib/core/decorators/context.d.ts +16 -9
- package/dist/lib/core/decorators/context.js +85 -59
- package/dist/lib/core/decorators/context.js.map +1 -1
- package/dist/lib/core/decorators/headers.d.ts +2 -2
- package/dist/lib/core/decorators/headers.js +1 -1
- package/dist/lib/core/decorators/headers.js.map +1 -1
- package/dist/lib/core/decorators/methods.d.ts +7 -7
- package/dist/lib/core/decorators/methods.js.map +1 -1
- package/dist/lib/core/decorators/middleware.d.ts +3 -3
- package/dist/lib/core/decorators/middleware.js +2 -2
- package/dist/lib/core/decorators/middleware.js.map +1 -1
- package/dist/lib/core/decorators/statusCode.d.ts +1 -1
- package/dist/lib/core/decorators/statusCode.js.map +1 -1
- package/dist/lib/core/decorators/swagger.d.ts +1 -1
- package/dist/lib/core/decorators/swagger.js.map +1 -1
- package/dist/lib/core/server/fast-router.d.ts +133 -0
- package/dist/lib/core/server/fast-router.js +277 -0
- package/dist/lib/core/server/fast-router.js.map +1 -0
- package/dist/lib/core/server/index.d.ts +39 -37
- package/dist/lib/core/server/index.js +201 -501
- package/dist/lib/core/server/index.js.map +1 -1
- package/dist/lib/core/server/net/index.d.ts +20 -0
- package/dist/lib/core/server/net/index.js +393 -0
- package/dist/lib/core/server/net/index.js.map +1 -0
- package/dist/lib/core/server/parser-factory.d.ts +10 -11
- package/dist/lib/core/server/parser-factory.js +259 -437
- package/dist/lib/core/server/parser-factory.js.map +1 -1
- package/dist/lib/core/server/response.d.ts +6 -0
- package/dist/lib/core/server/response.js +168 -0
- package/dist/lib/core/server/response.js.map +1 -0
- package/dist/lib/core/server/router.d.ts +2 -12
- package/dist/lib/core/server/router.js +2 -13
- package/dist/lib/core/server/router.js.map +1 -1
- package/dist/lib/core/server/uWS/index.d.ts +30 -0
- package/dist/lib/core/server/uWS/index.js +357 -0
- package/dist/lib/core/server/uWS/index.js.map +1 -0
- package/dist/lib/core/types/index.d.ts +150 -48
- package/dist/lib/core/utils/index.d.ts +12 -0
- package/dist/lib/core/utils/index.js +137 -0
- package/dist/lib/core/utils/index.js.map +1 -0
- package/dist/lib/index.d.ts +4 -3
- package/dist/lib/index.js +4 -2
- package/dist/lib/index.js.map +1 -1
- package/package.json +20 -14
|
@@ -1,17 +1,21 @@
|
|
|
1
1
|
import http, { IncomingHttpHeaders, IncomingMessage, ServerResponse } from "http";
|
|
2
2
|
import WebSocket from "ws";
|
|
3
|
-
|
|
3
|
+
import net from 'net';
|
|
4
|
+
type TContextBase = {
|
|
4
5
|
req: TRequest;
|
|
5
6
|
res: TResponse;
|
|
6
7
|
headers: THeaders;
|
|
7
|
-
query: TQuery;
|
|
8
8
|
params: TParams;
|
|
9
|
-
body: TBody;
|
|
10
|
-
files: TFileUpload;
|
|
11
9
|
cookies: TCookies;
|
|
12
10
|
ip: TIp;
|
|
11
|
+
ips: TIps;
|
|
12
|
+
body: TBody;
|
|
13
|
+
query: TQuery;
|
|
14
|
+
files: TFileUpload;
|
|
13
15
|
};
|
|
16
|
+
type TContext<Override extends Partial<Pick<TContextBase, "body" | "query" | "files" | "params">> = {}> = Omit<TContextBase, keyof Override> & Override;
|
|
14
17
|
type TIp = string | null;
|
|
18
|
+
type TIps = string[];
|
|
15
19
|
type THeaders<T = IncomingHttpHeaders> = {
|
|
16
20
|
[K in keyof T]: T[K];
|
|
17
21
|
};
|
|
@@ -37,37 +41,121 @@ type TFile = {
|
|
|
37
41
|
};
|
|
38
42
|
type TFileUpload = Record<string, TFile[] | undefined>;
|
|
39
43
|
type TNextFunction<T = any> = (err?: Error) => T | Promise<T>;
|
|
40
|
-
type TRequest = IncomingMessage &
|
|
44
|
+
type TRequest = IncomingMessage & {
|
|
45
|
+
uWs: any;
|
|
46
|
+
query: TQuery;
|
|
47
|
+
files: TFileUpload;
|
|
48
|
+
body: TBody;
|
|
49
|
+
params: TParams;
|
|
50
|
+
} & Partial<any>;
|
|
41
51
|
type THttpResponder = {
|
|
42
|
-
/**
|
|
52
|
+
/**
|
|
53
|
+
* Raw uWS HttpResponse instance.
|
|
54
|
+
*/
|
|
55
|
+
uWS: any;
|
|
56
|
+
/** 200 OK - Standard successful response */
|
|
43
57
|
ok: (data?: Record<string, any>) => any;
|
|
58
|
+
/** 201 Created - Resource successfully created */
|
|
44
59
|
created: (data?: Record<string, any>) => any;
|
|
60
|
+
/** 202 Accepted - Request accepted for processing */
|
|
45
61
|
accepted: (data?: Record<string, any>) => any;
|
|
46
|
-
/**
|
|
62
|
+
/** 204 No Content - Successful request with no response body */
|
|
47
63
|
noContent: (message?: string) => any;
|
|
64
|
+
/** 400 Bad Request - Invalid request from client */
|
|
48
65
|
badRequest: (message?: string) => any;
|
|
66
|
+
/** 401 Unauthorized - Authentication required or failed */
|
|
49
67
|
unauthorized: (message?: string) => any;
|
|
68
|
+
/** 402 Payment Required - Reserved for future/payment flow */
|
|
50
69
|
paymentRequired: (message?: string) => any;
|
|
70
|
+
/** 403 Forbidden - Client does not have access rights */
|
|
51
71
|
forbidden: (message?: string) => any;
|
|
72
|
+
/** 422 Unprocessable Entity - Valid request but semantic errors */
|
|
52
73
|
unprocessable: (message?: string) => any;
|
|
74
|
+
/** 429 Too Many Requests - Rate limit exceeded */
|
|
53
75
|
tooManyRequests: (message?: string) => any;
|
|
76
|
+
/** 404 Not Found - Resource does not exist */
|
|
54
77
|
notFound: (message?: string) => any;
|
|
55
|
-
/**500
|
|
78
|
+
/** 500 Internal Server Error - Generic server failure */
|
|
56
79
|
serverError: (message?: string) => any;
|
|
80
|
+
/** 502 Bad Gateway - Invalid response from upstream server */
|
|
57
81
|
badGateway: (message?: string) => any;
|
|
82
|
+
/** 503 Service Unavailable - Server temporarily unavailable */
|
|
58
83
|
unavailable: (message?: string) => any;
|
|
84
|
+
/** 504 Gateway Timeout - Upstream server timeout */
|
|
59
85
|
gatewayTimeout: (message?: string) => any;
|
|
60
|
-
/**
|
|
86
|
+
/**
|
|
87
|
+
* Serve a media file (video, image, PDF, etc.) from file system.
|
|
88
|
+
* @param filePath Absolute or relative path to media file
|
|
89
|
+
*/
|
|
90
|
+
serveMedia: (filePath: string) => any;
|
|
91
|
+
/**
|
|
92
|
+
* Send JSON response.
|
|
93
|
+
* @param data JSON serializable object
|
|
94
|
+
*/
|
|
61
95
|
json: (data?: Record<string, any>) => any;
|
|
96
|
+
/**
|
|
97
|
+
* Send error response (generic wrapper).
|
|
98
|
+
* @param err Error object or message
|
|
99
|
+
*/
|
|
62
100
|
error: (err: any) => any;
|
|
101
|
+
/**
|
|
102
|
+
* Send plain text response.
|
|
103
|
+
* @param message Text content
|
|
104
|
+
*/
|
|
63
105
|
send: (message: string) => any;
|
|
106
|
+
/**
|
|
107
|
+
* Send HTML response.
|
|
108
|
+
* @param html HTML string
|
|
109
|
+
*/
|
|
64
110
|
html: (html: string) => any;
|
|
111
|
+
/**
|
|
112
|
+
* Set HTTP status code and return chained response helpers.
|
|
113
|
+
*
|
|
114
|
+
* This method does not send a response immediately.
|
|
115
|
+
* Instead, it sets the status code and returns a response builder
|
|
116
|
+
* that allows sending the response in different formats.
|
|
117
|
+
*
|
|
118
|
+
* @param code HTTP status code to set for the response
|
|
119
|
+
* @returns An object containing response methods bound to the given status
|
|
120
|
+
*
|
|
121
|
+
* @example
|
|
122
|
+
* res.status(200).json({ success: true });
|
|
123
|
+
*
|
|
124
|
+
* @example
|
|
125
|
+
* res.status(404).send("Not Found");
|
|
126
|
+
*
|
|
127
|
+
* @example
|
|
128
|
+
* res.status(204).end();
|
|
129
|
+
*/
|
|
65
130
|
status: (code: TStatusCode) => {
|
|
131
|
+
/**
|
|
132
|
+
* Send JSON response with the previously set status code.
|
|
133
|
+
*
|
|
134
|
+
* @param data JSON-serializable object to send as response body
|
|
135
|
+
*/
|
|
66
136
|
json: (data?: Record<string, any>) => any;
|
|
137
|
+
/**
|
|
138
|
+
* Send plain text response with the previously set status code.
|
|
139
|
+
*
|
|
140
|
+
* @param message Text response body
|
|
141
|
+
*/
|
|
67
142
|
send: (message: string) => any;
|
|
143
|
+
/**
|
|
144
|
+
* End the response with optional raw message body.
|
|
145
|
+
*
|
|
146
|
+
* Commonly used for empty responses (e.g. 204 No Content).
|
|
147
|
+
*
|
|
148
|
+
* @param message Optional raw response body
|
|
149
|
+
*/
|
|
150
|
+
end: (message?: string) => any;
|
|
68
151
|
};
|
|
152
|
+
/**
|
|
153
|
+
* Set HTTP cookies.
|
|
154
|
+
* @param cookies Key-value map or detailed cookie objects
|
|
155
|
+
*/
|
|
69
156
|
setCookies: (cookies: Record<string, string | {
|
|
70
157
|
value: string;
|
|
158
|
+
path?: string;
|
|
71
159
|
sameSite?: 'Strict' | 'Lax' | 'None';
|
|
72
160
|
domain?: string;
|
|
73
161
|
secure?: boolean;
|
|
@@ -89,16 +177,16 @@ type TRoute = {
|
|
|
89
177
|
};
|
|
90
178
|
type TMethod = 'get' | 'post' | 'patch' | 'put' | 'delete' | 'all' | 'head' | 'options';
|
|
91
179
|
type TMethodInput = Uppercase<TMethod>;
|
|
92
|
-
type
|
|
180
|
+
type HandlerUWS = (res: unknown, req: unknown) => void | Promise<void>;
|
|
93
181
|
type UWS = {
|
|
94
182
|
App: () => {
|
|
95
|
-
get: (path: string, handler:
|
|
96
|
-
post: (path: string, handler:
|
|
97
|
-
patch: (path: string, handler:
|
|
98
|
-
put: (path: string, handler:
|
|
99
|
-
del: (path: string, handler:
|
|
100
|
-
any: (path: string, handler:
|
|
101
|
-
options: (path: string, handler:
|
|
183
|
+
get: (path: string, handler: HandlerUWS) => any;
|
|
184
|
+
post: (path: string, handler: HandlerUWS) => any;
|
|
185
|
+
patch: (path: string, handler: HandlerUWS) => any;
|
|
186
|
+
put: (path: string, handler: HandlerUWS) => any;
|
|
187
|
+
del: (path: string, handler: HandlerUWS) => any;
|
|
188
|
+
any: (path: string, handler: HandlerUWS) => any;
|
|
189
|
+
options: (path: string, handler: HandlerUWS) => any;
|
|
102
190
|
listen: (...args: any[]) => any;
|
|
103
191
|
ws: (path: string, options: {
|
|
104
192
|
open?: (ws: any) => void;
|
|
@@ -107,22 +195,33 @@ type UWS = {
|
|
|
107
195
|
}) => any;
|
|
108
196
|
};
|
|
109
197
|
};
|
|
110
|
-
type TAdapter =
|
|
198
|
+
type TAdapter = {
|
|
199
|
+
kind: 'http';
|
|
200
|
+
server: typeof http;
|
|
201
|
+
} | {
|
|
202
|
+
kind: 'net';
|
|
203
|
+
server: typeof net;
|
|
204
|
+
} | {
|
|
205
|
+
kind: 'uWS';
|
|
206
|
+
server: UWS;
|
|
207
|
+
};
|
|
208
|
+
type TAdapterServer = typeof http | typeof net | UWS;
|
|
111
209
|
type TApplication = {
|
|
112
210
|
controllers?: (new () => any)[] | {
|
|
113
211
|
folder: string;
|
|
114
212
|
name?: RegExp;
|
|
115
213
|
};
|
|
116
|
-
middlewares?:
|
|
214
|
+
middlewares?: TContextHandler[] | {
|
|
117
215
|
folder: string;
|
|
118
216
|
name?: RegExp;
|
|
119
217
|
};
|
|
120
218
|
globalPrefix?: string;
|
|
121
219
|
logger?: boolean;
|
|
122
220
|
cluster?: boolean | number;
|
|
123
|
-
adapter?:
|
|
221
|
+
adapter?: TAdapterServer;
|
|
222
|
+
express?: boolean;
|
|
124
223
|
};
|
|
125
|
-
type
|
|
224
|
+
type TContextHandler = (ctx: TContext, next: TNextFunction) => any;
|
|
126
225
|
type TErrorFunction = (err: Error, ctx: TContext) => any;
|
|
127
226
|
type TSwaggerFormat = "string" | "number" | "integer" | "boolean" | "object" | "array" | "date" | "date-time" | "password" | "int32" | "int64" | "float" | "double" | "byte" | "binary" | "base64" | "email" | "uuid" | "uri" | "hostname" | "ipv4" | "ipv6" | "json" | "xml";
|
|
128
227
|
type TSwaggerType = "string" | "number" | "integer" | "boolean" | "object" | "array" | "date" | "date-time" | "file";
|
|
@@ -164,40 +263,40 @@ type TSwaggerDoc = {
|
|
|
164
263
|
example?: Record<string, any>;
|
|
165
264
|
}[];
|
|
166
265
|
};
|
|
266
|
+
type TSwaggerPropertyOptions = {
|
|
267
|
+
type: "array";
|
|
268
|
+
items: TSwaggerPropertyOptions;
|
|
269
|
+
enum?: never;
|
|
270
|
+
required?: boolean;
|
|
271
|
+
example?: any;
|
|
272
|
+
description?: string;
|
|
273
|
+
format?: TSwaggerFormat;
|
|
274
|
+
} | {
|
|
275
|
+
type?: Exclude<TSwaggerType, "array">;
|
|
276
|
+
enum?: (string | number)[];
|
|
277
|
+
required?: boolean;
|
|
278
|
+
example?: any;
|
|
279
|
+
description?: string;
|
|
280
|
+
format?: TSwaggerFormat;
|
|
281
|
+
items?: never;
|
|
282
|
+
};
|
|
167
283
|
type TSwagger = {
|
|
168
284
|
disabled?: boolean;
|
|
169
285
|
description?: string;
|
|
170
286
|
summary?: string;
|
|
171
287
|
bearerToken?: boolean;
|
|
172
288
|
tags?: string[];
|
|
173
|
-
params?: Record<string,
|
|
174
|
-
|
|
175
|
-
type?: TSwaggerType;
|
|
176
|
-
example?: any;
|
|
177
|
-
}>;
|
|
178
|
-
query?: Record<string, {
|
|
179
|
-
required?: boolean;
|
|
180
|
-
description?: string;
|
|
181
|
-
type?: TSwaggerType;
|
|
182
|
-
example?: any;
|
|
183
|
-
}>;
|
|
289
|
+
params?: Record<string, TSwaggerPropertyOptions>;
|
|
290
|
+
query?: Record<string, TSwaggerPropertyOptions>;
|
|
184
291
|
body?: {
|
|
185
292
|
required?: boolean;
|
|
186
293
|
description?: string;
|
|
187
|
-
properties: Record<string,
|
|
188
|
-
type: TSwaggerType;
|
|
189
|
-
example?: any;
|
|
190
|
-
}>;
|
|
294
|
+
properties: Record<string, TSwaggerPropertyOptions>;
|
|
191
295
|
};
|
|
192
296
|
files?: {
|
|
193
297
|
required?: boolean;
|
|
194
298
|
description?: string;
|
|
195
|
-
properties: Record<string,
|
|
196
|
-
type: TSwaggerType;
|
|
197
|
-
format?: TSwaggerFormat;
|
|
198
|
-
items?: any;
|
|
199
|
-
example?: any;
|
|
200
|
-
}>;
|
|
299
|
+
properties: Record<string, TSwaggerPropertyOptions>;
|
|
201
300
|
};
|
|
202
301
|
cookies?: {
|
|
203
302
|
names: string[];
|
|
@@ -211,23 +310,24 @@ type TSwagger = {
|
|
|
211
310
|
}[];
|
|
212
311
|
};
|
|
213
312
|
type TWSHandler = {
|
|
214
|
-
connection: (ws: WebSocket) => void | string | Buffer;
|
|
215
|
-
message: (ws: WebSocket
|
|
216
|
-
close: (ws: WebSocket
|
|
217
|
-
error: (ws: WebSocket
|
|
313
|
+
connection: (ws: WebSocket & Partial<any>) => void | string | Buffer;
|
|
314
|
+
message: (ws: WebSocket & Partial<any>, data: WebSocket.Data) => void | string | Buffer;
|
|
315
|
+
close: (ws: WebSocket & Partial<any>, code: number, reason: Buffer) => void;
|
|
316
|
+
error: (ws: WebSocket & Partial<any>, error: Error) => void;
|
|
218
317
|
};
|
|
219
318
|
export declare namespace T {
|
|
319
|
+
type Context<O extends Partial<Pick<TContextBase, "body" | "query" | "files" | "params">> = {}> = TContext<O>;
|
|
220
320
|
type Adapter = TAdapter;
|
|
321
|
+
type AdapterServer = TAdapterServer;
|
|
221
322
|
type Application = TApplication;
|
|
222
323
|
type NextFunction = TNextFunction;
|
|
223
324
|
type File = TFile;
|
|
224
|
-
type Context = TContext;
|
|
225
325
|
type Router = TRouter;
|
|
226
326
|
type Route = TRoute;
|
|
227
327
|
type Method = TMethod;
|
|
228
328
|
type ErrorFunction = TErrorFunction;
|
|
229
329
|
type HttpStatus = THttpResponder;
|
|
230
|
-
type
|
|
330
|
+
type ContextHandler = TContextHandler;
|
|
231
331
|
type WebSocketHandler = TWSHandler;
|
|
232
332
|
type StatusCode = TStatusCode;
|
|
233
333
|
type MethodInput = TMethodInput;
|
|
@@ -239,6 +339,8 @@ export declare namespace T {
|
|
|
239
339
|
type Query<T = Record<string, string>> = TQuery<T>;
|
|
240
340
|
type Body<T = Record<string, any>> = TBody<T>;
|
|
241
341
|
type Headers<T = IncomingHttpHeaders> = THeaders<T>;
|
|
342
|
+
type Ip = TIp;
|
|
343
|
+
type Ips = TIps;
|
|
242
344
|
namespace Swagger {
|
|
243
345
|
type Spec = TSwagger;
|
|
244
346
|
type Format = TSwaggerFormat;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { IncomingMessage, ServerResponse } from "http";
|
|
2
|
+
import { Stream } from "stream";
|
|
3
|
+
export declare const normalizeRequestBody: ({ contentType, payload, }: {
|
|
4
|
+
contentType: string | null;
|
|
5
|
+
payload: any;
|
|
6
|
+
}) => Promise<any>;
|
|
7
|
+
export declare const pipeStream: ({ req, res, filePath, isUwebSocket, }: {
|
|
8
|
+
req: IncomingMessage;
|
|
9
|
+
res: ServerResponse;
|
|
10
|
+
filePath: string;
|
|
11
|
+
isUwebSocket?: boolean;
|
|
12
|
+
}) => Promise<Stream>;
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.pipeStream = exports.normalizeRequestBody = void 0;
|
|
7
|
+
const const_1 = require("../const");
|
|
8
|
+
const uWS_1 = require("../server/uWS");
|
|
9
|
+
const querystring_1 = __importDefault(require("querystring"));
|
|
10
|
+
const fs_1 = __importDefault(require("fs"));
|
|
11
|
+
const path_1 = __importDefault(require("path"));
|
|
12
|
+
const mime_types_1 = __importDefault(require("mime-types"));
|
|
13
|
+
const xml2js_1 = __importDefault(require("xml2js"));
|
|
14
|
+
const crypto_1 = __importDefault(require("crypto"));
|
|
15
|
+
const normalizeRequestBody = async ({ contentType, payload, }) => {
|
|
16
|
+
if (contentType == null || payload == null || payload === "") {
|
|
17
|
+
return {};
|
|
18
|
+
}
|
|
19
|
+
if (contentType.includes("x-www-form-urlencoded")) {
|
|
20
|
+
return querystring_1.default.parse(payload);
|
|
21
|
+
}
|
|
22
|
+
if (contentType.includes("application/json")) {
|
|
23
|
+
try {
|
|
24
|
+
return JSON.parse(payload);
|
|
25
|
+
}
|
|
26
|
+
catch (err) {
|
|
27
|
+
throw new Error("Invalid JSON format in request body.");
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
if (contentType.includes("application/xml") ||
|
|
31
|
+
contentType.includes("text/xml")) {
|
|
32
|
+
try {
|
|
33
|
+
const result = await xml2js_1.default.parseStringPromise(payload, {
|
|
34
|
+
explicitArray: false,
|
|
35
|
+
});
|
|
36
|
+
return result;
|
|
37
|
+
}
|
|
38
|
+
catch (err) {
|
|
39
|
+
throw new Error("Invalid XML format in request body.");
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
if (contentType.includes("text/plain") ||
|
|
43
|
+
contentType.includes("text/javascript") ||
|
|
44
|
+
contentType.includes("application/javascript") ||
|
|
45
|
+
contentType.includes("application/x-javascript")) {
|
|
46
|
+
return { contentType, text: payload };
|
|
47
|
+
}
|
|
48
|
+
return {};
|
|
49
|
+
};
|
|
50
|
+
exports.normalizeRequestBody = normalizeRequestBody;
|
|
51
|
+
const pipeStream = async ({ req, res, filePath, isUwebSocket, }) => {
|
|
52
|
+
if (!fs_1.default.existsSync(filePath)) {
|
|
53
|
+
return res
|
|
54
|
+
.writeHead(404, const_1.HEADER_CONTENT_TYPES["text"])
|
|
55
|
+
.end(`File not found: ${path_1.default.basename(filePath)}`);
|
|
56
|
+
}
|
|
57
|
+
if (isUwebSocket) {
|
|
58
|
+
return (0, uWS_1.uWSPipeStream)({ req, res, filePath });
|
|
59
|
+
}
|
|
60
|
+
const stat = fs_1.default.statSync(filePath);
|
|
61
|
+
const fileSize = stat.size;
|
|
62
|
+
const range = req.headers["range"] ?? null;
|
|
63
|
+
const contentType = mime_types_1.default.lookup(filePath) || "application/octet-stream";
|
|
64
|
+
const isVideo = contentType.startsWith("video/");
|
|
65
|
+
const writeHead = (header, code = 200) => {
|
|
66
|
+
const extension = filePath.split(".").pop();
|
|
67
|
+
const previews = Object.values({
|
|
68
|
+
video: [
|
|
69
|
+
"mp4",
|
|
70
|
+
"webm",
|
|
71
|
+
"ogg",
|
|
72
|
+
"ogv",
|
|
73
|
+
"avi",
|
|
74
|
+
"mov",
|
|
75
|
+
"mkv",
|
|
76
|
+
"flv",
|
|
77
|
+
"f4v",
|
|
78
|
+
"wmv",
|
|
79
|
+
"ts",
|
|
80
|
+
"mpeg",
|
|
81
|
+
],
|
|
82
|
+
audio: ["wav", "mp3"],
|
|
83
|
+
document: ["pdf"],
|
|
84
|
+
image: ["png", "jpeg", "jpg", "gif", "webp", "svg", "ico"],
|
|
85
|
+
}).flat();
|
|
86
|
+
if (previews.some((p) => extension?.toLocaleLowerCase().includes(p))) {
|
|
87
|
+
res.writeHead(code, header);
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
res.setHeader("Content-Disposition", `attachment; filename=${+new Date()}.${extension}`);
|
|
91
|
+
res.setHeader("Content-Type", "application/octet-stream");
|
|
92
|
+
};
|
|
93
|
+
const maxAge = 1000 * 60 * 60 * 24 * 7;
|
|
94
|
+
const etag = crypto_1.default.createHash("md5").update(`${stat.size}-${stat.mtimeMs}`).digest("hex");
|
|
95
|
+
const baseHeader = {
|
|
96
|
+
"Connection": "keep-alive",
|
|
97
|
+
"Keep-Alive": "timeout=60, max=1000",
|
|
98
|
+
"Cache-Control": `public, max-age=${maxAge}, immutable`,
|
|
99
|
+
"Strict-transport-security": `max-age=${maxAge}; includeSubDomains`,
|
|
100
|
+
"ETag": `"${etag}"`,
|
|
101
|
+
"Date": new Date(stat.birthtimeMs).toUTCString(),
|
|
102
|
+
"Last-modified": new Date(stat.birthtimeMs).toUTCString(),
|
|
103
|
+
"Vary": "Origin, Accept-Encoding",
|
|
104
|
+
"Accept-Ranges": "bytes",
|
|
105
|
+
"Content-Length": fileSize,
|
|
106
|
+
"Content-Type": contentType,
|
|
107
|
+
"X-Content-type-options": "nosniff",
|
|
108
|
+
"X-Xss-protection": "1; mode=block",
|
|
109
|
+
};
|
|
110
|
+
if (!isVideo || range == null) {
|
|
111
|
+
const header = {
|
|
112
|
+
...baseHeader,
|
|
113
|
+
"Content-Length": fileSize,
|
|
114
|
+
"Content-Type": contentType,
|
|
115
|
+
};
|
|
116
|
+
const stream = fs_1.default.createReadStream(filePath);
|
|
117
|
+
writeHead(header);
|
|
118
|
+
stream.on("error", () => res.end());
|
|
119
|
+
return stream.pipe(res);
|
|
120
|
+
}
|
|
121
|
+
const parts = range.replace(/bytes=/, "").split("-");
|
|
122
|
+
const start = parseInt(parts[0], 10);
|
|
123
|
+
const end = parts[1] ? parseInt(parts[1], 10) : fileSize - 1;
|
|
124
|
+
const chunksize = end - start + 1;
|
|
125
|
+
const stream = fs_1.default.createReadStream(filePath, { start, end });
|
|
126
|
+
const header = {
|
|
127
|
+
...baseHeader,
|
|
128
|
+
"Content-Range": `bytes ${start}-${end}/${fileSize}`,
|
|
129
|
+
"Content-Length": chunksize,
|
|
130
|
+
"Accept-Ranges": "bytes"
|
|
131
|
+
};
|
|
132
|
+
writeHead(header, 206);
|
|
133
|
+
stream.on("error", () => res.end());
|
|
134
|
+
return stream.pipe(res);
|
|
135
|
+
};
|
|
136
|
+
exports.pipeStream = pipeStream;
|
|
137
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/lib/core/utils/index.ts"],"names":[],"mappings":"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"}
|
package/dist/lib/index.d.ts
CHANGED
|
@@ -3,9 +3,10 @@
|
|
|
3
3
|
*
|
|
4
4
|
* @module tspace-spear
|
|
5
5
|
*/
|
|
6
|
+
import Spear from './core/server';
|
|
6
7
|
export * from './core/decorators';
|
|
7
|
-
export * from './core/
|
|
8
|
+
export * from './core/types';
|
|
8
9
|
export * from './core/server';
|
|
9
|
-
export
|
|
10
|
-
|
|
10
|
+
export * from './core/server/router';
|
|
11
|
+
export * from './core/client';
|
|
11
12
|
export default Spear;
|
package/dist/lib/index.js
CHANGED
|
@@ -22,9 +22,11 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
22
22
|
*
|
|
23
23
|
* @module tspace-spear
|
|
24
24
|
*/
|
|
25
|
+
const server_1 = __importDefault(require("./core/server"));
|
|
25
26
|
__exportStar(require("./core/decorators"), exports);
|
|
26
|
-
__exportStar(require("./core/
|
|
27
|
+
__exportStar(require("./core/types"), exports);
|
|
27
28
|
__exportStar(require("./core/server"), exports);
|
|
28
|
-
|
|
29
|
+
__exportStar(require("./core/server/router"), exports);
|
|
30
|
+
__exportStar(require("./core/client"), exports);
|
|
29
31
|
exports.default = server_1.default;
|
|
30
32
|
//# sourceMappingURL=index.js.map
|
package/dist/lib/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/lib/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;AAAA;;;;IAII;AACJ,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/lib/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;AAAA;;;;IAII;AACJ,2DAAkC;AAElC,oDAAkC;AAClC,+CAA6B;AAC7B,gDAA8B;AAC9B,uDAAqC;AACrC,gDAA6B;AAE7B,kBAAe,gBAAK,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tspace-spear",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.5-beta.1",
|
|
4
4
|
"description": "tspace-spear is a lightweight, high-performance API framework for Node.js that leverages the native HTTP server and supports uWebSockets.js (C++) for maximum speed and efficiency.",
|
|
5
5
|
"main": "./dist/lib/index.js",
|
|
6
6
|
"types": "./dist/lib/index.d.ts",
|
|
@@ -18,6 +18,8 @@
|
|
|
18
18
|
"api",
|
|
19
19
|
"rest api",
|
|
20
20
|
"fast",
|
|
21
|
+
"fast-api",
|
|
22
|
+
"fast api",
|
|
21
23
|
"low overhead",
|
|
22
24
|
"http",
|
|
23
25
|
"uWS",
|
|
@@ -35,9 +37,12 @@
|
|
|
35
37
|
"prepare": "npm run build",
|
|
36
38
|
"release": "npm run build && npm publish",
|
|
37
39
|
"beta": "npm run build && npm publish --tag beta",
|
|
38
|
-
"
|
|
40
|
+
"bm:node": "npm run benchmark:node",
|
|
41
|
+
"bm:bun": "npm run benchmark:bun",
|
|
42
|
+
"benchmark:node": "npm run build && node benchmarks/tests/benchmark.node.js",
|
|
43
|
+
"benchmark:bun": "npm run build && bun run benchmarks/tests/benchmark.bun.js",
|
|
39
44
|
"test": "mocha dist/tests/**/0*.test.js",
|
|
40
|
-
"load": "autocannon -c 100 -d 30 -p 10 localhost:
|
|
45
|
+
"load": "autocannon -c 100 -d 30 -p 10 localhost:5000",
|
|
41
46
|
"docs": "npx docsify-cli serve docs"
|
|
42
47
|
},
|
|
43
48
|
"engines": {
|
|
@@ -45,36 +50,37 @@
|
|
|
45
50
|
},
|
|
46
51
|
"dependencies": {
|
|
47
52
|
"busboy": "1.6.0",
|
|
48
|
-
"
|
|
53
|
+
"fast-querystring": "1.1.2",
|
|
49
54
|
"mime-types": "2.1.35",
|
|
50
55
|
"on-finished": "2.4.1",
|
|
51
56
|
"reflect-metadata": "0.2.2",
|
|
52
57
|
"swagger-ui-dist": "5.32.0",
|
|
53
|
-
"ws": "8.19.0"
|
|
58
|
+
"ws": "8.19.0",
|
|
59
|
+
"xml2js": "0.6.2",
|
|
60
|
+
"ts-morph": "28.0.0"
|
|
54
61
|
},
|
|
55
62
|
"devDependencies": {
|
|
63
|
+
"@elysiajs/node": "1.4.5",
|
|
64
|
+
"@hono/node-server": "1.19.11",
|
|
56
65
|
"@types/autocannon": "7.12.5",
|
|
57
66
|
"@types/busboy": "1.5.4",
|
|
58
|
-
"@types/chai": "5.2.3",
|
|
59
|
-
"@types/chai-http": "4.2.4",
|
|
60
67
|
"@types/express": "4.17.21",
|
|
61
|
-
"@types/jest": "30.0.0",
|
|
62
68
|
"@types/mime-types": "2.1.4",
|
|
63
|
-
"@types/
|
|
64
|
-
"@types/node": "16.4.0",
|
|
69
|
+
"@types/node": "16.18.126",
|
|
65
70
|
"@types/on-finished": "2.3.4",
|
|
66
71
|
"@types/swagger-ui-dist": "3.30.4",
|
|
67
72
|
"@types/ws": "8.18.1",
|
|
68
|
-
"@types/
|
|
73
|
+
"@types/xml2js": "0.4.14",
|
|
74
|
+
"0http": "4.4.0",
|
|
69
75
|
"autocannon": "7.15.0",
|
|
70
|
-
"chai": "4.5.0",
|
|
71
|
-
"chai-http": "5.1.2",
|
|
72
76
|
"docsify-cli": "4.4.4",
|
|
77
|
+
"elysia": "1.4.28",
|
|
73
78
|
"express": "4.19.2",
|
|
74
79
|
"fastify": "4.28.1",
|
|
80
|
+
"hono": "4.12.9",
|
|
75
81
|
"ts-node": "10.9.2",
|
|
76
82
|
"typescript": "5.9.3",
|
|
77
|
-
"
|
|
83
|
+
"uWebSockets.js": "github:uNetworking/uWebSockets.js#v20.63.0",
|
|
78
84
|
"zod": "4.3.6"
|
|
79
85
|
}
|
|
80
86
|
}
|