silgi 0.33.1 → 0.34.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/build.mjs +1 -2
- package/dist/cli/build.mjs +35 -309
- package/dist/cli/index.mjs +1 -1
- package/dist/cli/init.mjs +1 -2
- package/dist/cli/install.mjs +13 -2
- package/dist/cli/types.mjs +2 -10
- package/dist/cli/watch.mjs +1 -2
- package/dist/core/index.d.mts +292 -210
- package/dist/core/index.mjs +670 -465
- package/dist/index.d.mts +1 -1
- package/dist/index.mjs +3 -3
- package/dist/kit/index.d.mts +5 -31
- package/dist/kit/index.mjs +1 -55
- package/dist/runtime/internal/config.d.mts +2 -2
- package/dist/runtime/internal/next.d.mts +4 -0
- package/dist/runtime/internal/next.mjs +72 -0
- package/dist/runtime/internal/nitro.d.mts +1 -1
- package/dist/runtime/internal/nitro.mjs +87 -61
- package/dist/types/index.d.mts +418 -448
- package/package.json +6 -2
- package/dist/_chunks/routeRules.mjs +0 -288
- package/dist/runtime/internal/nextjs.d.mts +0 -4
- package/dist/runtime/internal/nextjs.mjs +0 -58
package/dist/core/index.mjs
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import { createConsola } from 'consola';
|
|
2
2
|
import { defu } from 'defu';
|
|
3
3
|
import { createHooks } from 'hookable';
|
|
4
|
-
import {
|
|
4
|
+
import { createRouter, addRoute, findRoute } from 'rou3';
|
|
5
5
|
import { getContext } from 'unctx';
|
|
6
6
|
import { Buffer } from 'node:buffer';
|
|
7
7
|
import { klona } from 'klona';
|
|
8
8
|
import { useSilgiRuntimeConfig } from 'silgi/runtime';
|
|
9
9
|
import { createStorage as createStorage$1, builtinDrivers, prefixStorage } from 'unstorage';
|
|
10
|
+
import { FastURL, FastResponse } from 'srvx';
|
|
10
11
|
import { isRuntimePresents } from 'silgi/kit';
|
|
11
12
|
export { s as silgiCLICtx, t as tryUseSilgiCLI, u as useSilgiCLI } from '../_chunks/silgiApp.mjs';
|
|
12
|
-
import 'ufo';
|
|
13
13
|
|
|
14
14
|
const silgiCtx = getContext("silgi");
|
|
15
15
|
function useSilgi() {
|
|
@@ -35,387 +35,133 @@ function tryUseSilgi() {
|
|
|
35
35
|
return silgiCtx.tryUse();
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
HttpStatus2[HttpStatus2["MULTI_STATUS"] = 207] = "MULTI_STATUS";
|
|
51
|
-
HttpStatus2[HttpStatus2["ALREADY_REPORTED"] = 208] = "ALREADY_REPORTED";
|
|
52
|
-
HttpStatus2[HttpStatus2["IM_USED"] = 226] = "IM_USED";
|
|
53
|
-
HttpStatus2[HttpStatus2["MULTIPLE_CHOICES"] = 300] = "MULTIPLE_CHOICES";
|
|
54
|
-
HttpStatus2[HttpStatus2["MOVED_PERMANENTLY"] = 301] = "MOVED_PERMANENTLY";
|
|
55
|
-
HttpStatus2[HttpStatus2["FOUND"] = 302] = "FOUND";
|
|
56
|
-
HttpStatus2[HttpStatus2["SEE_OTHER"] = 303] = "SEE_OTHER";
|
|
57
|
-
HttpStatus2[HttpStatus2["NOT_MODIFIED"] = 304] = "NOT_MODIFIED";
|
|
58
|
-
HttpStatus2[HttpStatus2["USE_PROXY"] = 305] = "USE_PROXY";
|
|
59
|
-
HttpStatus2[HttpStatus2["TEMPORARY_REDIRECT"] = 307] = "TEMPORARY_REDIRECT";
|
|
60
|
-
HttpStatus2[HttpStatus2["PERMANENT_REDIRECT"] = 308] = "PERMANENT_REDIRECT";
|
|
61
|
-
HttpStatus2[HttpStatus2["BAD_REQUEST"] = 400] = "BAD_REQUEST";
|
|
62
|
-
HttpStatus2[HttpStatus2["UNAUTHORIZED"] = 401] = "UNAUTHORIZED";
|
|
63
|
-
HttpStatus2[HttpStatus2["PAYMENT_REQUIRED"] = 402] = "PAYMENT_REQUIRED";
|
|
64
|
-
HttpStatus2[HttpStatus2["FORBIDDEN"] = 403] = "FORBIDDEN";
|
|
65
|
-
HttpStatus2[HttpStatus2["NOT_FOUND"] = 404] = "NOT_FOUND";
|
|
66
|
-
HttpStatus2[HttpStatus2["METHOD_NOT_ALLOWED"] = 405] = "METHOD_NOT_ALLOWED";
|
|
67
|
-
HttpStatus2[HttpStatus2["NOT_ACCEPTABLE"] = 406] = "NOT_ACCEPTABLE";
|
|
68
|
-
HttpStatus2[HttpStatus2["PROXY_AUTHENTICATION_REQUIRED"] = 407] = "PROXY_AUTHENTICATION_REQUIRED";
|
|
69
|
-
HttpStatus2[HttpStatus2["REQUEST_TIMEOUT"] = 408] = "REQUEST_TIMEOUT";
|
|
70
|
-
HttpStatus2[HttpStatus2["CONFLICT"] = 409] = "CONFLICT";
|
|
71
|
-
HttpStatus2[HttpStatus2["GONE"] = 410] = "GONE";
|
|
72
|
-
HttpStatus2[HttpStatus2["LENGTH_REQUIRED"] = 411] = "LENGTH_REQUIRED";
|
|
73
|
-
HttpStatus2[HttpStatus2["PRECONDITION_FAILED"] = 412] = "PRECONDITION_FAILED";
|
|
74
|
-
HttpStatus2[HttpStatus2["PAYLOAD_TOO_LARGE"] = 413] = "PAYLOAD_TOO_LARGE";
|
|
75
|
-
HttpStatus2[HttpStatus2["URI_TOO_LONG"] = 414] = "URI_TOO_LONG";
|
|
76
|
-
HttpStatus2[HttpStatus2["UNSUPPORTED_MEDIA_TYPE"] = 415] = "UNSUPPORTED_MEDIA_TYPE";
|
|
77
|
-
HttpStatus2[HttpStatus2["RANGE_NOT_SATISFIABLE"] = 416] = "RANGE_NOT_SATISFIABLE";
|
|
78
|
-
HttpStatus2[HttpStatus2["EXPECTATION_FAILED"] = 417] = "EXPECTATION_FAILED";
|
|
79
|
-
HttpStatus2[HttpStatus2["IM_A_TEAPOT"] = 418] = "IM_A_TEAPOT";
|
|
80
|
-
HttpStatus2[HttpStatus2["MISDIRECTED_REQUEST"] = 421] = "MISDIRECTED_REQUEST";
|
|
81
|
-
HttpStatus2[HttpStatus2["UNPROCESSABLE_ENTITY"] = 422] = "UNPROCESSABLE_ENTITY";
|
|
82
|
-
HttpStatus2[HttpStatus2["LOCKED"] = 423] = "LOCKED";
|
|
83
|
-
HttpStatus2[HttpStatus2["FAILED_DEPENDENCY"] = 424] = "FAILED_DEPENDENCY";
|
|
84
|
-
HttpStatus2[HttpStatus2["TOO_EARLY"] = 425] = "TOO_EARLY";
|
|
85
|
-
HttpStatus2[HttpStatus2["UPGRADE_REQUIRED"] = 426] = "UPGRADE_REQUIRED";
|
|
86
|
-
HttpStatus2[HttpStatus2["PRECONDITION_REQUIRED"] = 428] = "PRECONDITION_REQUIRED";
|
|
87
|
-
HttpStatus2[HttpStatus2["TOO_MANY_REQUESTS"] = 429] = "TOO_MANY_REQUESTS";
|
|
88
|
-
HttpStatus2[HttpStatus2["REQUEST_HEADER_FIELDS_TOO_LARGE"] = 431] = "REQUEST_HEADER_FIELDS_TOO_LARGE";
|
|
89
|
-
HttpStatus2[HttpStatus2["UNAVAILABLE_FOR_LEGAL_REASONS"] = 451] = "UNAVAILABLE_FOR_LEGAL_REASONS";
|
|
90
|
-
HttpStatus2[HttpStatus2["INTERNAL_SERVER_ERROR"] = 500] = "INTERNAL_SERVER_ERROR";
|
|
91
|
-
HttpStatus2[HttpStatus2["NOT_IMPLEMENTED"] = 501] = "NOT_IMPLEMENTED";
|
|
92
|
-
HttpStatus2[HttpStatus2["BAD_GATEWAY"] = 502] = "BAD_GATEWAY";
|
|
93
|
-
HttpStatus2[HttpStatus2["SERVICE_UNAVAILABLE"] = 503] = "SERVICE_UNAVAILABLE";
|
|
94
|
-
HttpStatus2[HttpStatus2["GATEWAY_TIMEOUT"] = 504] = "GATEWAY_TIMEOUT";
|
|
95
|
-
HttpStatus2[HttpStatus2["HTTP_VERSION_NOT_SUPPORTED"] = 505] = "HTTP_VERSION_NOT_SUPPORTED";
|
|
96
|
-
HttpStatus2[HttpStatus2["VARIANT_ALSO_NEGOTIATES"] = 506] = "VARIANT_ALSO_NEGOTIATES";
|
|
97
|
-
HttpStatus2[HttpStatus2["INSUFFICIENT_STORAGE"] = 507] = "INSUFFICIENT_STORAGE";
|
|
98
|
-
HttpStatus2[HttpStatus2["LOOP_DETECTED"] = 508] = "LOOP_DETECTED";
|
|
99
|
-
HttpStatus2[HttpStatus2["NOT_EXTENDED"] = 510] = "NOT_EXTENDED";
|
|
100
|
-
HttpStatus2[HttpStatus2["NETWORK_AUTHENTICATION_REQUIRED"] = 511] = "NETWORK_AUTHENTICATION_REQUIRED";
|
|
101
|
-
return HttpStatus2;
|
|
102
|
-
})(HttpStatus || {});
|
|
103
|
-
var ErrorSeverity = /* @__PURE__ */ ((ErrorSeverity2) => {
|
|
104
|
-
ErrorSeverity2["DEBUG"] = "DEBUG";
|
|
105
|
-
ErrorSeverity2["INFO"] = "INFO";
|
|
106
|
-
ErrorSeverity2["WARNING"] = "WARNING";
|
|
107
|
-
ErrorSeverity2["ERROR"] = "ERROR";
|
|
108
|
-
ErrorSeverity2["CRITICAL"] = "CRITICAL";
|
|
109
|
-
return ErrorSeverity2;
|
|
110
|
-
})(ErrorSeverity || {});
|
|
111
|
-
var ErrorCategory = /* @__PURE__ */ ((ErrorCategory2) => {
|
|
112
|
-
ErrorCategory2["AUTHENTICATION"] = "auth";
|
|
113
|
-
ErrorCategory2["AUTHORIZATION"] = "authorization";
|
|
114
|
-
ErrorCategory2["VALIDATION"] = "validation";
|
|
115
|
-
ErrorCategory2["BUSINESS"] = "business";
|
|
116
|
-
ErrorCategory2["INFRASTRUCTURE"] = "infrastructure";
|
|
117
|
-
ErrorCategory2["EXTERNAL"] = "external";
|
|
118
|
-
ErrorCategory2["UNKNOWN"] = "unknown";
|
|
119
|
-
return ErrorCategory2;
|
|
120
|
-
})(ErrorCategory || {});
|
|
121
|
-
class ErrorFactory {
|
|
122
|
-
static createMetadata(metadata) {
|
|
123
|
-
return {
|
|
124
|
-
timestamp: Date.now(),
|
|
125
|
-
...metadata
|
|
126
|
-
};
|
|
38
|
+
const EmptyObject = /* @__PURE__ */ (() => {
|
|
39
|
+
const C = function() {
|
|
40
|
+
};
|
|
41
|
+
C.prototype = /* @__PURE__ */ Object.create(null);
|
|
42
|
+
return C;
|
|
43
|
+
})();
|
|
44
|
+
|
|
45
|
+
function hasProp(obj, prop) {
|
|
46
|
+
try {
|
|
47
|
+
return prop in obj;
|
|
48
|
+
} catch {
|
|
49
|
+
return false;
|
|
127
50
|
}
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
category: options.category ?? "unknown" /* UNKNOWN */,
|
|
133
|
-
severity: options.severity ?? "ERROR" /* ERROR */,
|
|
134
|
-
httpStatus: options.httpStatus ?? 500 /* INTERNAL_SERVER_ERROR */,
|
|
135
|
-
metadata: this.createMetadata(options.metadata),
|
|
136
|
-
cause: options.cause,
|
|
137
|
-
context: options.context
|
|
138
|
-
});
|
|
51
|
+
}
|
|
52
|
+
function isJSONSerializable(value, _type) {
|
|
53
|
+
if (value === null || value === void 0) {
|
|
54
|
+
return true;
|
|
139
55
|
}
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
return this.create({
|
|
143
|
-
message,
|
|
144
|
-
code: 401,
|
|
145
|
-
category: "auth" /* AUTHENTICATION */,
|
|
146
|
-
severity: "ERROR" /* ERROR */,
|
|
147
|
-
httpStatus: 401 /* UNAUTHORIZED */,
|
|
148
|
-
context
|
|
149
|
-
});
|
|
56
|
+
if (_type !== "object") {
|
|
57
|
+
return _type === "boolean" || _type === "number" || _type === "string";
|
|
150
58
|
}
|
|
151
|
-
|
|
152
|
-
return
|
|
153
|
-
message,
|
|
154
|
-
code: 403,
|
|
155
|
-
category: "authorization" /* AUTHORIZATION */,
|
|
156
|
-
severity: "ERROR" /* ERROR */,
|
|
157
|
-
httpStatus: 403 /* FORBIDDEN */,
|
|
158
|
-
context
|
|
159
|
-
});
|
|
59
|
+
if (typeof value.toJSON === "function") {
|
|
60
|
+
return true;
|
|
160
61
|
}
|
|
161
|
-
|
|
162
|
-
return
|
|
163
|
-
message,
|
|
164
|
-
code: 400,
|
|
165
|
-
category: "validation" /* VALIDATION */,
|
|
166
|
-
severity: "WARNING" /* WARNING */,
|
|
167
|
-
httpStatus: 400 /* BAD_REQUEST */,
|
|
168
|
-
context
|
|
169
|
-
});
|
|
62
|
+
if (Array.isArray(value)) {
|
|
63
|
+
return true;
|
|
170
64
|
}
|
|
171
|
-
|
|
172
|
-
return
|
|
173
|
-
message,
|
|
174
|
-
code: 404,
|
|
175
|
-
category: "business" /* BUSINESS */,
|
|
176
|
-
severity: "WARNING" /* WARNING */,
|
|
177
|
-
httpStatus: 404 /* NOT_FOUND */,
|
|
178
|
-
context
|
|
179
|
-
});
|
|
65
|
+
if (typeof value.pipe === "function" || typeof value.pipeTo === "function") {
|
|
66
|
+
return false;
|
|
180
67
|
}
|
|
181
|
-
|
|
182
|
-
return
|
|
183
|
-
message,
|
|
184
|
-
code: 500,
|
|
185
|
-
category: "infrastructure" /* INFRASTRUCTURE */,
|
|
186
|
-
severity: "CRITICAL" /* CRITICAL */,
|
|
187
|
-
httpStatus: 500 /* INTERNAL_SERVER_ERROR */,
|
|
188
|
-
cause
|
|
189
|
-
});
|
|
68
|
+
if (value instanceof EmptyObject) {
|
|
69
|
+
return true;
|
|
190
70
|
}
|
|
71
|
+
const proto = Object.getPrototypeOf(value);
|
|
72
|
+
return proto === Object.prototype || proto === null;
|
|
191
73
|
}
|
|
74
|
+
|
|
75
|
+
const DISALLOWED_STATUS_CHARS = /[^\u0009\u0020-\u007E]/g;
|
|
76
|
+
function sanitizeStatusMessage(statusMessage = "") {
|
|
77
|
+
return statusMessage.replace(DISALLOWED_STATUS_CHARS, "");
|
|
78
|
+
}
|
|
79
|
+
function sanitizeStatusCode(statusCode, defaultStatusCode = 200) {
|
|
80
|
+
if (!statusCode) {
|
|
81
|
+
return defaultStatusCode;
|
|
82
|
+
}
|
|
83
|
+
if (typeof statusCode === "string") {
|
|
84
|
+
statusCode = Number.parseInt(statusCode, 10);
|
|
85
|
+
}
|
|
86
|
+
if (statusCode < 100 || statusCode > 999) {
|
|
87
|
+
return defaultStatusCode;
|
|
88
|
+
}
|
|
89
|
+
return statusCode;
|
|
90
|
+
}
|
|
91
|
+
|
|
192
92
|
class SilgiError extends Error {
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
93
|
+
static __silgi_error__ = true;
|
|
94
|
+
statusCode = 500;
|
|
95
|
+
fatal = false;
|
|
96
|
+
unhandled = false;
|
|
97
|
+
statusMessage;
|
|
98
|
+
data;
|
|
199
99
|
cause;
|
|
200
|
-
constructor(
|
|
201
|
-
super(
|
|
202
|
-
this.
|
|
203
|
-
|
|
204
|
-
this.category = error.category;
|
|
205
|
-
this.severity = error.severity;
|
|
206
|
-
this.httpStatus = error.httpStatus;
|
|
207
|
-
this.metadata = error.metadata ?? { timestamp: Date.now() };
|
|
208
|
-
this.context = error.context;
|
|
209
|
-
this.cause = error.cause;
|
|
210
|
-
if (Error.captureStackTrace) {
|
|
211
|
-
Error.captureStackTrace(this, this.constructor);
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
toString() {
|
|
215
|
-
let str = `${this.name} [${this.code}] ${this.severity}: ${this.message}`;
|
|
216
|
-
str += `
|
|
217
|
-
Category: ${this.category}`;
|
|
218
|
-
str += `
|
|
219
|
-
HTTP Status: ${this.httpStatus}`;
|
|
220
|
-
if (this.context) {
|
|
221
|
-
str += `
|
|
222
|
-
Context: ${JSON.stringify(this.context, null, 2)}`;
|
|
223
|
-
}
|
|
224
|
-
if (this.metadata) {
|
|
225
|
-
str += `
|
|
226
|
-
Metadata: ${JSON.stringify(this.metadata, null, 2)}`;
|
|
227
|
-
}
|
|
228
|
-
if (this.stack) {
|
|
229
|
-
str += `
|
|
230
|
-
${this.stack}`;
|
|
100
|
+
constructor(message, opts = {}) {
|
|
101
|
+
super(message, opts);
|
|
102
|
+
if (opts.cause && !this.cause) {
|
|
103
|
+
this.cause = opts.cause;
|
|
231
104
|
}
|
|
232
|
-
return str;
|
|
233
105
|
}
|
|
234
106
|
toJSON() {
|
|
235
|
-
|
|
236
|
-
name: this.name,
|
|
237
|
-
code: this.code,
|
|
107
|
+
const obj = {
|
|
238
108
|
message: this.message,
|
|
239
|
-
|
|
240
|
-
severity: this.severity,
|
|
241
|
-
httpStatus: this.httpStatus,
|
|
242
|
-
metadata: this.metadata,
|
|
243
|
-
context: this.context,
|
|
244
|
-
stack: this.stack
|
|
109
|
+
statusCode: sanitizeStatusCode(this.statusCode, 500)
|
|
245
110
|
};
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
return error instanceof SilgiError || typeof error === "object" && error !== null && "name" in error && error.name === "SilgiError";
|
|
249
|
-
}
|
|
250
|
-
static from(error) {
|
|
251
|
-
if (error instanceof SilgiError) {
|
|
252
|
-
return error;
|
|
111
|
+
if (this.statusMessage) {
|
|
112
|
+
obj.statusMessage = sanitizeStatusMessage(this.statusMessage);
|
|
253
113
|
}
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
114
|
+
if (this.data !== void 0) {
|
|
115
|
+
obj.data = this.data;
|
|
116
|
+
}
|
|
117
|
+
return obj;
|
|
258
118
|
}
|
|
259
119
|
}
|
|
260
|
-
function
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
function parseURI(uri, uris) {
|
|
265
|
-
if (!uri) {
|
|
266
|
-
throw ErrorFactory.create({
|
|
267
|
-
message: "URI cannot be empty",
|
|
268
|
-
httpStatus: HttpStatus.BAD_REQUEST,
|
|
269
|
-
context: { uri }
|
|
270
|
-
});
|
|
271
|
-
}
|
|
272
|
-
if (!uris) {
|
|
273
|
-
throw ErrorFactory.create({
|
|
274
|
-
message: "URIs configuration is not provided",
|
|
275
|
-
httpStatus: HttpStatus.INTERNAL_SERVER_ERROR,
|
|
276
|
-
context: { uri }
|
|
277
|
-
});
|
|
120
|
+
function createError(input) {
|
|
121
|
+
if (typeof input === "string") {
|
|
122
|
+
return new SilgiError(input);
|
|
278
123
|
}
|
|
279
|
-
const
|
|
280
|
-
const
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
const methodName = method || parts[2];
|
|
289
|
-
const actionName = method ? parts[2] : parts[3];
|
|
290
|
-
if (!namespaceName || !serviceName || !methodName || !actionName) {
|
|
291
|
-
throw ErrorFactory.create({
|
|
292
|
-
message: "Invalid URI format: Insufficient path segments",
|
|
293
|
-
httpStatus: HttpStatus.BAD_REQUEST,
|
|
294
|
-
context: {
|
|
295
|
-
uri,
|
|
296
|
-
cleanUri,
|
|
297
|
-
partsLength: parts.length,
|
|
298
|
-
method,
|
|
299
|
-
namespaceName,
|
|
300
|
-
serviceName,
|
|
301
|
-
methodName,
|
|
302
|
-
actionName
|
|
303
|
-
}
|
|
304
|
-
});
|
|
305
|
-
}
|
|
306
|
-
const baseUri = `${namespaceName}/${serviceName}/${methodName}/${actionName}`;
|
|
307
|
-
const paramStartIndex = method ? 3 : 4;
|
|
308
|
-
const parameters = parts.slice(paramStartIndex);
|
|
309
|
-
const normalizedUri = method ? `${namespaceName}/${serviceName}/${method}/${actionName}${parameters.length ? `/${parameters.join("/")}` : ""}` : cleanUri;
|
|
310
|
-
const template = uris[baseUri];
|
|
311
|
-
if (template === void 0) {
|
|
312
|
-
throw ErrorFactory.create({
|
|
313
|
-
message: "No route found for URI",
|
|
314
|
-
httpStatus: HttpStatus.NOT_FOUND,
|
|
315
|
-
context: {
|
|
316
|
-
uri,
|
|
317
|
-
baseUri
|
|
318
|
-
}
|
|
319
|
-
});
|
|
320
|
-
}
|
|
321
|
-
if (template === "") {
|
|
322
|
-
if (parameters.length > 0) {
|
|
323
|
-
throw ErrorFactory.create({
|
|
324
|
-
message: "No parameters expected for this route",
|
|
325
|
-
httpStatus: HttpStatus.BAD_REQUEST,
|
|
326
|
-
context: {
|
|
327
|
-
uri,
|
|
328
|
-
baseUri,
|
|
329
|
-
extraParams: parameters
|
|
124
|
+
const cause = input.cause;
|
|
125
|
+
const err = new SilgiError(input.message ?? input.statusMessage ?? "", {
|
|
126
|
+
cause: cause || input
|
|
127
|
+
});
|
|
128
|
+
if (hasProp(input, "stack")) {
|
|
129
|
+
try {
|
|
130
|
+
Object.defineProperty(err, "stack", {
|
|
131
|
+
get() {
|
|
132
|
+
return input.stack;
|
|
330
133
|
}
|
|
331
134
|
});
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
return {
|
|
337
|
-
namespaceName,
|
|
338
|
-
serviceName,
|
|
339
|
-
methodName,
|
|
340
|
-
actionName,
|
|
341
|
-
raw: normalizedUri,
|
|
342
|
-
parts: [namespaceName, serviceName, methodName, actionName],
|
|
343
|
-
routerParams: {},
|
|
344
|
-
query,
|
|
345
|
-
uri: baseUri
|
|
346
|
-
};
|
|
347
|
-
}
|
|
348
|
-
const routeTemplate = typeof template === "string" ? template : template.pattern;
|
|
349
|
-
const validators = typeof template === "string" ? void 0 : template.validators;
|
|
350
|
-
const routerParams = {};
|
|
351
|
-
const templateParts = routeTemplate.split("/").filter(Boolean);
|
|
352
|
-
const paramValues = parameters;
|
|
353
|
-
let valueIndex = 0;
|
|
354
|
-
templateParts.forEach((part) => {
|
|
355
|
-
if (part.startsWith(":")) {
|
|
356
|
-
const paramName = part.substring(1);
|
|
357
|
-
const paramValue = paramValues[valueIndex];
|
|
358
|
-
if (validators?.[paramName] && paramValue) {
|
|
359
|
-
if (!validators[paramName](paramValue)) {
|
|
360
|
-
throw ErrorFactory.create({
|
|
361
|
-
message: "Invalid value for parameter",
|
|
362
|
-
httpStatus: HttpStatus.UNPROCESSABLE_ENTITY,
|
|
363
|
-
context: {
|
|
364
|
-
uri,
|
|
365
|
-
paramName,
|
|
366
|
-
paramValue,
|
|
367
|
-
validatorName: paramName
|
|
368
|
-
}
|
|
369
|
-
});
|
|
370
|
-
}
|
|
135
|
+
} catch {
|
|
136
|
+
try {
|
|
137
|
+
err.stack = input.stack;
|
|
138
|
+
} catch {
|
|
371
139
|
}
|
|
372
|
-
routerParams[paramName] = paramValue || void 0;
|
|
373
|
-
valueIndex++;
|
|
374
|
-
} else if (part && part === paramValues[valueIndex]) {
|
|
375
|
-
valueIndex++;
|
|
376
|
-
}
|
|
377
|
-
});
|
|
378
|
-
return {
|
|
379
|
-
namespaceName,
|
|
380
|
-
serviceName,
|
|
381
|
-
methodName,
|
|
382
|
-
actionName,
|
|
383
|
-
raw: normalizedUri,
|
|
384
|
-
parts: [namespaceName, serviceName, methodName, actionName],
|
|
385
|
-
routerParams,
|
|
386
|
-
query: method ? void 0 : query,
|
|
387
|
-
uri: baseUri
|
|
388
|
-
};
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
async function findAction(silgi, uri) {
|
|
392
|
-
const { parts } = parseURI(uri, silgi.uris);
|
|
393
|
-
let result = silgi.services;
|
|
394
|
-
for (const part of parts) {
|
|
395
|
-
if (result && Object.prototype.hasOwnProperty.call(result, part)) {
|
|
396
|
-
result = Object.assign({}, result[part]);
|
|
397
|
-
} else {
|
|
398
|
-
silgi.logger.fail("Action not found:", `${parts.join("/")}`);
|
|
399
|
-
continue;
|
|
400
140
|
}
|
|
401
141
|
}
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
console.error(`Invalid URI format for key "${key}". URI must have exactly 4 segments in format: namespace/service/method/action`);
|
|
409
|
-
continue;
|
|
410
|
-
}
|
|
411
|
-
const [namespace, service, method, action] = segments;
|
|
412
|
-
if (!namespace || !service || !method || !action) {
|
|
413
|
-
console.error(`Invalid URI segments for key "${key}". All segments must be non-empty`);
|
|
414
|
-
continue;
|
|
415
|
-
}
|
|
416
|
-
const handler = await findAction(silgi, key);
|
|
417
|
-
silgi.scannedHandlers.set(key, handler);
|
|
142
|
+
if (input.data) {
|
|
143
|
+
err.data = input.data;
|
|
144
|
+
}
|
|
145
|
+
const statusCode = input.statusCode ?? input.status ?? cause?.statusCode ?? cause?.status;
|
|
146
|
+
if (typeof statusCode === "number") {
|
|
147
|
+
err.statusCode = sanitizeStatusCode(statusCode);
|
|
418
148
|
}
|
|
149
|
+
const statusMessage = input.statusMessage ?? input.statusText ?? cause?.statusMessage ?? cause?.statusText;
|
|
150
|
+
if (statusMessage) {
|
|
151
|
+
err.statusMessage = sanitizeStatusMessage(statusMessage);
|
|
152
|
+
}
|
|
153
|
+
const fatal = input.fatal ?? cause?.fatal;
|
|
154
|
+
if (fatal !== void 0) {
|
|
155
|
+
err.fatal = fatal;
|
|
156
|
+
}
|
|
157
|
+
const unhandled = input.unhandled ?? cause?.unhandled;
|
|
158
|
+
if (unhandled !== void 0) {
|
|
159
|
+
err.unhandled = unhandled;
|
|
160
|
+
}
|
|
161
|
+
return err;
|
|
162
|
+
}
|
|
163
|
+
function isError(input) {
|
|
164
|
+
return input?.constructor?.__silgi_error__ === true;
|
|
419
165
|
}
|
|
420
166
|
|
|
421
167
|
function replaceRuntimeValues(obj, runtime) {
|
|
@@ -463,7 +209,7 @@ function useSilgiStorage(base = "/memory:cache") {
|
|
|
463
209
|
}
|
|
464
210
|
async function generateStorageKey(params) {
|
|
465
211
|
const {
|
|
466
|
-
|
|
212
|
+
url,
|
|
467
213
|
input,
|
|
468
214
|
keyGenerator,
|
|
469
215
|
requestId,
|
|
@@ -473,18 +219,16 @@ async function generateStorageKey(params) {
|
|
|
473
219
|
const parts = [
|
|
474
220
|
cacheScopePrefix,
|
|
475
221
|
// Always include scope prefix first
|
|
476
|
-
|
|
477
|
-
operation.serviceName,
|
|
478
|
-
operation.methodName
|
|
222
|
+
url.raw
|
|
479
223
|
].filter(Boolean);
|
|
480
224
|
if (storageOptions?.scope === "request") {
|
|
481
225
|
if (!requestId) {
|
|
482
|
-
throw
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
226
|
+
throw createError({
|
|
227
|
+
statusCode: 400,
|
|
228
|
+
statusMessage: "Request ID is required for request-scoped cache",
|
|
229
|
+
data: {
|
|
486
230
|
requestId,
|
|
487
|
-
|
|
231
|
+
url,
|
|
488
232
|
input,
|
|
489
233
|
storageOptions,
|
|
490
234
|
keyGenerator
|
|
@@ -515,12 +259,11 @@ async function runSilgiPlugins(silgi) {
|
|
|
515
259
|
async function createSilgi(config) {
|
|
516
260
|
const hooks = createHooks();
|
|
517
261
|
const silgi = {
|
|
518
|
-
|
|
262
|
+
router: void 0,
|
|
263
|
+
routerPrefixs: [],
|
|
264
|
+
schemas: config.schemas ?? {},
|
|
519
265
|
services: config.services ?? {},
|
|
520
266
|
shared: config.shared ?? void 0,
|
|
521
|
-
uris: config.uris ?? {},
|
|
522
|
-
modulesURIs: config.modulesURIs ?? {},
|
|
523
|
-
scannedHandlers: /* @__PURE__ */ new Map(),
|
|
524
267
|
plugins: config.plugins ?? [],
|
|
525
268
|
framework: config.framework ?? void 0,
|
|
526
269
|
storage: config.options.putStorage ?? void 0,
|
|
@@ -538,15 +281,40 @@ async function createSilgi(config) {
|
|
|
538
281
|
tag: "silgi"
|
|
539
282
|
})).withTag("silgi"),
|
|
540
283
|
captureError: config.captureError ?? (() => {
|
|
541
|
-
})
|
|
542
|
-
routeRules: void 0
|
|
284
|
+
})
|
|
543
285
|
};
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
286
|
+
if (!silgi.router) {
|
|
287
|
+
silgi.router = createRouter();
|
|
288
|
+
}
|
|
289
|
+
for (const route in silgi.services) {
|
|
290
|
+
const routeParts = route.split("/").filter(Boolean);
|
|
291
|
+
if (routeParts.length > 0) {
|
|
292
|
+
const prefix = `/${routeParts[0]}`;
|
|
293
|
+
if (!silgi.routerPrefixs.includes(prefix)) {
|
|
294
|
+
silgi.routerPrefixs.push(prefix);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
const methods = silgi.services[route];
|
|
298
|
+
for (const method in methods) {
|
|
299
|
+
const methodObject = methods[method];
|
|
300
|
+
if (methodObject) {
|
|
301
|
+
const _method = (method || "").toUpperCase();
|
|
302
|
+
const schemaWrapper = silgi.schemas?.[route]?.[method];
|
|
303
|
+
if (!schemaWrapper) {
|
|
304
|
+
silgi.logger.warn(`Schema not found for ${route}`);
|
|
305
|
+
continue;
|
|
306
|
+
}
|
|
307
|
+
addRoute(silgi.router, _method, route, {
|
|
308
|
+
method: _method,
|
|
309
|
+
route,
|
|
310
|
+
setup: methodObject,
|
|
311
|
+
schema: schemaWrapper
|
|
312
|
+
});
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
}
|
|
547
316
|
silgi.hooks.addHooks(silgi.options.hooks);
|
|
548
317
|
await runSilgiPlugins(silgi);
|
|
549
|
-
await scanAction(silgi);
|
|
550
318
|
if (!silgi.storage) {
|
|
551
319
|
silgi.storage = await createStorage(silgi);
|
|
552
320
|
}
|
|
@@ -569,79 +337,275 @@ async function createSilgi(config) {
|
|
|
569
337
|
return silgi;
|
|
570
338
|
}
|
|
571
339
|
|
|
572
|
-
|
|
340
|
+
class _SilgiEvent {
|
|
341
|
+
static __is_event__ = true;
|
|
342
|
+
req;
|
|
343
|
+
url;
|
|
344
|
+
context;
|
|
345
|
+
_res;
|
|
346
|
+
constructor(req, context) {
|
|
347
|
+
this.context = context || new EmptyObject();
|
|
348
|
+
this.req = req;
|
|
349
|
+
const _url = req._url;
|
|
350
|
+
this.url = _url && _url instanceof URL ? _url : new FastURL(req.url);
|
|
351
|
+
}
|
|
352
|
+
get res() {
|
|
353
|
+
if (!this._res) {
|
|
354
|
+
this._res = new SilgiEventResponse();
|
|
355
|
+
}
|
|
356
|
+
return this._res;
|
|
357
|
+
}
|
|
358
|
+
toString() {
|
|
359
|
+
return `[${this.req.method}] ${this.req.url}`;
|
|
360
|
+
}
|
|
361
|
+
toJSON() {
|
|
362
|
+
return this.toString();
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
class SilgiEventResponse {
|
|
366
|
+
status;
|
|
367
|
+
// statusText?: string;
|
|
368
|
+
_headers;
|
|
369
|
+
get headers() {
|
|
370
|
+
if (!this._headers) {
|
|
371
|
+
this._headers = new Headers();
|
|
372
|
+
}
|
|
373
|
+
return this._headers;
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
async function parseRequestInput(req) {
|
|
378
|
+
const contentType = req.headers.get("content-type") || "";
|
|
379
|
+
if (contentType.startsWith("application/json")) {
|
|
380
|
+
const text = await req.text();
|
|
381
|
+
if (!text)
|
|
382
|
+
return void 0;
|
|
383
|
+
return JSON.parse(text);
|
|
384
|
+
}
|
|
385
|
+
if (contentType.startsWith("text/")) {
|
|
386
|
+
return await req.text();
|
|
387
|
+
}
|
|
388
|
+
if (contentType.startsWith("application/octet-stream")) {
|
|
389
|
+
return new File([await req.arrayBuffer()], "uploaded.bin", { type: contentType });
|
|
390
|
+
}
|
|
391
|
+
return await req.text();
|
|
392
|
+
}
|
|
393
|
+
function wrapBodyAndContentType(options) {
|
|
394
|
+
let { body, headers } = options;
|
|
395
|
+
const setContentType = (type) => {
|
|
396
|
+
if (!headers)
|
|
397
|
+
headers = {};
|
|
398
|
+
if (headers instanceof Headers) {
|
|
399
|
+
if (!headers.has("Content-Type"))
|
|
400
|
+
headers.set("Content-Type", type);
|
|
401
|
+
} else if (Array.isArray(headers)) {
|
|
402
|
+
if (!headers.some(([k]) => k.toLowerCase() === "content-type")) {
|
|
403
|
+
headers.push(["Content-Type", type]);
|
|
404
|
+
}
|
|
405
|
+
} else {
|
|
406
|
+
if (!("content-type" in headers)) {
|
|
407
|
+
headers["Content-Type"] = type;
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
};
|
|
411
|
+
if (typeof File !== "undefined" && body instanceof File) {
|
|
412
|
+
body = new Blob([body], { type: body.type });
|
|
413
|
+
setContentType(body.type || "application/octet-stream");
|
|
414
|
+
} else if (body instanceof Blob) {
|
|
415
|
+
setContentType(body.type || "application/octet-stream");
|
|
416
|
+
} else if (body && typeof body === "object") {
|
|
417
|
+
body = new Blob([JSON.stringify(body)], { type: "application/json" });
|
|
418
|
+
setContentType("application/json");
|
|
419
|
+
} else if (typeof body === "string") {
|
|
420
|
+
body = new Blob([body], { type: "text/plain" });
|
|
421
|
+
setContentType("text/plain");
|
|
422
|
+
}
|
|
423
|
+
return { body, headers };
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
const plusRegex = /\+/g;
|
|
427
|
+
function parseQuery(input) {
|
|
428
|
+
const params = new EmptyObject();
|
|
429
|
+
if (!input || input === "?") {
|
|
430
|
+
return params;
|
|
431
|
+
}
|
|
432
|
+
const inputLength = input.length;
|
|
433
|
+
let key = "";
|
|
434
|
+
let value = "";
|
|
435
|
+
let startingIndex = -1;
|
|
436
|
+
let equalityIndex = -1;
|
|
437
|
+
let shouldDecodeKey = false;
|
|
438
|
+
let shouldDecodeValue = false;
|
|
439
|
+
let keyHasPlus = false;
|
|
440
|
+
let valueHasPlus = false;
|
|
441
|
+
let hasBothKeyValuePair = false;
|
|
442
|
+
let c = 0;
|
|
443
|
+
for (let i = 0; i < inputLength + 1; i++) {
|
|
444
|
+
c = i === inputLength ? 38 : input.charCodeAt(i);
|
|
445
|
+
switch (c) {
|
|
446
|
+
case 38: {
|
|
447
|
+
hasBothKeyValuePair = equalityIndex > startingIndex;
|
|
448
|
+
if (!hasBothKeyValuePair) {
|
|
449
|
+
equalityIndex = i;
|
|
450
|
+
}
|
|
451
|
+
key = input.slice(startingIndex + 1, equalityIndex);
|
|
452
|
+
if (hasBothKeyValuePair || key.length > 0) {
|
|
453
|
+
if (keyHasPlus) {
|
|
454
|
+
key = key.replace(plusRegex, " ");
|
|
455
|
+
}
|
|
456
|
+
if (shouldDecodeKey) {
|
|
457
|
+
try {
|
|
458
|
+
key = decodeURIComponent(key);
|
|
459
|
+
} catch {
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
if (hasBothKeyValuePair) {
|
|
463
|
+
value = input.slice(equalityIndex + 1, i);
|
|
464
|
+
if (valueHasPlus) {
|
|
465
|
+
value = value.replace(plusRegex, " ");
|
|
466
|
+
}
|
|
467
|
+
if (shouldDecodeValue) {
|
|
468
|
+
try {
|
|
469
|
+
value = decodeURIComponent(value);
|
|
470
|
+
} catch {
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
const currentValue = params[key];
|
|
475
|
+
if (currentValue === void 0) {
|
|
476
|
+
params[key] = value;
|
|
477
|
+
} else {
|
|
478
|
+
if (Array.isArray(currentValue)) {
|
|
479
|
+
currentValue.push(value);
|
|
480
|
+
} else {
|
|
481
|
+
params[key] = [currentValue, value];
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
value = "";
|
|
486
|
+
startingIndex = i;
|
|
487
|
+
equalityIndex = i;
|
|
488
|
+
shouldDecodeKey = false;
|
|
489
|
+
shouldDecodeValue = false;
|
|
490
|
+
keyHasPlus = false;
|
|
491
|
+
valueHasPlus = false;
|
|
492
|
+
break;
|
|
493
|
+
}
|
|
494
|
+
case 61: {
|
|
495
|
+
if (equalityIndex <= startingIndex) {
|
|
496
|
+
equalityIndex = i;
|
|
497
|
+
} else {
|
|
498
|
+
shouldDecodeValue = true;
|
|
499
|
+
}
|
|
500
|
+
break;
|
|
501
|
+
}
|
|
502
|
+
case 43: {
|
|
503
|
+
if (equalityIndex > startingIndex) {
|
|
504
|
+
valueHasPlus = true;
|
|
505
|
+
} else {
|
|
506
|
+
keyHasPlus = true;
|
|
507
|
+
}
|
|
508
|
+
break;
|
|
509
|
+
}
|
|
510
|
+
case 37: {
|
|
511
|
+
if (equalityIndex > startingIndex) {
|
|
512
|
+
shouldDecodeValue = true;
|
|
513
|
+
} else {
|
|
514
|
+
shouldDecodeKey = true;
|
|
515
|
+
}
|
|
516
|
+
break;
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
return params;
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
function getQuery(event) {
|
|
524
|
+
return parseQuery(event.url.search.slice(1));
|
|
525
|
+
}
|
|
526
|
+
function getRouterParams(event, opts = {}) {
|
|
527
|
+
let params = event.context.params || {};
|
|
528
|
+
if (opts.decode) {
|
|
529
|
+
params = { ...params };
|
|
530
|
+
for (const key in params) {
|
|
531
|
+
params[key] = decodeURIComponent(params[key]);
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
return params;
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
function createRoute(params) {
|
|
573
538
|
return {
|
|
574
|
-
|
|
575
|
-
|
|
539
|
+
[params.route]: {
|
|
540
|
+
...params
|
|
576
541
|
}
|
|
577
542
|
};
|
|
578
543
|
}
|
|
579
|
-
|
|
580
|
-
const
|
|
581
|
-
const
|
|
582
|
-
|
|
544
|
+
function getUrlPrefix(path, method) {
|
|
545
|
+
const parse = path.includes("://") ? new FastURL(path) : new URL(path, "http://localhost");
|
|
546
|
+
const cleanedPath = parse.pathname + parse.search + parse.hash;
|
|
547
|
+
const segments = cleanedPath.split("/").filter(Boolean);
|
|
548
|
+
if (segments.length < 2) {
|
|
549
|
+
throw createError({
|
|
550
|
+
statusCode: 400,
|
|
551
|
+
statusMessage: "Invalid URL format",
|
|
552
|
+
data: { cleanedPath }
|
|
553
|
+
});
|
|
554
|
+
}
|
|
555
|
+
const prefix = `/${segments[0]}`;
|
|
556
|
+
const namespace = segments[1];
|
|
557
|
+
return {
|
|
558
|
+
namespaceName: namespace,
|
|
559
|
+
prefixName: prefix,
|
|
560
|
+
raw: path,
|
|
561
|
+
path: cleanedPath,
|
|
562
|
+
methodName: method || "GET"
|
|
583
563
|
};
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
async function orchestrate(route, event) {
|
|
567
|
+
const input = await parseRequestInput(event.req);
|
|
568
|
+
const silgiCtx = useSilgi();
|
|
569
|
+
const silgiURL = getUrlPrefix(route.route, event.req.method);
|
|
584
570
|
try {
|
|
585
|
-
const
|
|
586
|
-
|
|
587
|
-
throw ErrorFactory.create({ message: "Invalid URI", httpStatus: HttpStatus.BAD_REQUEST });
|
|
588
|
-
}
|
|
571
|
+
const routerParams = getRouterParams(event);
|
|
572
|
+
const queryParams = getQuery(event);
|
|
589
573
|
let success = false;
|
|
590
574
|
let cached = false;
|
|
591
575
|
let result;
|
|
592
|
-
const
|
|
593
|
-
|
|
594
|
-
throw ErrorFactory.create({
|
|
595
|
-
message: "execute not found",
|
|
596
|
-
httpStatus: HttpStatus.NOT_FOUND,
|
|
597
|
-
context: {
|
|
598
|
-
uri: uriString
|
|
599
|
-
}
|
|
600
|
-
});
|
|
601
|
-
}
|
|
602
|
-
await silgiCtx.callHook("execute:before", {
|
|
603
|
-
operation,
|
|
604
|
-
input,
|
|
605
|
-
event,
|
|
606
|
-
modules: handler.modules,
|
|
607
|
-
source,
|
|
608
|
-
config
|
|
609
|
-
});
|
|
610
|
-
if (config.returnNull) {
|
|
611
|
-
return result;
|
|
612
|
-
}
|
|
613
|
-
const cacheData = await cacheExecute(input, operation, handler, event);
|
|
576
|
+
const setup = route.setup;
|
|
577
|
+
const cacheData = await cacheExecute(input, route, silgiURL, event);
|
|
614
578
|
if (cacheData?.success) {
|
|
615
579
|
result = cacheData.data;
|
|
616
580
|
success = cacheData.success;
|
|
617
581
|
cached = cacheData.cached;
|
|
618
582
|
} else {
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
583
|
+
silgiCtx.shared.$fetch = silgiFetch;
|
|
584
|
+
result = await setup?.handler(
|
|
585
|
+
{
|
|
586
|
+
args: input,
|
|
587
|
+
parameters: {
|
|
588
|
+
query: queryParams,
|
|
589
|
+
path: routerParams
|
|
590
|
+
}
|
|
591
|
+
},
|
|
626
592
|
silgiCtx.shared,
|
|
627
593
|
event,
|
|
628
|
-
source
|
|
594
|
+
event.context.source
|
|
629
595
|
);
|
|
630
596
|
success = true;
|
|
631
597
|
}
|
|
632
598
|
await silgiCtx.callHook("execute:after", {
|
|
633
|
-
|
|
599
|
+
url: silgiURL,
|
|
634
600
|
input,
|
|
635
601
|
event,
|
|
636
602
|
result,
|
|
637
603
|
success,
|
|
638
|
-
|
|
639
|
-
modules: handler.modules,
|
|
640
|
-
config
|
|
604
|
+
modules: setup.modules
|
|
641
605
|
});
|
|
642
606
|
if (!cached) {
|
|
643
|
-
if (success && cacheData?.cachedKey &&
|
|
644
|
-
await useSilgiStorage(
|
|
607
|
+
if (success && cacheData?.cachedKey && setup.storage) {
|
|
608
|
+
await useSilgiStorage(setup.storage.base).setItem(cacheData.cachedKey, result, setup.storage.options);
|
|
645
609
|
}
|
|
646
610
|
}
|
|
647
611
|
return result;
|
|
@@ -649,30 +613,38 @@ async function execute(uriString, input, event, source, queryParams) {
|
|
|
649
613
|
await silgiCtx.callHook("execute:error", {
|
|
650
614
|
input,
|
|
651
615
|
event,
|
|
652
|
-
source,
|
|
653
616
|
error: err instanceof Error ? err : new Error(String(err)),
|
|
654
|
-
|
|
655
|
-
config
|
|
656
|
-
});
|
|
657
|
-
silgiCtx.captureError(silgiCtx, SilgiError.from(err), {
|
|
658
|
-
event,
|
|
659
|
-
tags: ["execute"]
|
|
617
|
+
url: silgiURL
|
|
660
618
|
});
|
|
619
|
+
silgiCtx.captureError(
|
|
620
|
+
silgiCtx,
|
|
621
|
+
createError({
|
|
622
|
+
message: err instanceof Error ? err.message : String(err),
|
|
623
|
+
statusCode: 500,
|
|
624
|
+
statusMessage: "Internal Server Error",
|
|
625
|
+
cause: err
|
|
626
|
+
}),
|
|
627
|
+
{
|
|
628
|
+
event,
|
|
629
|
+
tags: ["execute"]
|
|
630
|
+
}
|
|
631
|
+
);
|
|
661
632
|
throw err;
|
|
662
633
|
}
|
|
663
634
|
}
|
|
664
|
-
async function cacheExecute(input,
|
|
665
|
-
|
|
635
|
+
async function cacheExecute(input, route, silgiURL, event) {
|
|
636
|
+
const setup = route.setup;
|
|
637
|
+
if (!setup.storage)
|
|
666
638
|
return;
|
|
667
|
-
const cacheKey =
|
|
668
|
-
|
|
639
|
+
const cacheKey = setup.storage ? await generateStorageKey({
|
|
640
|
+
url: silgiURL,
|
|
669
641
|
input,
|
|
670
|
-
keyGenerator:
|
|
671
|
-
storageOptions:
|
|
642
|
+
keyGenerator: setup.storage.key,
|
|
643
|
+
storageOptions: setup.storage,
|
|
672
644
|
requestId: event?.requestId
|
|
673
645
|
}) : null;
|
|
674
646
|
if (cacheKey) {
|
|
675
|
-
const cachedResult = await useSilgiStorage(
|
|
647
|
+
const cachedResult = await useSilgiStorage(setup.storage.base).getItem(cacheKey);
|
|
676
648
|
if (cachedResult !== null) {
|
|
677
649
|
return {
|
|
678
650
|
success: true,
|
|
@@ -690,32 +662,235 @@ async function cacheExecute(input, operation, handler, event) {
|
|
|
690
662
|
};
|
|
691
663
|
}
|
|
692
664
|
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
665
|
+
const kNotFound = /* @__PURE__ */ Symbol.for("silgi.notFound");
|
|
666
|
+
const kHandled = /* @__PURE__ */ Symbol.for("silgi.handled");
|
|
667
|
+
function handleResponse(val, event, config) {
|
|
668
|
+
if (val && val instanceof Promise) {
|
|
669
|
+
return val.catch((error) => error).then((resolvedVal) => handleResponse(resolvedVal, event, config));
|
|
670
|
+
}
|
|
671
|
+
const response = prepareResponse(val, event, config);
|
|
672
|
+
if (response instanceof Promise) {
|
|
673
|
+
return handleResponse(response, event, config);
|
|
674
|
+
}
|
|
675
|
+
const { onBeforeResponse } = config;
|
|
676
|
+
return onBeforeResponse ? Promise.resolve(onBeforeResponse(event, response)).then(() => response) : response;
|
|
677
|
+
}
|
|
678
|
+
function prepareResponse(val, event, config, nested) {
|
|
679
|
+
if (val === kHandled) {
|
|
680
|
+
return new FastResponse(null);
|
|
681
|
+
}
|
|
682
|
+
if (val === kNotFound) {
|
|
683
|
+
val = createError({
|
|
684
|
+
statusCode: 404,
|
|
685
|
+
statusMessage: `Cannot find any route matching [${event.req.method}] ${event.url}`
|
|
707
686
|
});
|
|
708
|
-
|
|
709
|
-
|
|
687
|
+
}
|
|
688
|
+
if (val && val instanceof Error) {
|
|
689
|
+
const error = createError(val);
|
|
690
|
+
const { onError } = config;
|
|
691
|
+
return onError && !nested ? Promise.resolve(onError(error, event)).catch((error2) => error2).then((newVal) => prepareResponse(newVal ?? val, event, config, true)) : errorResponse(error, config.debug);
|
|
692
|
+
}
|
|
693
|
+
const eventHeaders = event.res._headers;
|
|
694
|
+
if (!(val instanceof Response)) {
|
|
695
|
+
const res = prepareResponseBody(val, event, config);
|
|
696
|
+
const status = event.res.status;
|
|
697
|
+
return new FastResponse(
|
|
698
|
+
nullBody(event.req.method, status) ? null : res.body,
|
|
699
|
+
{
|
|
700
|
+
status,
|
|
701
|
+
statusText: event.res.statusText,
|
|
702
|
+
headers: res.headers && eventHeaders ? mergeHeaders(res.headers, eventHeaders) : res.headers || eventHeaders
|
|
703
|
+
}
|
|
704
|
+
);
|
|
705
|
+
}
|
|
706
|
+
if (!eventHeaders) {
|
|
707
|
+
return val;
|
|
708
|
+
}
|
|
709
|
+
return new FastResponse(
|
|
710
|
+
nullBody(event.req.method, val.status) ? null : val.body,
|
|
711
|
+
{
|
|
712
|
+
status: val.status,
|
|
713
|
+
statusText: val.statusText,
|
|
714
|
+
headers: mergeHeaders(eventHeaders, val.headers)
|
|
715
|
+
}
|
|
716
|
+
);
|
|
717
|
+
}
|
|
718
|
+
function nullBody(method, status) {
|
|
719
|
+
return method === "HEAD" || status === 100 || status === 101 || status === 102 || status === 204 || status === 205 || status === 304;
|
|
720
|
+
}
|
|
721
|
+
function mergeHeaders(base, merge) {
|
|
722
|
+
const mergedHeaders = new Headers(base);
|
|
723
|
+
for (const [name, value] of merge) {
|
|
724
|
+
if (name === "set-cookie") {
|
|
725
|
+
mergedHeaders.append(name, value);
|
|
726
|
+
} else {
|
|
727
|
+
mergedHeaders.set(name, value);
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
return mergedHeaders;
|
|
731
|
+
}
|
|
732
|
+
const emptyHeaders = new Headers({ "content-length": "0" });
|
|
733
|
+
const jsonHeaders = new Headers({
|
|
734
|
+
"content-type": "application/json;charset=UTF-8"
|
|
735
|
+
});
|
|
736
|
+
function prepareResponseBody(val, event, config) {
|
|
737
|
+
if (val === null || val === void 0) {
|
|
738
|
+
return { body: "", headers: emptyHeaders };
|
|
739
|
+
}
|
|
740
|
+
const valType = typeof val;
|
|
741
|
+
if (valType === "string") {
|
|
742
|
+
return { body: val };
|
|
743
|
+
}
|
|
744
|
+
if (val instanceof Uint8Array) {
|
|
745
|
+
event.res.headers.set("content-length", val.byteLength.toString());
|
|
746
|
+
return { body: val };
|
|
747
|
+
}
|
|
748
|
+
if (isJSONSerializable(val, valType)) {
|
|
749
|
+
return {
|
|
750
|
+
body: JSON.stringify(val, void 0, config.debug ? 2 : void 0),
|
|
751
|
+
headers: jsonHeaders
|
|
752
|
+
};
|
|
753
|
+
}
|
|
754
|
+
if (valType === "bigint") {
|
|
755
|
+
return { body: val.toString(), headers: jsonHeaders };
|
|
756
|
+
}
|
|
757
|
+
if (val instanceof Blob) {
|
|
758
|
+
const headers = {
|
|
759
|
+
"content-type": val.type,
|
|
760
|
+
"content-length": val.size.toString()
|
|
761
|
+
};
|
|
762
|
+
if ("name" in val) {
|
|
763
|
+
const filename = encodeURIComponent(val.name);
|
|
764
|
+
headers["content-disposition"] = `filename="${filename}"; filename*=UTF-8''${filename}`;
|
|
765
|
+
}
|
|
766
|
+
return { body: val.stream(), headers };
|
|
767
|
+
}
|
|
768
|
+
if (valType === "symbol") {
|
|
769
|
+
return { body: val.toString() };
|
|
770
|
+
}
|
|
771
|
+
if (valType === "function") {
|
|
772
|
+
return { body: `${val.name}()` };
|
|
773
|
+
}
|
|
774
|
+
return { body: val };
|
|
775
|
+
}
|
|
776
|
+
function errorResponse(error, debug) {
|
|
777
|
+
return new FastResponse(
|
|
778
|
+
JSON.stringify(
|
|
779
|
+
{
|
|
780
|
+
statusCode: error.statusCode,
|
|
781
|
+
statusMessage: error.statusMessage,
|
|
782
|
+
data: error.data,
|
|
783
|
+
stack: debug && error.stack ? error.stack.split("\n").map((l) => l.trim()) : void 0
|
|
784
|
+
},
|
|
785
|
+
null,
|
|
786
|
+
2
|
|
787
|
+
),
|
|
788
|
+
{
|
|
789
|
+
status: error.statusCode,
|
|
790
|
+
statusText: error.statusMessage,
|
|
791
|
+
headers: {
|
|
792
|
+
"content-type": "application/json; charset=utf-8"
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
);
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
function getHeader(name, headers) {
|
|
799
|
+
if (!headers) {
|
|
800
|
+
return;
|
|
801
|
+
}
|
|
802
|
+
if (headers instanceof Headers) {
|
|
803
|
+
return headers.get(name);
|
|
804
|
+
}
|
|
805
|
+
const lName = name.toLowerCase();
|
|
806
|
+
if (Array.isArray(headers)) {
|
|
807
|
+
return headers.find(
|
|
808
|
+
(h) => h[0] === name || lName === h[0].toLowerCase()
|
|
809
|
+
)?.[1];
|
|
810
|
+
}
|
|
811
|
+
return headers[name] || headers[lName];
|
|
812
|
+
}
|
|
813
|
+
function substitutePathParams(path, pathParams) {
|
|
814
|
+
if (!pathParams)
|
|
815
|
+
return path;
|
|
816
|
+
return path.replace(/:(\w+)/g, (_, key) => {
|
|
817
|
+
if (key in pathParams) {
|
|
818
|
+
return encodeURIComponent(pathParams[key]);
|
|
819
|
+
}
|
|
820
|
+
return `:${key}`;
|
|
821
|
+
});
|
|
710
822
|
}
|
|
711
|
-
function
|
|
712
|
-
return
|
|
823
|
+
function createSilgiCore(event) {
|
|
824
|
+
return {
|
|
825
|
+
resolve: async (path, method, context) => {
|
|
826
|
+
const silgiCtx = useSilgi();
|
|
827
|
+
if (!silgiCtx.router) {
|
|
828
|
+
return void 0;
|
|
829
|
+
}
|
|
830
|
+
const routeStr = substitutePathParams(String(path), context?.pathParams);
|
|
831
|
+
const methodStr = String(method);
|
|
832
|
+
const match = findRoute(silgiCtx.router, methodStr, routeStr);
|
|
833
|
+
return match?.data?.setup.h;
|
|
834
|
+
}
|
|
835
|
+
};
|
|
713
836
|
}
|
|
714
|
-
function
|
|
715
|
-
|
|
837
|
+
async function silgiFetch(_request, options, context) {
|
|
838
|
+
let request;
|
|
839
|
+
if (options) {
|
|
840
|
+
const wrapped = wrapBodyAndContentType(options);
|
|
841
|
+
options.body = wrapped.body;
|
|
842
|
+
options.headers = wrapped.headers;
|
|
843
|
+
}
|
|
844
|
+
if (typeof _request === "string") {
|
|
845
|
+
_request = substitutePathParams(_request, options?.pathParams);
|
|
846
|
+
let url = _request;
|
|
847
|
+
if (url[0] === "/") {
|
|
848
|
+
const host = getHeader("Host", options?.headers) || ".";
|
|
849
|
+
const proto = getHeader("X-Forwarded-Proto", options?.headers) === "https" ? "https" : "http";
|
|
850
|
+
url = `${proto}://${host}${url}`;
|
|
851
|
+
}
|
|
852
|
+
request = new Request(url, options);
|
|
853
|
+
} else if (options || _request instanceof URL) {
|
|
854
|
+
request = new Request(_request, options);
|
|
855
|
+
} else {
|
|
856
|
+
request = _request;
|
|
857
|
+
}
|
|
858
|
+
const silgiEvent = new _SilgiEvent(request, context);
|
|
859
|
+
let handlerRes;
|
|
860
|
+
try {
|
|
861
|
+
handlerRes = await handler(silgiEvent);
|
|
862
|
+
} catch (error) {
|
|
863
|
+
handlerRes = Promise.reject(error);
|
|
864
|
+
}
|
|
865
|
+
return handleResponse(handlerRes, silgiEvent, {});
|
|
716
866
|
}
|
|
717
|
-
function
|
|
718
|
-
|
|
867
|
+
async function handler(event) {
|
|
868
|
+
const silgiCtx = useSilgi();
|
|
869
|
+
const pathname = event.url.pathname;
|
|
870
|
+
let _chain = void 0;
|
|
871
|
+
_chain = Promise.resolve(await silgiCtx.callHook("request:on", event));
|
|
872
|
+
if (silgiCtx.router) {
|
|
873
|
+
const match = findRoute(silgiCtx.router, event.req.method, pathname);
|
|
874
|
+
if (match) {
|
|
875
|
+
if (_chain) {
|
|
876
|
+
return _chain.then(async (_previous) => {
|
|
877
|
+
if (_previous !== void 0 && _previous !== kNotFound) {
|
|
878
|
+
return _previous;
|
|
879
|
+
}
|
|
880
|
+
event.context.params = match.params;
|
|
881
|
+
event.context.matchedRoute = match.data;
|
|
882
|
+
return orchestrate(match.data, event);
|
|
883
|
+
});
|
|
884
|
+
} else {
|
|
885
|
+
event.context.params = match.params;
|
|
886
|
+
event.context.matchedRoute = match.data;
|
|
887
|
+
return orchestrate(match.data, event);
|
|
888
|
+
}
|
|
889
|
+
}
|
|
890
|
+
}
|
|
891
|
+
return _chain ? _chain.then(
|
|
892
|
+
(_previous) => _previous === void 0 ? kNotFound : _previous
|
|
893
|
+
) : kNotFound;
|
|
719
894
|
}
|
|
720
895
|
|
|
721
896
|
function getEvent(event) {
|
|
@@ -735,12 +910,42 @@ function getEventContext(event) {
|
|
|
735
910
|
return _event;
|
|
736
911
|
}
|
|
737
912
|
|
|
738
|
-
function createSchema(
|
|
739
|
-
|
|
913
|
+
function createSchema(params) {
|
|
914
|
+
const { namespace, prefix, path, methods } = params;
|
|
915
|
+
const routeWithPrefix = `${prefix}${namespace}${path}`;
|
|
916
|
+
return {
|
|
917
|
+
[routeWithPrefix]: methods
|
|
918
|
+
};
|
|
919
|
+
}
|
|
920
|
+
|
|
921
|
+
function deepMerge(target, source) {
|
|
922
|
+
if (typeof target !== "object" || target === null)
|
|
923
|
+
return source;
|
|
924
|
+
if (typeof source !== "object" || source === null)
|
|
925
|
+
return source;
|
|
926
|
+
const result = Array.isArray(target) ? [...target] : { ...target };
|
|
927
|
+
for (const key of Object.keys(source)) {
|
|
928
|
+
if (key in target) {
|
|
929
|
+
result[key] = deepMerge(target[key], source[key]);
|
|
930
|
+
} else {
|
|
931
|
+
result[key] = source[key];
|
|
932
|
+
}
|
|
933
|
+
}
|
|
934
|
+
return result;
|
|
935
|
+
}
|
|
936
|
+
function deepMergeObjects(schemas) {
|
|
937
|
+
let merged = {};
|
|
938
|
+
for (const schema of schemas) {
|
|
939
|
+
merged = deepMerge(merged, schema);
|
|
940
|
+
}
|
|
941
|
+
return merged;
|
|
740
942
|
}
|
|
741
943
|
|
|
742
|
-
function createService(
|
|
743
|
-
|
|
944
|
+
function createService(params) {
|
|
945
|
+
const { path, methods } = params;
|
|
946
|
+
return {
|
|
947
|
+
[path]: methods
|
|
948
|
+
};
|
|
744
949
|
}
|
|
745
950
|
|
|
746
951
|
function createShared(shared) {
|
|
@@ -766,4 +971,4 @@ const autoImportTypes = [
|
|
|
766
971
|
"ExtractQueryParamsFromURI"
|
|
767
972
|
];
|
|
768
973
|
|
|
769
|
-
export {
|
|
974
|
+
export { SilgiError, autoImportTypes, createError, createRoute, createSchema, createService, createShared, createSilgi, createSilgiCore, createStorage, deepMergeObjects, getEvent, getEventContext, handleResponse, isError, replaceRuntimeValues, silgiCtx, silgiFetch, storageMount, tryUseSilgi, useSilgi, useSilgiStorage };
|