@sdkgen/node-runtime 2.3.2 → 2.5.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.
Files changed (54) hide show
  1. package/dist/api-config.js +19 -0
  2. package/dist/{src/context.d.ts → context.d.ts} +1 -0
  3. package/dist/context.js +3 -0
  4. package/dist/encode-decode.js +344 -0
  5. package/dist/error.js +33 -0
  6. package/dist/execute.js +57 -0
  7. package/dist/http-client.js +107 -0
  8. package/dist/{src/http-server.d.ts → http-server.d.ts} +1 -0
  9. package/dist/http-server.js +941 -0
  10. package/dist/{src/index.js → index.js} +1 -0
  11. package/dist/swagger.js +454 -0
  12. package/dist/test-wrapper.js +58 -0
  13. package/dist/utils.js +8 -0
  14. package/package.json +50 -34
  15. package/.eslintignore +0 -1
  16. package/.eslintrc.json +0 -3
  17. package/.prettierrc +0 -12
  18. package/.vscode/settings.json +0 -14
  19. package/dist/spec/error.spec.d.ts +0 -1
  20. package/dist/spec/error.spec.js +0 -15
  21. package/dist/spec/rest/rest.spec.d.ts +0 -1
  22. package/dist/spec/rest/rest.spec.js +0 -353
  23. package/dist/spec/runtime/errors.spec.d.ts +0 -1
  24. package/dist/spec/runtime/errors.spec.js +0 -43
  25. package/dist/spec/runtime/middleware.spec.d.ts +0 -1
  26. package/dist/spec/runtime/middleware.spec.js +0 -100
  27. package/dist/spec/simple/legacyNodeClient.d.ts +0 -17
  28. package/dist/spec/simple/legacyNodeClient.js +0 -128
  29. package/dist/spec/simple/simple.spec.d.ts +0 -1
  30. package/dist/spec/simple/simple.spec.js +0 -113
  31. package/dist/spec/types.d.ts +0 -1
  32. package/dist/spec/types.js +0 -60
  33. package/dist/spec/types.spec.d.ts +0 -1
  34. package/dist/spec/types.spec.js +0 -128
  35. package/dist/src/api-config.js +0 -19
  36. package/dist/src/context.js +0 -2
  37. package/dist/src/encode-decode.js +0 -376
  38. package/dist/src/error.js +0 -32
  39. package/dist/src/execute.js +0 -56
  40. package/dist/src/http-client.js +0 -105
  41. package/dist/src/http-server.js +0 -941
  42. package/dist/src/swagger.js +0 -439
  43. package/dist/src/test-wrapper.js +0 -52
  44. package/dist/src/utils.js +0 -7
  45. package/dist/tsconfig.tsbuildinfo +0 -1
  46. /package/dist/{src/api-config.d.ts → api-config.d.ts} +0 -0
  47. /package/dist/{src/encode-decode.d.ts → encode-decode.d.ts} +0 -0
  48. /package/dist/{src/error.d.ts → error.d.ts} +0 -0
  49. /package/dist/{src/execute.d.ts → execute.d.ts} +0 -0
  50. /package/dist/{src/http-client.d.ts → http-client.d.ts} +0 -0
  51. /package/dist/{src/index.d.ts → index.d.ts} +0 -0
  52. /package/dist/{src/swagger.d.ts → swagger.d.ts} +0 -0
  53. /package/dist/{src/test-wrapper.d.ts → test-wrapper.d.ts} +0 -0
  54. /package/dist/{src/utils.d.ts → utils.d.ts} +0 -0
@@ -1,941 +0,0 @@
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.SdkgenHttpServer = void 0;
7
- const crypto_1 = require("crypto");
8
- const fs_1 = require("fs");
9
- const http_1 = require("http");
10
- const os_1 = require("os");
11
- const querystring_1 = require("querystring");
12
- const url_1 = require("url");
13
- const util_1 = require("util");
14
- const csharp_generator_1 = require("@sdkgen/csharp-generator");
15
- const dart_generator_1 = require("@sdkgen/dart-generator");
16
- const fsharp_generator_1 = require("@sdkgen/fsharp-generator");
17
- const kotlin_generator_1 = require("@sdkgen/kotlin-generator");
18
- const parser_1 = require("@sdkgen/parser");
19
- const playground_1 = require("@sdkgen/playground");
20
- const swift_generator_1 = require("@sdkgen/swift-generator");
21
- const typescript_generator_1 = require("@sdkgen/typescript-generator");
22
- const busboy_1 = __importDefault(require("busboy"));
23
- const file_type_1 = __importDefault(require("file-type"));
24
- const request_ip_1 = require("request-ip");
25
- const serve_handler_1 = __importDefault(require("serve-handler"));
26
- const encode_decode_1 = require("./encode-decode");
27
- const error_1 = require("./error");
28
- const execute_1 = require("./execute");
29
- const swagger_1 = require("./swagger");
30
- const utils_1 = require("./utils");
31
- class SdkgenHttpServer {
32
- constructor(apiConfig, ...maybeExtraContext) {
33
- var _a;
34
- this.apiConfig = apiConfig;
35
- this.headers = new Map();
36
- this.healthChecks = [];
37
- this.handlers = [];
38
- this.dynamicCorsOrigin = true;
39
- this.introspection = true;
40
- this.log = (message) => {
41
- console.log(`${new Date().toISOString()} ${message}`);
42
- };
43
- this.hasSwagger = false;
44
- this.ignoredUrlPrefix = "";
45
- this.handleRequest = (req, res) => {
46
- const hrStart = process.hrtime();
47
- req.on("error", err => {
48
- console.error(err);
49
- res.end();
50
- });
51
- res.on("error", err => {
52
- console.error(err);
53
- res.end();
54
- });
55
- if (this.dynamicCorsOrigin && req.headers.origin) {
56
- res.setHeader("Access-Control-Allow-Origin", req.headers.origin);
57
- res.setHeader("Vary", "Origin");
58
- }
59
- for (const [header, value] of this.headers) {
60
- if (req.method === "OPTIONS" && !header.startsWith("access-control-")) {
61
- continue;
62
- }
63
- res.setHeader(header, value);
64
- }
65
- if (req.method === "OPTIONS") {
66
- res.writeHead(200);
67
- res.end();
68
- return;
69
- }
70
- const handleBody = (body) => {
71
- this.handleRequestWithBody(req, res, body, hrStart).catch((e) => this.writeReply(res, null, { error: e }, hrStart));
72
- };
73
- // Google Cloud Functions add a rawBody property to the request object
74
- if ((0, utils_1.has)(req, "rawBody") && req.rawBody instanceof Buffer) {
75
- handleBody(req.rawBody);
76
- }
77
- else {
78
- const body = [];
79
- // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
80
- req.on("data", chunk => body.push(chunk));
81
- req.on("end", () => {
82
- handleBody(Buffer.concat(body));
83
- });
84
- }
85
- };
86
- this.extraContext = ((_a = maybeExtraContext[0]) !== null && _a !== void 0 ? _a : {});
87
- this.httpServer = (0, http_1.createServer)(this.handleRequest.bind(this));
88
- this.enableCors();
89
- this.attachRestHandlers();
90
- const targetTable = [
91
- ["/targets/android/client.kt", (ast) => (0, kotlin_generator_1.generateAndroidClientSource)(ast, true)],
92
- ["/targets/android/client_without_callbacks.kt", (ast) => (0, kotlin_generator_1.generateAndroidClientSource)(ast, false)],
93
- ["/targets/dotnet/api.cs", csharp_generator_1.generateCSharpServerSource],
94
- ["/targets/dotnet/api.fs", fsharp_generator_1.generateFSharpServerSource],
95
- ["/targets/flutter/client.dart", dart_generator_1.generateDartClientSource],
96
- ["/targets/ios/client.swift", (ast) => (0, swift_generator_1.generateSwiftClientSource)(ast, false)],
97
- ["/targets/ios/client-rx.swift", (ast) => (0, swift_generator_1.generateSwiftClientSource)(ast, true)],
98
- ["/targets/node/api.ts", typescript_generator_1.generateNodeServerSource],
99
- ["/targets/node/client.ts", typescript_generator_1.generateNodeClientSource],
100
- ["/targets/web/client.ts", typescript_generator_1.generateBrowserClientSource],
101
- ];
102
- for (const [path, generateFn] of targetTable) {
103
- this.addHttpHandler("GET", path, (_req, res) => {
104
- if (!this.introspection) {
105
- res.statusCode = 404;
106
- res.end();
107
- return;
108
- }
109
- try {
110
- res.setHeader("Content-Type", "application/octet-stream");
111
- res.write(generateFn(this.apiConfig.ast));
112
- }
113
- catch (e) {
114
- console.error(e);
115
- res.statusCode = 500;
116
- res.write(`${e}`);
117
- }
118
- res.end();
119
- });
120
- }
121
- this.addHttpHandler("GET", "/ast.json", (_req, res) => {
122
- if (!this.introspection) {
123
- res.statusCode = 404;
124
- res.end();
125
- return;
126
- }
127
- res.setHeader("Content-Type", "application/json");
128
- res.write(JSON.stringify(apiConfig.astJson));
129
- res.end();
130
- });
131
- this.addHttpHandler("GET", /^\/playground.*/u, (req, res) => {
132
- if (!this.introspection) {
133
- res.statusCode = 404;
134
- res.end();
135
- return;
136
- }
137
- if (req.url) {
138
- req.url = req.url.endsWith("/playground") ? req.url.replace(/\/playground/u, "/index.html") : req.url.replace(/\/playground/u, "");
139
- }
140
- (0, serve_handler_1.default)(req, res, {
141
- cleanUrls: false,
142
- directoryListing: false,
143
- etag: true,
144
- public: playground_1.PLAYGROUND_PUBLIC_PATH,
145
- }).catch(e => {
146
- console.error(e);
147
- res.statusCode = 500;
148
- res.write(`${e}`);
149
- res.end();
150
- });
151
- });
152
- }
153
- registerHealthCheck(healthCheck) {
154
- this.healthChecks.push(healthCheck);
155
- }
156
- ignoreUrlPrefix(urlPrefix) {
157
- this.ignoredUrlPrefix = urlPrefix;
158
- }
159
- async listen(port = 8000) {
160
- return new Promise(resolve => {
161
- this.httpServer.listen(port, () => {
162
- const addr = this.httpServer.address();
163
- let urlHost;
164
- if (addr.address === "::") {
165
- urlHost = `localhost:${addr.port}`;
166
- }
167
- else if (addr.family === "ipv6") {
168
- urlHost = `[${addr.address}]:${addr.port}`;
169
- }
170
- else {
171
- urlHost = `${addr.address}:${addr.port}`;
172
- }
173
- if (addr.address === "::" || addr.address === "0.0.0.0") {
174
- console.log(`\nListening on port ${addr.port}`);
175
- }
176
- else {
177
- console.log(`\nListening on port ${addr.port} (${addr.address})`);
178
- }
179
- if (this.introspection) {
180
- console.log(`Playground: http://${urlHost}/playground`);
181
- }
182
- if (this.hasSwagger) {
183
- console.log(`Swagger UI: http://${urlHost}/swagger`);
184
- }
185
- console.log("");
186
- resolve();
187
- });
188
- });
189
- }
190
- async close() {
191
- return (0, util_1.promisify)(this.httpServer.close.bind(this.httpServer))();
192
- }
193
- enableCors() {
194
- this.addHeader("Access-Control-Allow-Methods", "DELETE, HEAD, PUT, POST, PATCH, GET, OPTIONS");
195
- this.addHeader("Access-Control-Allow-Headers", "Content-Type");
196
- this.addHeader("Access-Control-Max-Age", "86400");
197
- }
198
- addHeader(header, value) {
199
- const cleanHeader = header.toLowerCase().trim();
200
- const existing = this.headers.get(cleanHeader);
201
- if (existing) {
202
- if (!existing.includes(value)) {
203
- this.headers.set(cleanHeader, `${existing}, ${value}`);
204
- }
205
- }
206
- else {
207
- this.headers.set(cleanHeader, value);
208
- }
209
- }
210
- addHttpHandler(method, matcher, handler) {
211
- this.handlers.push({ handler, matcher, method });
212
- }
213
- findBestHandler(path, req) {
214
- const matchingHandlers = this.handlers
215
- .filter(({ method }) => method === req.method)
216
- .filter(({ matcher }) => {
217
- var _a;
218
- if (typeof matcher === "string") {
219
- return matcher === path;
220
- }
221
- return ((_a = matcher.exec(path)) === null || _a === void 0 ? void 0 : _a[0]) === path;
222
- })
223
- .sort(({ matcher: first }, { matcher: second }) => {
224
- // Prefer string matches instead of Regexp matches
225
- if (typeof first === "string" && typeof second === "string") {
226
- return 0;
227
- }
228
- else if (typeof first === "string") {
229
- return -1;
230
- }
231
- else if (typeof second === "string") {
232
- return 1;
233
- }
234
- const firstMatch = first.exec(path);
235
- const secondMatch = second.exec(path);
236
- if (!firstMatch) {
237
- return -1;
238
- }
239
- if (!secondMatch) {
240
- return 1;
241
- }
242
- // Compute how many characters were NOT part of a capture group
243
- const firstLength = firstMatch[0].length - firstMatch.slice(1).reduce((acc, cur) => acc + cur.length, 0);
244
- const secondLength = secondMatch[0].length - secondMatch.slice(1).reduce((acc, cur) => acc + cur.length, 0);
245
- // Prefer the maximum number of non-captured characters
246
- return secondLength - firstLength;
247
- });
248
- return matchingHandlers.length ? matchingHandlers[0] : null;
249
- }
250
- attachRestHandlers() {
251
- function escapeRegExp(str) {
252
- return str.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&");
253
- }
254
- for (const op of this.apiConfig.ast.operations) {
255
- for (const ann of op.annotations) {
256
- if (!(ann instanceof parser_1.RestAnnotation)) {
257
- continue;
258
- }
259
- if (!this.hasSwagger) {
260
- (0, swagger_1.setupSwagger)(this);
261
- this.hasSwagger = true;
262
- }
263
- const pathFragments = ann.path.split(/\{\w+\}/u);
264
- let pathRegex = "^";
265
- for (let i = 0; i < pathFragments.length; ++i) {
266
- if (i > 0) {
267
- pathRegex += "([^/]+?)";
268
- }
269
- pathRegex += escapeRegExp(pathFragments[i]);
270
- }
271
- pathRegex += "/?$";
272
- for (const header of ann.headers.keys()) {
273
- this.addHeader("Access-Control-Allow-Headers", header.toLowerCase());
274
- }
275
- // eslint-disable-next-line @typescript-eslint/no-misused-promises
276
- this.addHttpHandler(ann.method, new RegExp(pathRegex, "u"), async (req, res, body) => {
277
- var _a, _b, _c, _d, _e, _f, _g;
278
- try {
279
- const args = {};
280
- const files = [];
281
- const { pathname, query } = (0, url_1.parse)((_a = req.url) !== null && _a !== void 0 ? _a : "");
282
- const match = pathname === null || pathname === void 0 ? void 0 : pathname.match(pathRegex);
283
- if (!match) {
284
- res.statusCode = 404;
285
- return;
286
- }
287
- const simpleArgs = new Map();
288
- for (let i = 0; i < ann.pathVariables.length; ++i) {
289
- const argName = ann.pathVariables[i];
290
- const argValue = match[i + 1];
291
- simpleArgs.set(argName, argValue);
292
- }
293
- const parsedQuery = query ? (0, querystring_1.parse)(query) : {};
294
- for (const argName of ann.queryVariables) {
295
- const argValue = (_b = parsedQuery[argName]) !== null && _b !== void 0 ? _b : null;
296
- if (argValue === null) {
297
- continue;
298
- }
299
- simpleArgs.set(argName, Array.isArray(argValue) ? argValue.join("") : argValue);
300
- }
301
- for (const [headerName, argName] of ann.headers) {
302
- const argValue = (_c = req.headers[headerName.toLowerCase()]) !== null && _c !== void 0 ? _c : null;
303
- if (argValue === null) {
304
- continue;
305
- }
306
- simpleArgs.set(argName, Array.isArray(argValue) ? argValue.join("") : argValue);
307
- }
308
- if (!ann.bodyVariable && ((_d = req.headers["content-type"]) === null || _d === void 0 ? void 0 : _d.match(/^application\/x-www-form-urlencoded/iu))) {
309
- const parsedBody = (0, querystring_1.parse)(body.toString());
310
- for (const argName of ann.queryVariables) {
311
- const argValue = (_e = parsedBody[argName]) !== null && _e !== void 0 ? _e : null;
312
- if (argValue === null) {
313
- continue;
314
- }
315
- simpleArgs.set(argName, Array.isArray(argValue) ? argValue.join("") : argValue);
316
- }
317
- }
318
- else if (!ann.bodyVariable && ((_f = req.headers["content-type"]) === null || _f === void 0 ? void 0 : _f.match(/^multipart\/form-data/iu))) {
319
- const busboy = (0, busboy_1.default)({ headers: req.headers });
320
- const filePromises = [];
321
- busboy.on("field", (field, value) => {
322
- if (ann.queryVariables.includes(field)) {
323
- simpleArgs.set(field, `${value}`);
324
- }
325
- });
326
- busboy.on("file", (_field, stream, info) => {
327
- const tempName = (0, crypto_1.randomBytes)(32).toString("hex");
328
- const writeStream = (0, fs_1.createWriteStream)(tempName);
329
- filePromises.push(new Promise((resolve, reject) => {
330
- writeStream.on("error", reject);
331
- writeStream.on("close", () => {
332
- const contents = (0, fs_1.createReadStream)(tempName);
333
- files.push({ contents, name: info.filename });
334
- contents.on("open", () => {
335
- (0, fs_1.unlink)(tempName, err => {
336
- if (err) {
337
- reject(err);
338
- }
339
- else {
340
- resolve();
341
- }
342
- });
343
- });
344
- });
345
- writeStream.on("open", () => {
346
- stream.pipe(writeStream);
347
- });
348
- }));
349
- });
350
- await new Promise((resolve, reject) => {
351
- busboy.on("finish", resolve);
352
- busboy.on("error", reject);
353
- busboy.write(body);
354
- });
355
- await Promise.all(filePromises);
356
- }
357
- else if (ann.bodyVariable) {
358
- const argName = ann.bodyVariable;
359
- const arg = op.args.find(x => x.name === argName);
360
- if (/application\/json/iu.test((_g = req.headers["content-type"]) !== null && _g !== void 0 ? _g : "")) {
361
- args[argName] = JSON.parse(body.toString());
362
- }
363
- else if (arg) {
364
- let { type } = arg;
365
- let solved = false;
366
- if (type instanceof parser_1.OptionalType) {
367
- if (body.length === 0) {
368
- args[argName] = null;
369
- solved = true;
370
- }
371
- else {
372
- type = type.base;
373
- }
374
- }
375
- if (!solved) {
376
- if (type instanceof parser_1.BoolPrimitiveType ||
377
- type instanceof parser_1.IntPrimitiveType ||
378
- type instanceof parser_1.UIntPrimitiveType ||
379
- type instanceof parser_1.FloatPrimitiveType ||
380
- type instanceof parser_1.StringPrimitiveType ||
381
- type instanceof parser_1.DatePrimitiveType ||
382
- type instanceof parser_1.DateTimePrimitiveType ||
383
- type instanceof parser_1.MoneyPrimitiveType ||
384
- type instanceof parser_1.DecimalPrimitiveType ||
385
- type instanceof parser_1.BigIntPrimitiveType ||
386
- type instanceof parser_1.CpfPrimitiveType ||
387
- type instanceof parser_1.CnpjPrimitiveType ||
388
- type instanceof parser_1.UuidPrimitiveType ||
389
- type instanceof parser_1.HexPrimitiveType ||
390
- type instanceof parser_1.Base64PrimitiveType) {
391
- simpleArgs.set(argName, body.toString());
392
- }
393
- else if (type instanceof parser_1.BytesPrimitiveType) {
394
- args[argName] = body.toString("base64");
395
- }
396
- else {
397
- args[argName] = JSON.parse(body.toString());
398
- }
399
- }
400
- }
401
- }
402
- for (const [argName, argValue] of simpleArgs) {
403
- const arg = op.args.find(x => x.name === argName);
404
- if (!arg) {
405
- continue;
406
- }
407
- let { type } = arg;
408
- if (type instanceof parser_1.OptionalType) {
409
- if (argValue === null) {
410
- args[argName] = null;
411
- continue;
412
- }
413
- else {
414
- type = type.base;
415
- }
416
- }
417
- else if (argValue === null) {
418
- args[argName] = argValue;
419
- continue;
420
- }
421
- if (type instanceof parser_1.BoolPrimitiveType) {
422
- if (argValue === "true") {
423
- args[argName] = true;
424
- }
425
- else if (argValue === "false") {
426
- args[argName] = false;
427
- }
428
- else {
429
- args[argName] = argValue;
430
- }
431
- }
432
- else if (type instanceof parser_1.UIntPrimitiveType || type instanceof parser_1.IntPrimitiveType || type instanceof parser_1.MoneyPrimitiveType) {
433
- args[argName] = parseInt(argValue, 10);
434
- }
435
- else if (type instanceof parser_1.FloatPrimitiveType) {
436
- args[argName] = parseFloat(argValue);
437
- }
438
- else {
439
- args[argName] = argValue;
440
- }
441
- }
442
- const ip = (0, request_ip_1.getClientIp)(req);
443
- if (!ip) {
444
- throw new Error("Couldn't determine client IP");
445
- }
446
- const request = {
447
- args,
448
- deviceInfo: {
449
- fingerprint: null,
450
- id: (0, crypto_1.randomBytes)(16).toString("hex"),
451
- language: null,
452
- platform: null,
453
- timezone: null,
454
- type: "rest",
455
- version: null,
456
- },
457
- extra: {},
458
- files,
459
- headers: req.headers,
460
- id: (0, crypto_1.randomBytes)(16).toString("hex"),
461
- ip,
462
- name: op.name,
463
- version: 3,
464
- };
465
- await this.executeRequest(request, (ctx, reply) => {
466
- try {
467
- if (ctx) {
468
- for (const [headerKey, headerValue] of ctx.response.headers.entries()) {
469
- res.setHeader(headerKey, headerValue);
470
- }
471
- }
472
- if (ctx === null || ctx === void 0 ? void 0 : ctx.response.statusCode) {
473
- res.statusCode = ctx.response.statusCode;
474
- }
475
- if (reply.error) {
476
- const error = this.makeResponseError(reply.error);
477
- if (!(ctx === null || ctx === void 0 ? void 0 : ctx.response.statusCode)) {
478
- const errorNode = this.apiConfig.ast.errors.find(node => node.name === error.type);
479
- const statusAnnotation = errorNode === null || errorNode === void 0 ? void 0 : errorNode.annotations.find(x => x instanceof parser_1.StatusCodeAnnotation);
480
- res.statusCode = statusAnnotation ? statusAnnotation.statusCode : error.type === "Fatal" ? 500 : 400;
481
- }
482
- res.setHeader("content-type", "application/json");
483
- res.write(JSON.stringify(error));
484
- res.end();
485
- return;
486
- }
487
- if (req.headers.accept === "application/json") {
488
- res.setHeader("content-type", "application/json");
489
- res.write(JSON.stringify(reply.result));
490
- res.end();
491
- }
492
- else {
493
- let type = op.returnType;
494
- if (type instanceof parser_1.OptionalType) {
495
- if (reply.result === null) {
496
- if (!(ctx === null || ctx === void 0 ? void 0 : ctx.response.statusCode)) {
497
- res.statusCode = ann.method === "GET" ? 404 : 204;
498
- }
499
- res.end();
500
- return;
501
- }
502
- type = type.base;
503
- }
504
- if (type instanceof parser_1.BoolPrimitiveType ||
505
- type instanceof parser_1.IntPrimitiveType ||
506
- type instanceof parser_1.UIntPrimitiveType ||
507
- type instanceof parser_1.FloatPrimitiveType ||
508
- type instanceof parser_1.StringPrimitiveType ||
509
- type instanceof parser_1.DatePrimitiveType ||
510
- type instanceof parser_1.DateTimePrimitiveType ||
511
- type instanceof parser_1.MoneyPrimitiveType ||
512
- type instanceof parser_1.DecimalPrimitiveType ||
513
- type instanceof parser_1.BigIntPrimitiveType ||
514
- type instanceof parser_1.CpfPrimitiveType ||
515
- type instanceof parser_1.CnpjPrimitiveType ||
516
- type instanceof parser_1.UuidPrimitiveType ||
517
- type instanceof parser_1.HexPrimitiveType ||
518
- type instanceof parser_1.Base64PrimitiveType) {
519
- res.setHeader("content-type", "text/plain");
520
- res.write(`${reply.result}`);
521
- res.end();
522
- }
523
- else if (type instanceof parser_1.HtmlPrimitiveType) {
524
- res.setHeader("content-type", "text/html");
525
- res.write(`${reply.result}`);
526
- res.end();
527
- }
528
- else if (type instanceof parser_1.XmlPrimitiveType) {
529
- res.setHeader("content-type", "text/xml");
530
- res.write(`${reply.result}`);
531
- res.end();
532
- }
533
- else if (type instanceof parser_1.BytesPrimitiveType) {
534
- const buffer = Buffer.from(reply.result, "base64");
535
- // eslint-disable-next-line @typescript-eslint/no-floating-promises
536
- file_type_1.default.fromBuffer(buffer)
537
- .then(fileType => {
538
- var _a;
539
- res.setHeader("content-type", (_a = fileType === null || fileType === void 0 ? void 0 : fileType.mime) !== null && _a !== void 0 ? _a : "application/octet-stream");
540
- })
541
- .catch(err => {
542
- console.error(err);
543
- res.setHeader("content-type", "application/octet-stream");
544
- })
545
- .then(() => {
546
- res.write(buffer);
547
- res.end();
548
- })
549
- .catch(() => { });
550
- }
551
- else {
552
- res.setHeader("content-type", "application/json");
553
- res.write(JSON.stringify(reply.result));
554
- res.end();
555
- }
556
- }
557
- }
558
- catch (error) {
559
- console.error(error);
560
- if (!res.headersSent) {
561
- res.statusCode = 500;
562
- }
563
- res.end();
564
- }
565
- });
566
- }
567
- catch (error) {
568
- console.error(error);
569
- if (!res.headersSent) {
570
- res.statusCode = 500;
571
- }
572
- res.end();
573
- }
574
- });
575
- }
576
- }
577
- }
578
- async handleRequestWithBody(req, res, body, hrStart) {
579
- var _a, _b;
580
- const { pathname, query } = (0, url_1.parse)((_a = req.url) !== null && _a !== void 0 ? _a : "");
581
- let path = pathname !== null && pathname !== void 0 ? pathname : "";
582
- if (path.startsWith(this.ignoredUrlPrefix)) {
583
- path = path.slice(this.ignoredUrlPrefix.length);
584
- }
585
- if (!((_b = req.headers["content-type"]) === null || _b === void 0 ? void 0 : _b.match(/application\/sdkgen/iu))) {
586
- const externalHandler = this.findBestHandler(path, req);
587
- if (externalHandler) {
588
- this.log(`HTTP ${req.method} ${path}${query ? `?${query}` : ""}`);
589
- externalHandler.handler(req, res, body);
590
- return;
591
- }
592
- }
593
- res.setHeader("Content-Type", "application/json; charset=utf-8");
594
- if (req.method === "HEAD") {
595
- res.writeHead(200);
596
- res.end();
597
- return;
598
- }
599
- if (req.method === "GET") {
600
- if (path !== "/") {
601
- res.writeHead(404);
602
- res.end();
603
- return;
604
- }
605
- let ok = true;
606
- try {
607
- for (const healthCheck of this.healthChecks) {
608
- if (!ok) {
609
- break;
610
- }
611
- ok = await healthCheck();
612
- }
613
- }
614
- catch (e) {
615
- ok = false;
616
- }
617
- res.statusCode = ok ? 200 : 500;
618
- res.write(JSON.stringify({ ok }));
619
- res.end();
620
- return;
621
- }
622
- if (req.method !== "POST") {
623
- res.writeHead(400);
624
- res.end();
625
- return;
626
- }
627
- const clientIp = (0, request_ip_1.getClientIp)(req);
628
- if (!clientIp) {
629
- this.writeReply(res, null, {
630
- error: new error_1.Fatal("Couldn't determine client IP"),
631
- }, hrStart);
632
- return;
633
- }
634
- const request = this.parseRequest(req, body.toString(), clientIp);
635
- if (!request) {
636
- this.writeReply(res, null, {
637
- error: new error_1.Fatal("Couldn't parse request"),
638
- }, hrStart);
639
- return;
640
- }
641
- await this.executeRequest(request, (ctx, reply) => this.writeReply(res, ctx, reply, hrStart));
642
- }
643
- async executeRequest(request, writeReply) {
644
- const ctx = Object.assign(Object.assign({}, this.extraContext), { request, response: {
645
- headers: new Map(),
646
- } });
647
- writeReply(ctx, await (0, execute_1.executeRequest)(ctx, this.apiConfig));
648
- }
649
- parseRequest(req, body, ip) {
650
- switch (this.identifyRequestVersion(req, body)) {
651
- case 1:
652
- return this.parseRequestV1(req, body, ip);
653
- case 2:
654
- return this.parseRequestV2(req, body, ip);
655
- case 3:
656
- return this.parseRequestV3(req, body, ip);
657
- default:
658
- throw new Error("Failed to understand request");
659
- }
660
- }
661
- identifyRequestVersion(_req, body) {
662
- const parsed = JSON.parse(body);
663
- if (typeof parsed === "object" && parsed && (0, utils_1.has)(parsed, "version") && typeof parsed.version === "number") {
664
- return parsed.version;
665
- }
666
- else if (typeof parsed === "object" && parsed && (0, utils_1.has)(parsed, "requestId")) {
667
- return 2;
668
- }
669
- else if (typeof parsed === "object" && parsed && (0, utils_1.has)(parsed, "device")) {
670
- return 1;
671
- }
672
- return 3;
673
- }
674
- // Old Sdkgen format
675
- parseRequestV1(req, body, ip) {
676
- var _a, _b;
677
- const parsed = (0, encode_decode_1.decode)({
678
- Request: {
679
- args: "json",
680
- device: "RequestDevice",
681
- id: "string",
682
- name: "string",
683
- },
684
- RequestDevice: {
685
- fingerprint: "string?",
686
- id: "string?",
687
- language: "string?",
688
- platform: "json?",
689
- timezone: "string?",
690
- type: "string?",
691
- version: "string?",
692
- },
693
- }, "root", "Request", JSON.parse(body));
694
- const deviceId = (_a = parsed.device.id) !== null && _a !== void 0 ? _a : (0, crypto_1.randomBytes)(20).toString("hex");
695
- if (!parsed.args || Array.isArray(parsed.args) || typeof parsed.args !== "object") {
696
- throw new Error("Expected 'args' to be an object");
697
- }
698
- return {
699
- args: parsed.args,
700
- deviceInfo: {
701
- fingerprint: parsed.device.fingerprint,
702
- id: deviceId,
703
- language: parsed.device.language,
704
- platform: parsed.device.platform,
705
- timezone: parsed.device.timezone,
706
- type: (_b = parsed.device.type) !== null && _b !== void 0 ? _b : (typeof parsed.device.platform === "string" ? parsed.device.platform : ""),
707
- version: parsed.device.version,
708
- },
709
- extra: {},
710
- files: [],
711
- headers: req.headers,
712
- id: `${deviceId}-${parsed.id}`,
713
- ip,
714
- name: parsed.name,
715
- version: 1,
716
- };
717
- }
718
- // Maxima sdkgen format
719
- parseRequestV2(req, body, ip) {
720
- var _a, _b;
721
- const parsed = (0, encode_decode_1.decode)({
722
- Request: {
723
- args: "json",
724
- deviceFingerprint: "string?",
725
- deviceId: "string",
726
- info: "RequestInfo",
727
- name: "string",
728
- partnerId: "string?",
729
- requestId: "string?",
730
- sessionId: "string?",
731
- },
732
- RequestInfo: {
733
- browserUserAgent: "string?",
734
- language: "string",
735
- type: "string",
736
- },
737
- }, "root", "Request", JSON.parse(body));
738
- if (!parsed.args || Array.isArray(parsed.args) || typeof parsed.args !== "object") {
739
- throw new Error("Expected 'args' to be an object");
740
- }
741
- return {
742
- args: parsed.args,
743
- deviceInfo: {
744
- fingerprint: parsed.deviceFingerprint,
745
- id: parsed.deviceId,
746
- language: parsed.info.language,
747
- platform: {
748
- browserUserAgent: (_a = parsed.info.browserUserAgent) !== null && _a !== void 0 ? _a : null,
749
- },
750
- timezone: null,
751
- type: parsed.info.type,
752
- version: "",
753
- },
754
- extra: {
755
- partnerId: parsed.partnerId,
756
- sessionId: parsed.sessionId,
757
- },
758
- files: [],
759
- headers: req.headers,
760
- id: `${parsed.deviceId}-${(_b = parsed.requestId) !== null && _b !== void 0 ? _b : (0, crypto_1.randomBytes)(16).toString("hex")}`,
761
- ip,
762
- name: parsed.name,
763
- version: 2,
764
- };
765
- }
766
- // New sdkgen format
767
- parseRequestV3(req, body, ip) {
768
- var _a, _b, _c, _d;
769
- const parsed = (0, encode_decode_1.decode)({
770
- DeviceInfo: {
771
- fingerprint: "string?",
772
- id: "string?",
773
- language: "string?",
774
- platform: "json?",
775
- timezone: "string?",
776
- type: "string?",
777
- version: "string?",
778
- },
779
- Request: {
780
- args: "json",
781
- deviceInfo: "DeviceInfo?",
782
- extra: "json?",
783
- name: "string",
784
- requestId: "string?",
785
- },
786
- }, "root", "Request", JSON.parse(body));
787
- const deviceInfo = (_a = parsed.deviceInfo) !== null && _a !== void 0 ? _a : {
788
- fingerprint: null,
789
- id: null,
790
- language: null,
791
- platform: null,
792
- timezone: null,
793
- type: null,
794
- version: null,
795
- };
796
- const deviceId = (_b = deviceInfo.id) !== null && _b !== void 0 ? _b : (0, crypto_1.randomBytes)(16).toString("hex");
797
- if (!parsed.args || Array.isArray(parsed.args) || typeof parsed.args !== "object") {
798
- throw new Error("Expected 'args' to be an object");
799
- }
800
- return {
801
- args: parsed.args,
802
- deviceInfo: {
803
- fingerprint: deviceInfo.fingerprint,
804
- id: deviceId,
805
- language: deviceInfo.language,
806
- platform: typeof deviceInfo.platform === "object" ? Object.assign({}, deviceInfo.platform) : {},
807
- timezone: deviceInfo.timezone,
808
- type: (_c = deviceInfo.type) !== null && _c !== void 0 ? _c : "api",
809
- version: deviceInfo.version,
810
- },
811
- extra: typeof parsed.extra === "object" ? Object.assign({}, parsed.extra) : {},
812
- files: [],
813
- headers: req.headers,
814
- id: `${deviceId}-${(_d = parsed.requestId) !== null && _d !== void 0 ? _d : (0, crypto_1.randomBytes)(16).toString("hex")}`,
815
- ip,
816
- name: parsed.name,
817
- version: 3,
818
- };
819
- }
820
- makeResponseError(err) {
821
- let type = "Fatal";
822
- if (typeof err === "object" && err !== null && (0, utils_1.has)(err, "type") && typeof err.type === "string") {
823
- ({ type } = err);
824
- }
825
- let message;
826
- if (typeof err === "object" && err !== null && (0, utils_1.has)(err, "message") && typeof err.message === "string") {
827
- ({ message } = err);
828
- }
829
- else if (err instanceof Error) {
830
- message = err.toString();
831
- }
832
- else if (typeof err === "object") {
833
- message = JSON.stringify(err);
834
- }
835
- else {
836
- message = `${err}`;
837
- }
838
- let data;
839
- if (typeof err === "object" && err !== null && (0, utils_1.has)(err, "data")) {
840
- ({ data } = err);
841
- }
842
- const error = this.apiConfig.ast.errors.find(x => x.name === type);
843
- if (error) {
844
- if (!(error.dataType instanceof parser_1.VoidPrimitiveType)) {
845
- try {
846
- data = (0, encode_decode_1.encode)(this.apiConfig.astJson.typeTable, `error.${type}`, error.dataType.name, data);
847
- }
848
- catch (encodeError) {
849
- message = `Failed to encode error ${type} because: ${encodeError}. Original message: ${message}`;
850
- type = "Fatal";
851
- }
852
- }
853
- }
854
- else {
855
- type = "Fatal";
856
- }
857
- return { data, message, type };
858
- }
859
- writeReply(res, ctx, reply, hrStart) {
860
- var _a, _b;
861
- if (!ctx) {
862
- res.statusCode = 500;
863
- res.write(JSON.stringify({
864
- error: this.makeResponseError((_a = reply.error) !== null && _a !== void 0 ? _a : new error_1.Fatal("Response without context")),
865
- }));
866
- res.end();
867
- return;
868
- }
869
- const deltaTime = process.hrtime(hrStart);
870
- const duration = deltaTime[0] + deltaTime[1] * 1e-9;
871
- if (reply.error) {
872
- console.error(reply.error);
873
- }
874
- this.log(`${ctx.request.id} [${duration.toFixed(6)}s] ${ctx.request.name}() -> ${reply.error ? this.makeResponseError(reply.error).type : "OK"}`);
875
- if (ctx.response.statusCode) {
876
- res.statusCode = ctx.response.statusCode;
877
- }
878
- for (const [headerKey, headerValue] of ctx.response.headers.entries()) {
879
- res.setHeader(headerKey, headerValue);
880
- }
881
- switch (ctx.request.version) {
882
- case 1: {
883
- const response = {
884
- deviceId: ctx.request.deviceInfo.id,
885
- duration,
886
- error: reply.error ? this.makeResponseError(reply.error) : null,
887
- host: (0, os_1.hostname)(),
888
- id: ctx.request.id,
889
- ok: !reply.error,
890
- result: reply.error ? null : reply.result,
891
- };
892
- if (response.error && !ctx.response.statusCode) {
893
- res.statusCode = this.makeResponseError(response.error).type === "Fatal" ? 500 : 400;
894
- }
895
- res.write(JSON.stringify(response));
896
- res.end();
897
- break;
898
- }
899
- case 2: {
900
- const response = {
901
- deviceId: ctx.request.deviceInfo.id,
902
- error: reply.error ? this.makeResponseError(reply.error) : null,
903
- ok: !reply.error,
904
- requestId: ctx.request.id,
905
- result: reply.error ? null : reply.result,
906
- sessionId: ctx.request.extra.sessionId,
907
- };
908
- if (response.error && !ctx.response.statusCode) {
909
- res.statusCode = this.makeResponseError(response.error).type === "Fatal" ? 500 : 400;
910
- }
911
- res.write(JSON.stringify(response));
912
- res.end();
913
- break;
914
- }
915
- case 3: {
916
- const response = {
917
- duration,
918
- error: reply.error ? this.makeResponseError(reply.error) : null,
919
- host: (0, os_1.hostname)(),
920
- result: reply.error ? null : reply.result,
921
- };
922
- if (response.error && !ctx.response.statusCode) {
923
- res.statusCode = this.makeResponseError(response.error).type === "Fatal" ? 500 : 400;
924
- }
925
- res.setHeader("x-request-id", ctx.request.id);
926
- res.write(JSON.stringify(response));
927
- res.end();
928
- break;
929
- }
930
- default: {
931
- res.statusCode = 500;
932
- res.write(JSON.stringify({
933
- error: this.makeResponseError((_b = reply.error) !== null && _b !== void 0 ? _b : new error_1.Fatal("Unknown request version")),
934
- }));
935
- res.end();
936
- return;
937
- }
938
- }
939
- }
940
- }
941
- exports.SdkgenHttpServer = SdkgenHttpServer;