@sdkgen/node-runtime 2.1.0 → 2.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -29,6 +29,7 @@ function typeToSchema(definitions, type) {
29
29
  ])),
30
30
  required: type.fields.filter(f => !(f.type instanceof parser_1.OptionalType)).map(f => f.name),
31
31
  type: "object",
32
+ additionalProperties: false,
32
33
  };
33
34
  }
34
35
  else if (type instanceof parser_1.StringPrimitiveType ||
@@ -113,6 +114,14 @@ function typeToSchema(definitions, type) {
113
114
  type: "string",
114
115
  };
115
116
  }
117
+ else if (type instanceof parser_1.DecimalPrimitiveType) {
118
+ return {
119
+ type: "string",
120
+ };
121
+ }
122
+ else if (type instanceof parser_1.JsonPrimitiveType) {
123
+ return {};
124
+ }
116
125
  else if (type instanceof parser_1.OptionalType) {
117
126
  return {
118
127
  oneOf: [typeToSchema(definitions, type.base), { type: "null" }],
@@ -128,10 +137,199 @@ function typeToSchema(definitions, type) {
128
137
  if (!definitions[type.name]) {
129
138
  definitions[type.name] = typeToSchema(definitions, type.type);
130
139
  }
131
- return { $ref: `#/definitions/${type.name}` };
140
+ return { $ref: `#/components/schemas/${type.name}` };
132
141
  }
133
142
  throw new Error(`Unhandled type ${type.constructor.name}`);
134
143
  }
144
+ function getSwaggerJson(apiConfig) {
145
+ var _a;
146
+ const schemas = {};
147
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
148
+ const paths = {};
149
+ for (const op of apiConfig.ast.operations) {
150
+ const throwAnnotations = op.annotations.filter(ann => ann instanceof parser_1.ThrowsAnnotation);
151
+ let possibleErrors = throwAnnotations.map(ann => apiConfig.ast.errors.find(err => err.name === ann.error)).filter(x => x);
152
+ if (possibleErrors.length === 0) {
153
+ possibleErrors = apiConfig.ast.errors;
154
+ }
155
+ const errorsByStatus = new Map();
156
+ for (const error of possibleErrors) {
157
+ const statusAnnotation = error.annotations.find(ann => ann instanceof parser_1.StatusCodeAnnotation);
158
+ const statusCode = statusAnnotation ? statusAnnotation.statusCode : error.name === "Fatal" ? 500 : 400;
159
+ const errorList = (_a = errorsByStatus.get(statusCode)) !== null && _a !== void 0 ? _a : [];
160
+ errorList.push(error);
161
+ errorsByStatus.set(statusCode, errorList);
162
+ }
163
+ const errorResponses = Object.fromEntries([...errorsByStatus.entries()].map(([status, errors]) => [
164
+ status,
165
+ {
166
+ description: errors
167
+ .map(error => error.name)
168
+ .sort((a, b) => a.localeCompare(b))
169
+ .join("<br>"),
170
+ content: {
171
+ "application/json": {
172
+ schema: {
173
+ anyOf: errors.map(error => ({
174
+ properties: Object.assign({ message: {
175
+ type: "string",
176
+ }, type: {
177
+ enum: [error.name],
178
+ type: "string",
179
+ } }, (error.dataType instanceof parser_1.VoidPrimitiveType
180
+ ? {}
181
+ : {
182
+ data: typeToSchema(schemas, error.dataType),
183
+ })),
184
+ required: ["type", "message", ...(error.dataType instanceof parser_1.VoidPrimitiveType ? [] : ["data"])],
185
+ type: "object",
186
+ additionalProperties: false,
187
+ })),
188
+ },
189
+ },
190
+ },
191
+ },
192
+ ]));
193
+ for (const ann of op.annotations) {
194
+ if (ann instanceof parser_1.RestAnnotation) {
195
+ if (!paths[ann.path]) {
196
+ paths[ann.path] = {};
197
+ }
198
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
199
+ paths[ann.path][ann.method.toLowerCase()] = {
200
+ operationId: op.name,
201
+ parameters: [
202
+ ...ann.pathVariables.map(name => ({
203
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
204
+ arg: op.args.find(arg => arg.name === name),
205
+ location: "path",
206
+ name,
207
+ })),
208
+ ...ann.queryVariables.map(name => ({
209
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
210
+ arg: op.args.find(arg => arg.name === name),
211
+ location: "query",
212
+ name,
213
+ })),
214
+ ...[...ann.headers.entries()].map(([header, name]) => ({
215
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
216
+ arg: op.args.find(arg => arg.name === name),
217
+ location: "header",
218
+ name: header,
219
+ })),
220
+ ].map(({ name, location, arg }) => ({
221
+ description: arg.annotations
222
+ .filter(x => x instanceof parser_1.DescriptionAnnotation)
223
+ .map(x => x.text)
224
+ .join(" ") || undefined,
225
+ in: location,
226
+ name,
227
+ required: !(arg.type instanceof parser_1.OptionalType),
228
+ schema: typeToSchema(schemas, arg.type),
229
+ })),
230
+ requestBody: ann.bodyVariable
231
+ ? {
232
+ content: Object.assign(Object.assign({}, (() => {
233
+ var _a;
234
+ const bodyType = (_a = op.args.find(arg => arg.name === ann.bodyVariable)) === null || _a === void 0 ? void 0 : _a.type;
235
+ return bodyType instanceof parser_1.BoolPrimitiveType ||
236
+ bodyType instanceof parser_1.IntPrimitiveType ||
237
+ bodyType instanceof parser_1.UIntPrimitiveType ||
238
+ bodyType instanceof parser_1.FloatPrimitiveType ||
239
+ bodyType instanceof parser_1.StringPrimitiveType ||
240
+ bodyType instanceof parser_1.DatePrimitiveType ||
241
+ bodyType instanceof parser_1.DateTimePrimitiveType ||
242
+ bodyType instanceof parser_1.MoneyPrimitiveType ||
243
+ bodyType instanceof parser_1.CpfPrimitiveType ||
244
+ bodyType instanceof parser_1.CnpjPrimitiveType ||
245
+ bodyType instanceof parser_1.EmailPrimitiveType ||
246
+ bodyType instanceof parser_1.HtmlPrimitiveType ||
247
+ bodyType instanceof parser_1.UuidPrimitiveType ||
248
+ bodyType instanceof parser_1.HexPrimitiveType ||
249
+ bodyType instanceof parser_1.BytesPrimitiveType ||
250
+ bodyType instanceof parser_1.Base64PrimitiveType
251
+ ? {
252
+ [bodyType instanceof parser_1.HtmlPrimitiveType ? "text/html" : "text/plain"]: {
253
+ schema: typeToSchema(schemas, bodyType),
254
+ },
255
+ }
256
+ : {};
257
+ })()), { "application/json": {
258
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
259
+ schema: typeToSchema(schemas, op.args.find(arg => arg.name === ann.bodyVariable).type),
260
+ } }),
261
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
262
+ required: !(op.args.find(arg => arg.name === ann.bodyVariable).type instanceof parser_1.OptionalType),
263
+ }
264
+ : undefined,
265
+ responses: Object.assign(Object.assign(Object.assign({}, (op.returnType instanceof parser_1.OptionalType || op.returnType instanceof parser_1.VoidPrimitiveType
266
+ ? { [ann.method === "GET" ? "404" : "204"]: {} }
267
+ : {})), (op.returnType instanceof parser_1.VoidPrimitiveType
268
+ ? {}
269
+ : {
270
+ 200: {
271
+ description: "",
272
+ content: Object.assign(Object.assign({}, (() => {
273
+ return op.returnType instanceof parser_1.BoolPrimitiveType ||
274
+ op.returnType instanceof parser_1.IntPrimitiveType ||
275
+ op.returnType instanceof parser_1.UIntPrimitiveType ||
276
+ op.returnType instanceof parser_1.FloatPrimitiveType ||
277
+ op.returnType instanceof parser_1.StringPrimitiveType ||
278
+ op.returnType instanceof parser_1.DatePrimitiveType ||
279
+ op.returnType instanceof parser_1.DateTimePrimitiveType ||
280
+ op.returnType instanceof parser_1.MoneyPrimitiveType ||
281
+ op.returnType instanceof parser_1.CpfPrimitiveType ||
282
+ op.returnType instanceof parser_1.CnpjPrimitiveType ||
283
+ op.returnType instanceof parser_1.EmailPrimitiveType ||
284
+ op.returnType instanceof parser_1.UuidPrimitiveType ||
285
+ op.returnType instanceof parser_1.HexPrimitiveType ||
286
+ op.returnType instanceof parser_1.BytesPrimitiveType ||
287
+ op.returnType instanceof parser_1.Base64PrimitiveType
288
+ ? {
289
+ "text/plain": {
290
+ schema: typeToSchema(schemas, op.returnType),
291
+ },
292
+ }
293
+ : {};
294
+ })()), { "application/json": {
295
+ schema: typeToSchema(schemas, op.returnType),
296
+ } }),
297
+ },
298
+ })), errorResponses),
299
+ summary: op.annotations
300
+ .filter(x => x instanceof parser_1.DescriptionAnnotation)
301
+ .map(x => x.text)
302
+ .join(" ") || undefined,
303
+ tags: [ann.path.split("/")[1]],
304
+ };
305
+ }
306
+ }
307
+ }
308
+ const securitySchemes = {
309
+ bearerAuth: {
310
+ type: "http",
311
+ scheme: "bearer",
312
+ },
313
+ };
314
+ const security = [
315
+ {
316
+ bearerAuth: [],
317
+ },
318
+ ];
319
+ return {
320
+ openapi: "3.0.0",
321
+ info: {
322
+ title: "",
323
+ version: "",
324
+ },
325
+ paths,
326
+ components: {
327
+ schemas,
328
+ securitySchemes,
329
+ },
330
+ security,
331
+ };
332
+ }
135
333
  function setupSwagger(server) {
136
334
  server.addHttpHandler("GET", "/swagger", (req, res) => {
137
335
  if (!server.introspection) {
@@ -139,6 +337,7 @@ function setupSwagger(server) {
139
337
  res.end();
140
338
  return;
141
339
  }
340
+ res.setHeader("content-type", "text/html");
142
341
  res.write(`
143
342
  <!DOCTYPE html>
144
343
  <html lang="en">
@@ -164,7 +363,7 @@ function setupSwagger(server) {
164
363
  background: #fafafa;
165
364
  }
166
365
 
167
- .swagger-ui .scheme-container, .swagger-ui .topbar {
366
+ .topbar {
168
367
  display: none !important;
169
368
  }
170
369
  </style>
@@ -172,12 +371,15 @@ function setupSwagger(server) {
172
371
 
173
372
  <body>
174
373
  <div id="swagger-ui"></div>
175
- <script src="/swagger/swagger-ui-bundle.js"> </script>
176
- <script src="/swagger/swagger-ui-standalone-preset.js"> </script>
374
+ <script src="swagger/swagger-ui-bundle.js"> </script>
375
+ <script src="swagger/swagger-ui-standalone-preset.js"> </script>
177
376
  <script>
178
377
  window.onload = function() {
179
378
  window.ui = SwaggerUIBundle({
180
- url: location.origin + "/swagger.json",
379
+ spec: {
380
+ ...${JSON.stringify(getSwaggerJson(server.apiConfig))},
381
+ servers: [{ url: location.origin + location.pathname.replace(/\\/swagger$/, "") }]
382
+ },
181
383
  dom_id: '#swagger-ui',
182
384
  deepLinking: true,
183
385
  presets: [
@@ -225,156 +427,7 @@ function setupSwagger(server) {
225
427
  return;
226
428
  }
227
429
  try {
228
- const definitions = {};
229
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
230
- const paths = {};
231
- for (const op of server.apiConfig.ast.operations) {
232
- for (const ann of op.annotations) {
233
- if (ann instanceof parser_1.RestAnnotation) {
234
- if (!paths[ann.path]) {
235
- paths[ann.path] = {};
236
- }
237
- // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
238
- paths[ann.path][ann.method.toLowerCase()] = {
239
- operationId: op.name,
240
- parameters: [
241
- ...ann.pathVariables.map(name => ({
242
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
243
- arg: op.args.find(arg => arg.name === name),
244
- location: "path",
245
- name,
246
- })),
247
- ...ann.queryVariables.map(name => ({
248
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
249
- arg: op.args.find(arg => arg.name === name),
250
- location: "query",
251
- name,
252
- })),
253
- ...[...ann.headers.entries()].map(([header, name]) => ({
254
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
255
- arg: op.args.find(arg => arg.name === name),
256
- location: "header",
257
- name: header,
258
- })),
259
- ].map(({ name, location, arg }) => ({
260
- description: arg.annotations
261
- .filter(x => x instanceof parser_1.DescriptionAnnotation)
262
- .map(x => x.text)
263
- .join(" ") || undefined,
264
- in: location,
265
- name,
266
- required: !(arg.type instanceof parser_1.OptionalType),
267
- schema: typeToSchema(definitions, arg.type),
268
- })),
269
- requestBody: ann.bodyVariable
270
- ? {
271
- content: Object.assign(Object.assign({}, (() => {
272
- var _a;
273
- const bodyType = (_a = op.args.find(arg => arg.name === ann.bodyVariable)) === null || _a === void 0 ? void 0 : _a.type;
274
- return bodyType instanceof parser_1.BoolPrimitiveType ||
275
- bodyType instanceof parser_1.IntPrimitiveType ||
276
- bodyType instanceof parser_1.UIntPrimitiveType ||
277
- bodyType instanceof parser_1.FloatPrimitiveType ||
278
- bodyType instanceof parser_1.StringPrimitiveType ||
279
- bodyType instanceof parser_1.DatePrimitiveType ||
280
- bodyType instanceof parser_1.DateTimePrimitiveType ||
281
- bodyType instanceof parser_1.MoneyPrimitiveType ||
282
- bodyType instanceof parser_1.CpfPrimitiveType ||
283
- bodyType instanceof parser_1.CnpjPrimitiveType ||
284
- bodyType instanceof parser_1.EmailPrimitiveType ||
285
- bodyType instanceof parser_1.HtmlPrimitiveType ||
286
- bodyType instanceof parser_1.UuidPrimitiveType ||
287
- bodyType instanceof parser_1.HexPrimitiveType ||
288
- bodyType instanceof parser_1.BytesPrimitiveType ||
289
- bodyType instanceof parser_1.Base64PrimitiveType
290
- ? {
291
- [bodyType instanceof parser_1.HtmlPrimitiveType ? "text/html" : "text/plain"]: {
292
- schema: typeToSchema(definitions, bodyType),
293
- },
294
- }
295
- : {};
296
- })()), { "application/json": {
297
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
298
- schema: typeToSchema(definitions, op.args.find(arg => arg.name === ann.bodyVariable).type),
299
- } }),
300
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
301
- required: !(op.args.find(arg => arg.name === ann.bodyVariable).type instanceof parser_1.OptionalType),
302
- }
303
- : undefined,
304
- responses: Object.assign(Object.assign(Object.assign({}, (op.returnType instanceof parser_1.OptionalType || op.returnType instanceof parser_1.VoidPrimitiveType
305
- ? { [ann.method === "GET" ? "404" : "204"]: {} }
306
- : {})), (op.returnType instanceof parser_1.VoidPrimitiveType
307
- ? {}
308
- : {
309
- 200: {
310
- content: Object.assign(Object.assign({}, (() => {
311
- return op.returnType instanceof parser_1.BoolPrimitiveType ||
312
- op.returnType instanceof parser_1.IntPrimitiveType ||
313
- op.returnType instanceof parser_1.UIntPrimitiveType ||
314
- op.returnType instanceof parser_1.FloatPrimitiveType ||
315
- op.returnType instanceof parser_1.StringPrimitiveType ||
316
- op.returnType instanceof parser_1.DatePrimitiveType ||
317
- op.returnType instanceof parser_1.DateTimePrimitiveType ||
318
- op.returnType instanceof parser_1.MoneyPrimitiveType ||
319
- op.returnType instanceof parser_1.CpfPrimitiveType ||
320
- op.returnType instanceof parser_1.CnpjPrimitiveType ||
321
- op.returnType instanceof parser_1.EmailPrimitiveType ||
322
- op.returnType instanceof parser_1.UuidPrimitiveType ||
323
- op.returnType instanceof parser_1.HexPrimitiveType ||
324
- op.returnType instanceof parser_1.BytesPrimitiveType ||
325
- op.returnType instanceof parser_1.Base64PrimitiveType
326
- ? {
327
- "text/plain": {
328
- schema: typeToSchema(definitions, op.returnType),
329
- },
330
- }
331
- : {};
332
- })()), { "application/json": {
333
- schema: typeToSchema(definitions, op.returnType),
334
- } }),
335
- },
336
- })), { 400: {
337
- content: {
338
- "application/json": {
339
- schema: {
340
- anyOf: [
341
- server.apiConfig.ast.errors.map(error => ({
342
- properties: Object.assign({ message: {
343
- type: "string",
344
- }, type: {
345
- enum: [error.name],
346
- type: "string",
347
- } }, (error.dataType instanceof parser_1.VoidPrimitiveType
348
- ? {}
349
- : {
350
- data: typeToSchema(definitions, error.dataType),
351
- })),
352
- required: ["type", "message"],
353
- type: "object",
354
- })),
355
- ],
356
- },
357
- },
358
- },
359
- }, 500: {} }),
360
- summary: op.annotations
361
- .filter(x => x instanceof parser_1.DescriptionAnnotation)
362
- .map(x => x.text)
363
- .join(" ") || undefined,
364
- tags: ["REST Endpoints"],
365
- };
366
- }
367
- }
368
- }
369
- res.write(JSON.stringify({
370
- consumes: ["application/json"],
371
- definitions,
372
- info: {},
373
- openapi: "3.0.3",
374
- paths,
375
- produces: ["application/json"],
376
- schemes: ["https"],
377
- }));
430
+ res.write(JSON.stringify(getSwaggerJson(server.apiConfig)));
378
431
  }
379
432
  catch (error) {
380
433
  console.error(error);
@@ -1,3 +1,2 @@
1
1
  import type { BaseApiConfig } from "./api-config";
2
- import type { Context } from "./context";
3
- export declare function apiTestWrapper<T>(api: T extends BaseApiConfig<Context & infer _ExtraContextT> ? T : never): T;
2
+ export declare function apiTestWrapper<ExtraContextT, ApiT extends BaseApiConfig<ExtraContextT>>(api: ApiT, extraContext?: Partial<ExtraContextT>): ApiT;
@@ -10,31 +10,33 @@ exports.apiTestWrapper = void 0;
10
10
  const crypto_1 = require("crypto");
11
11
  const encode_decode_1 = require("./encode-decode");
12
12
  const execute_1 = require("./execute");
13
- function apiTestWrapper(api) {
13
+ function apiTestWrapper(api, extraContext = {}) {
14
14
  const wrappedApi = new api.constructor();
15
15
  for (const functionName of Object.keys(api.astJson.functionTable)) {
16
- wrappedApi.fn[functionName] = async (ctx, args) => {
16
+ wrappedApi.fn[functionName] = async (partialCtx, args) => {
17
17
  var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
18
18
  const encodedArgs = (0, encode_decode_1.encode)(api.astJson.typeTable, `fn.${functionName}.args`, api.astJson.functionTable[functionName].args, args);
19
- ctx.request = {
20
- args: encodedArgs,
21
- deviceInfo: (_b = (_a = ctx.request) === null || _a === void 0 ? void 0 : _a.deviceInfo) !== null && _b !== void 0 ? _b : {
22
- fingerprint: null,
23
- id: (0, crypto_1.randomBytes)(16).toString("hex"),
24
- language: null,
25
- platform: null,
26
- timezone: null,
27
- type: "test",
28
- version: null,
29
- },
30
- extra: (_d = (_c = ctx.request) === null || _c === void 0 ? void 0 : _c.extra) !== null && _d !== void 0 ? _d : {},
31
- files: (_f = (_e = ctx.request) === null || _e === void 0 ? void 0 : _e.files) !== null && _f !== void 0 ? _f : [],
32
- headers: (_h = (_g = ctx.request) === null || _g === void 0 ? void 0 : _g.headers) !== null && _h !== void 0 ? _h : {},
33
- id: (_k = (_j = ctx.request) === null || _j === void 0 ? void 0 : _j.id) !== null && _k !== void 0 ? _k : (0, crypto_1.randomBytes)(16).toString("hex"),
34
- ip: (_m = (_l = ctx.request) === null || _l === void 0 ? void 0 : _l.ip) !== null && _m !== void 0 ? _m : "0.0.0.0",
35
- name: functionName,
36
- version: 3,
37
- };
19
+ const ctx = Object.assign(Object.assign(Object.assign({}, extraContext), partialCtx), { request: {
20
+ args: encodedArgs,
21
+ deviceInfo: (_b = (_a = partialCtx.request) === null || _a === void 0 ? void 0 : _a.deviceInfo) !== null && _b !== void 0 ? _b : {
22
+ fingerprint: null,
23
+ id: (0, crypto_1.randomBytes)(16).toString("hex"),
24
+ language: null,
25
+ platform: null,
26
+ timezone: null,
27
+ type: "test",
28
+ version: null,
29
+ },
30
+ extra: (_d = (_c = partialCtx.request) === null || _c === void 0 ? void 0 : _c.extra) !== null && _d !== void 0 ? _d : {},
31
+ files: (_f = (_e = partialCtx.request) === null || _e === void 0 ? void 0 : _e.files) !== null && _f !== void 0 ? _f : [],
32
+ headers: (_h = (_g = partialCtx.request) === null || _g === void 0 ? void 0 : _g.headers) !== null && _h !== void 0 ? _h : {},
33
+ id: (_k = (_j = partialCtx.request) === null || _j === void 0 ? void 0 : _j.id) !== null && _k !== void 0 ? _k : (0, crypto_1.randomBytes)(16).toString("hex"),
34
+ ip: (_m = (_l = partialCtx.request) === null || _l === void 0 ? void 0 : _l.ip) !== null && _m !== void 0 ? _m : "0.0.0.0",
35
+ name: functionName,
36
+ version: 3,
37
+ }, response: {
38
+ headers: new Map(),
39
+ } });
38
40
  const reply = await (0, execute_1.executeRequest)(ctx, api);
39
41
  if (reply.error) {
40
42
  throw reply.error;
@@ -1,4 +1,4 @@
1
- export declare type DeepReadonly<T> = T extends undefined | null | boolean | string | number | Function ? T : T extends [] ? readonly [] : T extends [infer U, ...infer Rest] ? readonly [DeepReadonly<U>, ...DeepReadonly<Rest>] : T extends Array<infer U> ? ReadonlyArray<DeepReadonly<U>> : T extends Map<infer K, infer V> ? ReadonlyMap<DeepReadonly<K>, DeepReadonly<V>> : T extends Set<infer U> ? ReadonlySet<DeepReadonly<U>> : {
1
+ export type DeepReadonly<T> = T extends undefined | null | boolean | string | number | Function ? T : T extends [] ? readonly [] : T extends [infer U, ...infer Rest] ? readonly [DeepReadonly<U>, ...DeepReadonly<Rest>] : T extends Array<infer U> ? ReadonlyArray<DeepReadonly<U>> : T extends Map<infer K, infer V> ? ReadonlyMap<DeepReadonly<K>, DeepReadonly<V>> : T extends Set<infer U> ? ReadonlySet<DeepReadonly<U>> : {
2
2
  readonly [K in keyof T]: DeepReadonly<T[K]>;
3
3
  };
4
4
  export declare function has<P extends PropertyKey>(target: object, property: P): target is {