@modern-js/bff-core 2.4.1-beta.0 → 2.5.1-alpha.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/CHANGELOG.md +12 -2
- package/dist/cjs/api.js +76 -0
- package/dist/cjs/client/generate-client.js +91 -0
- package/dist/cjs/client/index.js +17 -0
- package/dist/cjs/client/result.js +46 -0
- package/dist/cjs/errors/http.js +40 -0
- package/dist/cjs/index.js +52 -0
- package/dist/cjs/operators/http.js +206 -0
- package/dist/cjs/router/constants.js +61 -0
- package/dist/cjs/router/index.js +280 -0
- package/dist/cjs/router/types.js +15 -0
- package/dist/cjs/router/utils.js +116 -0
- package/dist/cjs/types.js +73 -0
- package/dist/cjs/utils/alias.js +107 -0
- package/dist/cjs/utils/debug.js +28 -0
- package/dist/cjs/utils/index.js +32 -0
- package/dist/cjs/utils/meta.js +40 -0
- package/dist/cjs/utils/storage.js +71 -0
- package/dist/cjs/utils/validate.js +74 -0
- package/dist/esm/api.js +47 -0
- package/dist/esm/client/generate-client.js +61 -0
- package/dist/esm/client/index.js +1 -0
- package/dist/esm/client/result.js +22 -0
- package/dist/esm/errors/http.js +16 -0
- package/dist/esm/index.js +27 -0
- package/dist/esm/operators/http.js +167 -0
- package/dist/esm/router/constants.js +32 -0
- package/dist/esm/router/index.js +260 -0
- package/dist/esm/router/types.js +0 -0
- package/dist/esm/router/utils.js +83 -0
- package/dist/esm/types.js +45 -0
- package/dist/esm/utils/alias.js +76 -0
- package/dist/esm/utils/debug.js +5 -0
- package/dist/esm/utils/index.js +8 -0
- package/dist/esm/utils/meta.js +14 -0
- package/dist/esm/utils/storage.js +42 -0
- package/dist/esm/utils/validate.js +43 -0
- package/dist/types/client/generate-client.d.ts +4 -1
- package/dist/types/index.d.ts +1 -1
- package/dist/types/router/index.d.ts +5 -1
- package/dist/types/utils/meta.d.ts +3 -1
- package/package.json +11 -11
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
import path from "path";
|
|
2
|
+
import { fs, logger } from "@modern-js/utils";
|
|
3
|
+
import "reflect-metadata";
|
|
4
|
+
import { HttpMethod, httpMethods, OperatorType, TriggerType } from "../types";
|
|
5
|
+
import { debug, INPUT_PARAMS_DECIDER } from "../utils";
|
|
6
|
+
import {
|
|
7
|
+
APIMode,
|
|
8
|
+
FRAMEWORK_MODE_LAMBDA_DIR,
|
|
9
|
+
API_FILE_RULES,
|
|
10
|
+
FRAMEWORK_MODE_APP_DIR
|
|
11
|
+
} from "./constants";
|
|
12
|
+
import {
|
|
13
|
+
getFiles,
|
|
14
|
+
getPathFromFilename,
|
|
15
|
+
requireHandlerModule,
|
|
16
|
+
sortRoutes
|
|
17
|
+
} from "./utils";
|
|
18
|
+
export * from "./types";
|
|
19
|
+
export * from "./constants";
|
|
20
|
+
class ApiRouter {
|
|
21
|
+
constructor({
|
|
22
|
+
apiDir,
|
|
23
|
+
lambdaDir,
|
|
24
|
+
prefix,
|
|
25
|
+
httpMethodDecider = "functionName"
|
|
26
|
+
}) {
|
|
27
|
+
this.apiFiles = [];
|
|
28
|
+
this.getExactApiMode = (apiDir) => {
|
|
29
|
+
const exist = this.createExistChecker(apiDir);
|
|
30
|
+
const existLambdaDir = exist(FRAMEWORK_MODE_LAMBDA_DIR);
|
|
31
|
+
const existAppDir = exist(FRAMEWORK_MODE_APP_DIR);
|
|
32
|
+
const existAppFile = exist("app.ts") || exist("app.js");
|
|
33
|
+
if (existLambdaDir || existAppDir || existAppFile) {
|
|
34
|
+
return APIMode.FARMEWORK;
|
|
35
|
+
}
|
|
36
|
+
return APIMode.FUNCTION;
|
|
37
|
+
};
|
|
38
|
+
this.createExistChecker = (base) => (target) => fs.pathExistsSync(path.resolve(base, target));
|
|
39
|
+
this.getExactLambdaDir = (apiDir) => {
|
|
40
|
+
if (this.lambdaDir) {
|
|
41
|
+
return this.lambdaDir;
|
|
42
|
+
}
|
|
43
|
+
const lambdaDir = this.apiMode === APIMode.FARMEWORK ? path.join(apiDir, FRAMEWORK_MODE_LAMBDA_DIR) : apiDir;
|
|
44
|
+
return lambdaDir;
|
|
45
|
+
};
|
|
46
|
+
this.validateAbsolute(apiDir, "apiDir");
|
|
47
|
+
this.validateAbsolute(lambdaDir, "lambdaDir");
|
|
48
|
+
this.prefix = this.initPrefix(prefix);
|
|
49
|
+
this.apiDir = apiDir;
|
|
50
|
+
this.apiMode = this.getExactApiMode(apiDir);
|
|
51
|
+
this.httpMethodDecider = httpMethodDecider;
|
|
52
|
+
this.lambdaDir = lambdaDir || this.getExactLambdaDir(this.apiDir);
|
|
53
|
+
this.existLambdaDir = fs.existsSync(this.lambdaDir);
|
|
54
|
+
}
|
|
55
|
+
isExistLambda() {
|
|
56
|
+
return this.existLambdaDir;
|
|
57
|
+
}
|
|
58
|
+
getApiMode() {
|
|
59
|
+
return this.apiMode;
|
|
60
|
+
}
|
|
61
|
+
getLambdaDir() {
|
|
62
|
+
return this.lambdaDir;
|
|
63
|
+
}
|
|
64
|
+
isApiFile(filename) {
|
|
65
|
+
if (this.existLambdaDir) {
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
if (!this.apiFiles.includes(filename)) {
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
return true;
|
|
72
|
+
}
|
|
73
|
+
getSingleModuleHandlers(filename) {
|
|
74
|
+
const moduleInfo = this.getModuleInfo(filename);
|
|
75
|
+
if (moduleInfo) {
|
|
76
|
+
return this.getModuleHandlerInfos(moduleInfo);
|
|
77
|
+
}
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
getHandlerInfo(filename, originFuncName, handler) {
|
|
81
|
+
const httpMethod = this.getHttpMethod(originFuncName, handler);
|
|
82
|
+
const routeName = this.getRouteName(filename, handler);
|
|
83
|
+
if (httpMethod && routeName) {
|
|
84
|
+
return {
|
|
85
|
+
handler,
|
|
86
|
+
name: originFuncName,
|
|
87
|
+
httpMethod,
|
|
88
|
+
routeName,
|
|
89
|
+
filename,
|
|
90
|
+
routePath: this.getRoutePath(this.prefix, routeName)
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
getSafeRoutePath(filename, handler) {
|
|
96
|
+
this.loadApiFiles();
|
|
97
|
+
this.validateValidApifile(filename);
|
|
98
|
+
return this.getRouteName(filename, handler);
|
|
99
|
+
}
|
|
100
|
+
getRouteName(filename, handler) {
|
|
101
|
+
if (handler) {
|
|
102
|
+
const trigger = Reflect.getMetadata(OperatorType.Trigger, handler);
|
|
103
|
+
if (trigger && trigger.type === TriggerType.Http) {
|
|
104
|
+
if (!trigger.path) {
|
|
105
|
+
throw new Error(
|
|
106
|
+
`The http trigger ${trigger.name} needs to specify a path`
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
return trigger.path;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
let routePath = getPathFromFilename(this.lambdaDir, filename);
|
|
113
|
+
if (this.httpMethodDecider === "inputParams") {
|
|
114
|
+
if (routePath.endsWith("/")) {
|
|
115
|
+
routePath += `${handler == null ? void 0 : handler.name}`;
|
|
116
|
+
} else {
|
|
117
|
+
routePath += `/${handler == null ? void 0 : handler.name}`;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return routePath;
|
|
121
|
+
}
|
|
122
|
+
getHttpMethod(originHandlerName, handler) {
|
|
123
|
+
if (handler) {
|
|
124
|
+
const trigger = Reflect.getMetadata(OperatorType.Trigger, handler);
|
|
125
|
+
if (trigger && httpMethods.includes(trigger.method)) {
|
|
126
|
+
return trigger.method;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
if (this.httpMethodDecider === "functionName") {
|
|
130
|
+
const upperName = originHandlerName.toUpperCase();
|
|
131
|
+
switch (upperName) {
|
|
132
|
+
case "GET":
|
|
133
|
+
return HttpMethod.Get;
|
|
134
|
+
case "POST":
|
|
135
|
+
return HttpMethod.Post;
|
|
136
|
+
case "PUT":
|
|
137
|
+
return HttpMethod.Put;
|
|
138
|
+
case "DELETE":
|
|
139
|
+
case "DEL":
|
|
140
|
+
return HttpMethod.Delete;
|
|
141
|
+
case "CONNECT":
|
|
142
|
+
return HttpMethod.Connect;
|
|
143
|
+
case "TRACE":
|
|
144
|
+
return HttpMethod.Trace;
|
|
145
|
+
case "PATCH":
|
|
146
|
+
return HttpMethod.Patch;
|
|
147
|
+
case "OPTION":
|
|
148
|
+
return HttpMethod.Option;
|
|
149
|
+
case "DEFAULT": {
|
|
150
|
+
return HttpMethod.Get;
|
|
151
|
+
}
|
|
152
|
+
default:
|
|
153
|
+
logger.warn(
|
|
154
|
+
`Only api handlers are allowd to be exported, please remove the function ${originHandlerName} from exports`
|
|
155
|
+
);
|
|
156
|
+
return null;
|
|
157
|
+
}
|
|
158
|
+
} else {
|
|
159
|
+
if (!handler)
|
|
160
|
+
return null;
|
|
161
|
+
if (typeof handler === "function" && handler.length > 0) {
|
|
162
|
+
return HttpMethod.Post;
|
|
163
|
+
}
|
|
164
|
+
return HttpMethod.Get;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
loadApiFiles() {
|
|
168
|
+
if (!this.existLambdaDir) {
|
|
169
|
+
return [];
|
|
170
|
+
}
|
|
171
|
+
const apiFiles = this.apiFiles = getFiles(this.lambdaDir, API_FILE_RULES);
|
|
172
|
+
return apiFiles;
|
|
173
|
+
}
|
|
174
|
+
getApiFiles() {
|
|
175
|
+
if (!this.existLambdaDir) {
|
|
176
|
+
return [];
|
|
177
|
+
}
|
|
178
|
+
if (this.apiFiles.length > 0) {
|
|
179
|
+
return this.apiFiles;
|
|
180
|
+
}
|
|
181
|
+
return this.loadApiFiles();
|
|
182
|
+
}
|
|
183
|
+
getApiHandlers() {
|
|
184
|
+
const filenames = this.getApiFiles();
|
|
185
|
+
const moduleInfos = this.getModuleInfos(filenames);
|
|
186
|
+
const apiHandlers = this.getHandlerInfos(moduleInfos);
|
|
187
|
+
debug("apiHandlers", apiHandlers.length, apiHandlers);
|
|
188
|
+
return apiHandlers;
|
|
189
|
+
}
|
|
190
|
+
initPrefix(prefix) {
|
|
191
|
+
if (prefix === "/") {
|
|
192
|
+
return "";
|
|
193
|
+
}
|
|
194
|
+
return prefix || "/api";
|
|
195
|
+
}
|
|
196
|
+
validateAbsolute(filename, paramsName) {
|
|
197
|
+
if (typeof filename === "string" && !path.isAbsolute(filename)) {
|
|
198
|
+
throw new Error(`The ${paramsName} ${filename} is not a abolute path`);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
getModuleInfos(filenames) {
|
|
202
|
+
return filenames.map((filename) => this.getModuleInfo(filename)).filter((moduleInfo) => Boolean(moduleInfo));
|
|
203
|
+
}
|
|
204
|
+
getModuleInfo(filename) {
|
|
205
|
+
try {
|
|
206
|
+
const module = requireHandlerModule(filename);
|
|
207
|
+
return {
|
|
208
|
+
filename,
|
|
209
|
+
module
|
|
210
|
+
};
|
|
211
|
+
} catch (err) {
|
|
212
|
+
if (process.env.NODE_ENV === "production") {
|
|
213
|
+
throw err;
|
|
214
|
+
} else {
|
|
215
|
+
console.error(err);
|
|
216
|
+
return null;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
getHandlerInfos(moduleInfos) {
|
|
221
|
+
let apiHandlers = [];
|
|
222
|
+
moduleInfos.forEach((moduleInfo) => {
|
|
223
|
+
const handlerInfos = this.getModuleHandlerInfos(moduleInfo);
|
|
224
|
+
if (handlerInfos) {
|
|
225
|
+
apiHandlers = apiHandlers.concat(handlerInfos);
|
|
226
|
+
}
|
|
227
|
+
});
|
|
228
|
+
const sortedHandlers = sortRoutes(apiHandlers);
|
|
229
|
+
return sortedHandlers;
|
|
230
|
+
}
|
|
231
|
+
getModuleHandlerInfos(moduleInfo) {
|
|
232
|
+
const { module, filename } = moduleInfo;
|
|
233
|
+
const { httpMethodDecider } = this;
|
|
234
|
+
return Object.entries(module).filter(([, handler]) => typeof handler === "function").map(([key]) => {
|
|
235
|
+
const handler = module[key];
|
|
236
|
+
if (httpMethodDecider === "inputParams") {
|
|
237
|
+
Object.assign(handler, {
|
|
238
|
+
[INPUT_PARAMS_DECIDER]: true
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
const handlerInfo = this.getHandlerInfo(filename, key, handler);
|
|
242
|
+
return handlerInfo;
|
|
243
|
+
}).filter((handlerInfo) => Boolean(handlerInfo));
|
|
244
|
+
}
|
|
245
|
+
validateValidApifile(filename) {
|
|
246
|
+
if (!this.apiFiles.includes(filename)) {
|
|
247
|
+
throw new Error(`The ${filename} is not a valid api file.`);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
getRoutePath(prefix, routeName) {
|
|
251
|
+
const finalRouteName = routeName === "/" ? "" : routeName;
|
|
252
|
+
if (prefix === "" && finalRouteName === "") {
|
|
253
|
+
return "/";
|
|
254
|
+
}
|
|
255
|
+
return `${prefix}${finalRouteName}`;
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
export {
|
|
259
|
+
ApiRouter
|
|
260
|
+
};
|
|
File without changes
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import path from "path";
|
|
2
|
+
import { globby } from "@modern-js/utils";
|
|
3
|
+
import { INDEX_SUFFIX } from "./constants";
|
|
4
|
+
const getFiles = (lambdaDir, rules) => globby.sync(rules, {
|
|
5
|
+
cwd: lambdaDir,
|
|
6
|
+
gitignore: true
|
|
7
|
+
}).map((file) => path.resolve(lambdaDir, file));
|
|
8
|
+
const getPathFromFilename = (baseDir, filename) => {
|
|
9
|
+
const relativeName = filename.substring(baseDir.length);
|
|
10
|
+
const relativePath = relativeName.split(".").slice(0, -1).join(".");
|
|
11
|
+
const nameSplit = relativePath.split(path.sep).map((item) => {
|
|
12
|
+
if (item.length > 2) {
|
|
13
|
+
if (item.startsWith("[") && item.endsWith("]")) {
|
|
14
|
+
return `:${item.substring(1, item.length - 1)}`;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
return item;
|
|
18
|
+
});
|
|
19
|
+
const name = nameSplit.join("/");
|
|
20
|
+
const finalName = name.endsWith(INDEX_SUFFIX) ? name.substring(0, name.length - INDEX_SUFFIX.length) : name;
|
|
21
|
+
return clearRouteName(finalName);
|
|
22
|
+
};
|
|
23
|
+
const clearRouteName = (routeName) => {
|
|
24
|
+
let finalRouteName = routeName.trim();
|
|
25
|
+
if (!finalRouteName.startsWith("/")) {
|
|
26
|
+
finalRouteName = `/${finalRouteName}`;
|
|
27
|
+
}
|
|
28
|
+
if (finalRouteName.length > 1 && finalRouteName.endsWith("/")) {
|
|
29
|
+
finalRouteName = finalRouteName.substring(0, finalRouteName.length - 1);
|
|
30
|
+
}
|
|
31
|
+
return finalRouteName;
|
|
32
|
+
};
|
|
33
|
+
const isHandler = (input) => input && typeof input === "function";
|
|
34
|
+
const enableRegister = (requireFn) => {
|
|
35
|
+
let existTsLoader = false;
|
|
36
|
+
let firstCall = true;
|
|
37
|
+
return (modulePath) => {
|
|
38
|
+
if (firstCall) {
|
|
39
|
+
existTsLoader = Boolean(require.extensions[".ts"]);
|
|
40
|
+
firstCall = false;
|
|
41
|
+
}
|
|
42
|
+
if (!existTsLoader) {
|
|
43
|
+
const {
|
|
44
|
+
register
|
|
45
|
+
} = require("esbuild-register/dist/node");
|
|
46
|
+
const { unregister } = register({
|
|
47
|
+
extensions: [".ts"]
|
|
48
|
+
});
|
|
49
|
+
const requiredModule2 = requireFn(modulePath);
|
|
50
|
+
unregister();
|
|
51
|
+
return requiredModule2;
|
|
52
|
+
}
|
|
53
|
+
const requiredModule = requireFn(modulePath);
|
|
54
|
+
return requiredModule;
|
|
55
|
+
};
|
|
56
|
+
};
|
|
57
|
+
const isFunction = (input) => input && {}.toString.call(input) === "[object Function]";
|
|
58
|
+
const requireHandlerModule = enableRegister((modulePath) => {
|
|
59
|
+
const originRequire = process.env.NODE_ENV === "test" ? jest.requireActual : require;
|
|
60
|
+
const module = originRequire(modulePath);
|
|
61
|
+
if (isFunction(module)) {
|
|
62
|
+
return { default: module };
|
|
63
|
+
}
|
|
64
|
+
return module;
|
|
65
|
+
});
|
|
66
|
+
const routeValue = (routePath) => {
|
|
67
|
+
if (routePath.includes(":")) {
|
|
68
|
+
return 11;
|
|
69
|
+
}
|
|
70
|
+
return 1;
|
|
71
|
+
};
|
|
72
|
+
const sortRoutes = (apiHandlers) => {
|
|
73
|
+
return apiHandlers.sort((handlerA, handlerB) => {
|
|
74
|
+
return routeValue(handlerA.routeName) - routeValue(handlerB.routeName);
|
|
75
|
+
});
|
|
76
|
+
};
|
|
77
|
+
export {
|
|
78
|
+
getFiles,
|
|
79
|
+
getPathFromFilename,
|
|
80
|
+
isHandler,
|
|
81
|
+
requireHandlerModule,
|
|
82
|
+
sortRoutes
|
|
83
|
+
};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
var OperatorType = /* @__PURE__ */ ((OperatorType2) => {
|
|
2
|
+
OperatorType2[OperatorType2["Trigger"] = 0] = "Trigger";
|
|
3
|
+
OperatorType2[OperatorType2["Middleware"] = 1] = "Middleware";
|
|
4
|
+
return OperatorType2;
|
|
5
|
+
})(OperatorType || {});
|
|
6
|
+
var TriggerType = /* @__PURE__ */ ((TriggerType2) => {
|
|
7
|
+
TriggerType2[TriggerType2["Http"] = 0] = "Http";
|
|
8
|
+
return TriggerType2;
|
|
9
|
+
})(TriggerType || {});
|
|
10
|
+
var HttpMetadata = /* @__PURE__ */ ((HttpMetadata2) => {
|
|
11
|
+
HttpMetadata2["Method"] = "METHOD";
|
|
12
|
+
HttpMetadata2["Data"] = "DATA";
|
|
13
|
+
HttpMetadata2["Query"] = "QUERY";
|
|
14
|
+
HttpMetadata2["Params"] = "PARAMS";
|
|
15
|
+
HttpMetadata2["Headers"] = "HEADERS";
|
|
16
|
+
HttpMetadata2["Response"] = "RESPONSE";
|
|
17
|
+
return HttpMetadata2;
|
|
18
|
+
})(HttpMetadata || {});
|
|
19
|
+
var ResponseMetaType = /* @__PURE__ */ ((ResponseMetaType2) => {
|
|
20
|
+
ResponseMetaType2[ResponseMetaType2["StatusCode"] = 0] = "StatusCode";
|
|
21
|
+
ResponseMetaType2[ResponseMetaType2["Redirect"] = 1] = "Redirect";
|
|
22
|
+
ResponseMetaType2[ResponseMetaType2["Headers"] = 2] = "Headers";
|
|
23
|
+
return ResponseMetaType2;
|
|
24
|
+
})(ResponseMetaType || {});
|
|
25
|
+
var HttpMethod = /* @__PURE__ */ ((HttpMethod2) => {
|
|
26
|
+
HttpMethod2["Get"] = "GET";
|
|
27
|
+
HttpMethod2["Post"] = "POST";
|
|
28
|
+
HttpMethod2["Put"] = "PUT";
|
|
29
|
+
HttpMethod2["Delete"] = "DELETE";
|
|
30
|
+
HttpMethod2["Connect"] = "CONNECT";
|
|
31
|
+
HttpMethod2["Trace"] = "TRACE";
|
|
32
|
+
HttpMethod2["Patch"] = "PATCH";
|
|
33
|
+
HttpMethod2["Option"] = "OPTION";
|
|
34
|
+
HttpMethod2["Head"] = "HEAD";
|
|
35
|
+
return HttpMethod2;
|
|
36
|
+
})(HttpMethod || {});
|
|
37
|
+
const httpMethods = Object.values(HttpMethod);
|
|
38
|
+
export {
|
|
39
|
+
HttpMetadata,
|
|
40
|
+
HttpMethod,
|
|
41
|
+
OperatorType,
|
|
42
|
+
ResponseMetaType,
|
|
43
|
+
TriggerType,
|
|
44
|
+
httpMethods
|
|
45
|
+
};
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import * as path from "path";
|
|
2
|
+
import * as os from "os";
|
|
3
|
+
import fs from "fs";
|
|
4
|
+
import Module from "module";
|
|
5
|
+
const getRelativeRuntimePath = (appDirectory, serverRuntimePath) => {
|
|
6
|
+
let relativeRuntimePath = "";
|
|
7
|
+
if (os.platform() === "win32") {
|
|
8
|
+
relativeRuntimePath = `../${path.relative(
|
|
9
|
+
appDirectory,
|
|
10
|
+
serverRuntimePath
|
|
11
|
+
)}`;
|
|
12
|
+
} else {
|
|
13
|
+
relativeRuntimePath = path.join(
|
|
14
|
+
"../",
|
|
15
|
+
path.relative(appDirectory, serverRuntimePath)
|
|
16
|
+
);
|
|
17
|
+
}
|
|
18
|
+
if (process.env.NODE_ENV === "development" || process.env.NODE_ENV === "test") {
|
|
19
|
+
relativeRuntimePath = `./${path.relative(appDirectory, serverRuntimePath)}`;
|
|
20
|
+
}
|
|
21
|
+
return relativeRuntimePath;
|
|
22
|
+
};
|
|
23
|
+
const sortByLongestPrefix = (arr) => {
|
|
24
|
+
return arr.concat().sort((a, b) => b.length - a.length);
|
|
25
|
+
};
|
|
26
|
+
const createMatchPath = (paths) => {
|
|
27
|
+
const sortedKeys = sortByLongestPrefix(Object.keys(paths));
|
|
28
|
+
const sortedPaths = {};
|
|
29
|
+
sortedKeys.forEach((key) => {
|
|
30
|
+
sortedPaths[key] = paths[key];
|
|
31
|
+
});
|
|
32
|
+
return (request) => {
|
|
33
|
+
const found = Object.keys(sortedPaths).find((key) => {
|
|
34
|
+
return request.startsWith(key);
|
|
35
|
+
});
|
|
36
|
+
if (found) {
|
|
37
|
+
let foundPaths = sortedPaths[found];
|
|
38
|
+
if (!Array.isArray(foundPaths)) {
|
|
39
|
+
foundPaths = [foundPaths];
|
|
40
|
+
}
|
|
41
|
+
foundPaths = foundPaths.filter((foundPath) => path.isAbsolute(foundPath));
|
|
42
|
+
for (const p of foundPaths) {
|
|
43
|
+
const foundPath = request.replace(found, p);
|
|
44
|
+
if (fs.existsSync(foundPath)) {
|
|
45
|
+
return foundPath;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return request.replace(found, foundPaths[0]);
|
|
49
|
+
}
|
|
50
|
+
return null;
|
|
51
|
+
};
|
|
52
|
+
};
|
|
53
|
+
const registerPaths = (paths) => {
|
|
54
|
+
const originalResolveFilename = Module._resolveFilename;
|
|
55
|
+
const { builtinModules } = Module;
|
|
56
|
+
const matchPath = createMatchPath(paths);
|
|
57
|
+
Module._resolveFilename = function(request, _parent) {
|
|
58
|
+
const isCoreModule = builtinModules.includes(request);
|
|
59
|
+
if (!isCoreModule) {
|
|
60
|
+
const matched = matchPath(request);
|
|
61
|
+
if (matched) {
|
|
62
|
+
const modifiedArguments = [matched, ...[].slice.call(arguments, 1)];
|
|
63
|
+
return originalResolveFilename.apply(this, modifiedArguments);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return originalResolveFilename.apply(this, arguments);
|
|
67
|
+
};
|
|
68
|
+
return () => {
|
|
69
|
+
Module._resolveFilename = originalResolveFilename;
|
|
70
|
+
};
|
|
71
|
+
};
|
|
72
|
+
export {
|
|
73
|
+
createMatchPath,
|
|
74
|
+
getRelativeRuntimePath,
|
|
75
|
+
registerPaths
|
|
76
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
const HANDLER_WITH_META = "HANDLER_WITH_META";
|
|
2
|
+
const INPUT_PARAMS_DECIDER = "INPUT_PARAMS_DECIDER";
|
|
3
|
+
const isWithMetaHandler = (handler) => {
|
|
4
|
+
return typeof handler === "function" && handler[HANDLER_WITH_META];
|
|
5
|
+
};
|
|
6
|
+
const isInputParamsDeciderHandler = (handler) => {
|
|
7
|
+
return typeof handler === "function" && handler[INPUT_PARAMS_DECIDER];
|
|
8
|
+
};
|
|
9
|
+
export {
|
|
10
|
+
HANDLER_WITH_META,
|
|
11
|
+
INPUT_PARAMS_DECIDER,
|
|
12
|
+
isInputParamsDeciderHandler,
|
|
13
|
+
isWithMetaHandler
|
|
14
|
+
};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import * as ah from "async_hooks";
|
|
2
|
+
const createStorage = () => {
|
|
3
|
+
let storage;
|
|
4
|
+
if (typeof ah.AsyncLocalStorage !== "undefined") {
|
|
5
|
+
storage = new ah.AsyncLocalStorage();
|
|
6
|
+
}
|
|
7
|
+
const run = (context, cb) => {
|
|
8
|
+
if (!storage) {
|
|
9
|
+
throw new Error(`Unable to use async_hook, please confirm the node version >= 12.17
|
|
10
|
+
`);
|
|
11
|
+
}
|
|
12
|
+
return new Promise((resolve, reject) => {
|
|
13
|
+
storage.run(context, () => {
|
|
14
|
+
try {
|
|
15
|
+
return resolve(cb());
|
|
16
|
+
} catch (error) {
|
|
17
|
+
return reject(error);
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
});
|
|
21
|
+
};
|
|
22
|
+
const useContext = () => {
|
|
23
|
+
if (!storage) {
|
|
24
|
+
throw new Error(`Unable to use async_hook, please confirm the node version >= 12.17
|
|
25
|
+
`);
|
|
26
|
+
}
|
|
27
|
+
const context = storage.getStore();
|
|
28
|
+
if (!context) {
|
|
29
|
+
throw new Error(
|
|
30
|
+
`Can't call useContext out of scope, it should be placed in the bff function`
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
return context;
|
|
34
|
+
};
|
|
35
|
+
return {
|
|
36
|
+
run,
|
|
37
|
+
useContext
|
|
38
|
+
};
|
|
39
|
+
};
|
|
40
|
+
export {
|
|
41
|
+
createStorage
|
|
42
|
+
};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import util from "util";
|
|
2
|
+
const getTypeErrorMessage = (actual) => {
|
|
3
|
+
var _a;
|
|
4
|
+
let msg = "";
|
|
5
|
+
if (actual == null) {
|
|
6
|
+
msg += `. Received ${actual}`;
|
|
7
|
+
} else if (typeof actual === "function" && actual.name) {
|
|
8
|
+
msg += `. Received function ${actual.name}`;
|
|
9
|
+
} else if (typeof actual === "object") {
|
|
10
|
+
if ((_a = actual.constructor) == null ? void 0 : _a.name) {
|
|
11
|
+
msg += `. Received an instance of ${actual.constructor.name}`;
|
|
12
|
+
} else {
|
|
13
|
+
const inspected = util.inspect(actual, { depth: -1 });
|
|
14
|
+
msg += `. Received ${inspected}`;
|
|
15
|
+
}
|
|
16
|
+
} else {
|
|
17
|
+
let inspected = util.inspect(actual, { colors: false });
|
|
18
|
+
if (inspected.length > 25) {
|
|
19
|
+
inspected = `${inspected.slice(0, 25)}...`;
|
|
20
|
+
}
|
|
21
|
+
msg += `. Received type ${typeof actual} (${inspected})`;
|
|
22
|
+
}
|
|
23
|
+
return msg;
|
|
24
|
+
};
|
|
25
|
+
class ERR_INVALID_ARG_TYPE extends Error {
|
|
26
|
+
constructor(funcName, expectedType, actual) {
|
|
27
|
+
const message = `[ERR_INVALID_ARG_TYPE]: The '${funcName}' argument must be of type ${expectedType}${getTypeErrorMessage(
|
|
28
|
+
actual
|
|
29
|
+
)}`;
|
|
30
|
+
super(message);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
const validateFunction = (maybeFunc, name) => {
|
|
34
|
+
if (typeof maybeFunc !== "function") {
|
|
35
|
+
throw new ERR_INVALID_ARG_TYPE(name, "function", maybeFunc);
|
|
36
|
+
}
|
|
37
|
+
return true;
|
|
38
|
+
};
|
|
39
|
+
export {
|
|
40
|
+
ERR_INVALID_ARG_TYPE,
|
|
41
|
+
getTypeErrorMessage,
|
|
42
|
+
validateFunction
|
|
43
|
+
};
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { HttpMethodDecider } from '@modern-js/types';
|
|
1
2
|
import { Result } from './result';
|
|
2
3
|
export type GenClientResult = Result<string>;
|
|
3
4
|
export type GenClientOptions = {
|
|
@@ -10,6 +11,7 @@ export type GenClientOptions = {
|
|
|
10
11
|
fetcher?: string;
|
|
11
12
|
target?: string;
|
|
12
13
|
requireResolve?: typeof require.resolve;
|
|
14
|
+
httpMethodDecider: HttpMethodDecider;
|
|
13
15
|
};
|
|
14
16
|
export declare const DEFAULT_CLIENT_REQUEST_CREATOR = "@modern-js/create-request";
|
|
15
17
|
export declare const generateClient: ({
|
|
@@ -20,5 +22,6 @@ export declare const generateClient: ({
|
|
|
20
22
|
target,
|
|
21
23
|
requestCreator,
|
|
22
24
|
fetcher,
|
|
23
|
-
requireResolve
|
|
25
|
+
requireResolve,
|
|
26
|
+
httpMethodDecider
|
|
24
27
|
}: GenClientOptions) => Promise<GenClientResult>;
|
package/dist/types/index.d.ts
CHANGED
|
@@ -4,4 +4,4 @@ export * from './router';
|
|
|
4
4
|
export * from './types';
|
|
5
5
|
export * from './client';
|
|
6
6
|
export * from './operators/http';
|
|
7
|
-
export { getRelativeRuntimePath, HANDLER_WITH_META, isWithMetaHandler, createStorage, registerPaths } from './utils';
|
|
7
|
+
export { getRelativeRuntimePath, HANDLER_WITH_META, isWithMetaHandler, INPUT_PARAMS_DECIDER, isInputParamsDeciderHandler, createStorage, registerPaths } from './utils';
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import 'reflect-metadata';
|
|
2
|
+
import type { HttpMethodDecider } from '@modern-js/types';
|
|
2
3
|
import { HttpMethod } from '../types';
|
|
3
4
|
import { APIMode } from './constants';
|
|
4
5
|
import { ApiHandler, APIHandlerInfo } from './types';
|
|
@@ -8,17 +9,20 @@ export declare class ApiRouter {
|
|
|
8
9
|
private apiMode;
|
|
9
10
|
private apiDir;
|
|
10
11
|
private existLambdaDir;
|
|
12
|
+
private httpMethodDecider;
|
|
11
13
|
private lambdaDir;
|
|
12
14
|
private prefix;
|
|
13
15
|
private apiFiles;
|
|
14
16
|
constructor({
|
|
15
17
|
apiDir,
|
|
16
18
|
lambdaDir,
|
|
17
|
-
prefix
|
|
19
|
+
prefix,
|
|
20
|
+
httpMethodDecider
|
|
18
21
|
}: {
|
|
19
22
|
apiDir: string;
|
|
20
23
|
lambdaDir?: string;
|
|
21
24
|
prefix?: string;
|
|
25
|
+
httpMethodDecider?: HttpMethodDecider;
|
|
22
26
|
});
|
|
23
27
|
isExistLambda(): boolean;
|
|
24
28
|
getApiMode(): APIMode;
|
|
@@ -1,2 +1,4 @@
|
|
|
1
1
|
export declare const HANDLER_WITH_META = "HANDLER_WITH_META";
|
|
2
|
-
export declare const
|
|
2
|
+
export declare const INPUT_PARAMS_DECIDER = "INPUT_PARAMS_DECIDER";
|
|
3
|
+
export declare const isWithMetaHandler: (handler: any) => any;
|
|
4
|
+
export declare const isInputParamsDeciderHandler: (handler: any) => any;
|