equipped 5.0.0-alpha.13 → 5.0.0-alpha.14

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 CHANGED
@@ -2,6 +2,14 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
4
4
 
5
+ ## [5.0.0-alpha.14](https://github.com/kevinand11/equipped/compare/v5.0.0-alpha.13...v5.0.0-alpha.14) (2024-07-07)
6
+
7
+
8
+ ### Features
9
+
10
+ * add redocs doc ([f98347e](https://github.com/kevinand11/equipped/commit/f98347e5b815467a9c2a8bff97bdbfa166bc4c7d))
11
+ * redirect to scalar doc ([884f271](https://github.com/kevinand11/equipped/commit/884f271910d4bc2b1f2d50a6bbcf76f6889db85e))
12
+
5
13
  ## [5.0.0-alpha.13](https://github.com/kevinand11/equipped/compare/v5.0.0-alpha.12...v5.0.0-alpha.13) (2024-07-04)
6
14
 
7
15
  ## [5.0.0-alpha.12](https://github.com/kevinand11/equipped/compare/v5.0.0-alpha.11...v5.0.0-alpha.12) (2024-07-04)
@@ -52,7 +52,7 @@ class MongoDbChange extends _instance_1.DbChange {
52
52
  } : {}),
53
53
  'collection.include.list': dbColName
54
54
  });
55
- const hexId = '5f5f65717569707065645f5f'; // __equipped__
55
+ const hexId = '5f5f65717569707065645f5f';
56
56
  const TestId = new mongoose_1.default.Types.ObjectId(hexId);
57
57
  const hydrate = (data) => model.hydrate({
58
58
  ...data, _id: makeId(data._id['$oid'] ?? data._id)
@@ -71,6 +71,5 @@ class MongoDb extends _instance_1.Db {
71
71
  }
72
72
  exports.MongoDb = MongoDb;
73
73
  _MongoDb_started = new WeakMap(), _MongoDb_instances = new WeakSet(), _MongoDb_connections_get = function _MongoDb_connections_get() {
74
- // @ts-ignore
75
74
  return mongoose_1.default.connection.otherDbs ?? [];
76
75
  };
@@ -4,7 +4,6 @@ exports.parseMongodbQueryParams = void 0;
4
4
  const instance_1 = require("../../instance");
5
5
  const query_1 = require("../query");
6
6
  const parseMongodbQueryParams = async (model, params) => {
7
- // Handle where clauses
8
7
  const query = [];
9
8
  const whereType = Object.values(query_1.QueryKeys).indexOf(params.whereType) !== -1 ? params.whereType : query_1.QueryKeys.and;
10
9
  const authType = Object.values(query_1.QueryKeys).indexOf(params.authType) !== -1 ? params.authType : query_1.QueryKeys.and;
@@ -25,13 +24,10 @@ const parseMongodbQueryParams = async (model, params) => {
25
24
  const totalClause = {};
26
25
  if (query.length > 0)
27
26
  totalClause['$and'] = query;
28
- // Handle sort clauses
29
27
  const sort = params.sort?.map((p) => [p.field, p.desc ? 'desc' : 'asc']) ?? [];
30
28
  const all = params.all ?? false;
31
- // Handle limit clause
32
29
  const settings = instance_1.Instance.get().settings;
33
30
  const limit = Number(params.limit) <= settings.paginationDefaultLimit ? Number(params.limit) : settings.paginationDefaultLimit;
34
- // Handle offset clause
35
31
  let page = Number.isNaN(Number(params.page)) ? 0 : Number(params.page);
36
32
  page = page < 1 ? 1 : page;
37
33
  const total = await model.countDocuments(totalClause).catch(() => {
@@ -7,7 +7,6 @@ exports.readEmailFromPug = void 0;
7
7
  const path_1 = __importDefault(require("path"));
8
8
  const pug_1 = __importDefault(require("pug"));
9
9
  const readEmailFromPug = async (filePath, data) => {
10
- // filePath needs to be relative to the root of the running process
11
10
  const file = path_1.default.join('./', filePath);
12
11
  return pug_1.default.renderFile(file, data);
13
12
  };
package/lib/exit.js CHANGED
@@ -7,7 +7,6 @@ const addWaitBeforeExit = (fn) => {
7
7
  };
8
8
  exports.addWaitBeforeExit = addWaitBeforeExit;
9
9
  const exit = (message) => {
10
- // eslint-disable-next-line no-console
11
10
  console.error(message);
12
11
  process.exit(1);
13
12
  };
@@ -22,7 +21,6 @@ Object.entries(signals).forEach(([signal, code]) => {
22
21
  await Promise.all(listeners.map(async (l) => {
23
22
  try {
24
23
  await typeof l === 'function' ? l() : l;
25
- // eslint-disable-next-line no-empty
26
24
  }
27
25
  catch (err) {
28
26
  }
@@ -28,12 +28,31 @@ const ts_oas_1 = __importStar(require("ts-oas"));
28
28
  const instance_1 = require("../instance");
29
29
  const server_1 = require("../server");
30
30
  const statusCodes = Object.entries(server_1.StatusCodes);
31
+ const fileSchema = { type: 'string', format: 'binary', example: 'uploaded binary file' };
32
+ function isFile(schema) {
33
+ return schema.type === 'string' && schema.enum?.at(0) === 'equipped-file-schema';
34
+ }
31
35
  function generateJSONSchema(patterns, paths, options) {
32
36
  const tsProgram = (0, ts_oas_1.createProgram)(paths, options?.tsConfig);
33
37
  const logger = instance_1.Instance.createLogger();
34
38
  const tsoas = new ts_oas_1.default(tsProgram, {
35
39
  ref: false,
36
40
  nullableKeyword: false,
41
+ schemaProcessor: (schema) => {
42
+ if (isFile(schema))
43
+ return fileSchema;
44
+ if (schema.anyOf) {
45
+ const index = schema.anyOf.findIndex(isFile);
46
+ if (index !== -1)
47
+ schema.anyOf[index] = fileSchema;
48
+ }
49
+ if (schema.allOf) {
50
+ const index = schema.allOf.findIndex(isFile);
51
+ if (index !== -1)
52
+ schema.allOf[index] = fileSchema;
53
+ }
54
+ return schema;
55
+ },
37
56
  ...(options?.options ?? {})
38
57
  });
39
58
  const jsonSchema = tsoas.getSchemas(patterns);
@@ -24,6 +24,7 @@ const listeners_1 = require("../../listeners");
24
24
  const middlewares_1 = require("../middlewares");
25
25
  const requests_1 = require("../requests");
26
26
  const routes_1 = require("../routes");
27
+ const types_1 = require("../types");
27
28
  class Server {
28
29
  constructor(server) {
29
30
  _Server_instances.add(this);
@@ -87,11 +88,31 @@ class Server {
87
88
  return (0, supertest_1.default)(this.server);
88
89
  }
89
90
  async start(port) {
91
+ this.addRoute({
92
+ method: 'get',
93
+ path: `${this.settings.openapiDocsPath}/`,
94
+ handler: () => new requests_1.Response({
95
+ status: types_1.StatusCodes.Found,
96
+ headers: { 'Location': './index.html' },
97
+ }),
98
+ hideSchema: true,
99
+ });
90
100
  this.addRoute({
91
101
  method: 'get',
92
102
  path: `${this.settings.openapiDocsPath}/index.html`,
93
103
  handler: () => new requests_1.Response({
94
- body: openapiHtml
104
+ body: scalarHtml
105
+ .replaceAll('__API_TITLE__', this.settings.appId)
106
+ .replaceAll('__OPENAPI_JSON_URL__', './openapi.json'),
107
+ headers: { 'Content-Type': 'text/html' },
108
+ }),
109
+ hideSchema: true,
110
+ });
111
+ this.addRoute({
112
+ method: 'get',
113
+ path: `${this.settings.openapiDocsPath}/redoc.html`,
114
+ handler: () => new requests_1.Response({
115
+ body: redocHtml
95
116
  .replaceAll('__API_TITLE__', this.settings.appId)
96
117
  .replaceAll('__OPENAPI_JSON_URL__', './openapi.json'),
97
118
  headers: { 'Content-Type': 'text/html' },
@@ -163,7 +184,7 @@ _Server_routesByPath = new WeakMap(), _Server_routesByKey = new WeakMap(), _Serv
163
184
  __classPrivateFieldGet(this, _Server_routesByKey, "f").set(key, fullRoute);
164
185
  this.registerRoute(fullRoute);
165
186
  };
166
- const openapiHtml = `
187
+ const scalarHtml = `
167
188
  <!doctype html>
168
189
  <html>
169
190
  <head>
@@ -182,7 +203,27 @@ const openapiHtml = `
182
203
  const configuration = { theme: 'purple' };
183
204
  document.getElementById('api-reference').dataset.configuration = JSON.stringify(configuration);
184
205
  </script>
185
- <script src="https://cdn.jsdelivr.net/npm/@scalar/api-reference@1.24.30"></script>
206
+ <script src="https://cdn.jsdelivr.net/npm/@scalar/api-reference@1.24.36"></script>
207
+ </body>
208
+ </html>
209
+ `;
210
+ const redocHtml = `
211
+ <!doctype html>
212
+ <html>
213
+ <head>
214
+ <title>__API_TITLE__</title>
215
+ <meta charset="utf-8" />
216
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
217
+ <style>
218
+ body {
219
+ margin: 0;
220
+ padding: 0;
221
+ }
222
+ </style>
223
+ </head>
224
+ <body>
225
+ <redoc spec-url="__OPENAPI_JSON_URL__"></redoc>
226
+ <script src="https://cdn.redoc.ly/redoc/v2.1.5/bundles/redoc.standalone.js"> </script>
186
227
  </body>
187
228
  </html>
188
229
  `;
@@ -25,7 +25,6 @@ const express_fileupload_1 = __importDefault(require("express-fileupload"));
25
25
  const express_rate_limit_1 = __importDefault(require("express-rate-limit"));
26
26
  const helmet_1 = __importDefault(require("helmet"));
27
27
  const http_1 = __importDefault(require("http"));
28
- // @ts-ignore
29
28
  const json_schema_resolver_1 = __importDefault(require("json-schema-resolver"));
30
29
  const pino_http_1 = require("pino-http");
31
30
  const errors_1 = require("../../errors");
@@ -68,11 +67,6 @@ class ExpressServer extends base_1.Server {
68
67
  limit: this.settings.rateLimit,
69
68
  handler: (_, res) => res.status(types_1.StatusCodes.TooManyRequests).json([{ message: 'Too Many Requests' }])
70
69
  }));
71
- /* if (this.settings.useSlowDown) app.use(slowDown({
72
- windowMs: this.settings.slowDownPeriodInMs,
73
- delayAfter: this.settings.slowDownAfter,
74
- delayMs: this.settings.slowDownDelayInMs
75
- })) */
76
70
  }
77
71
  registerRoute(route) {
78
72
  const openapi = (0, utils_1.prepareOpenapiMethod)(route.schema, __classPrivateFieldGet(this, _ExpressServer_ref, "f"), this.baseOpenapiDoc, route.path);
@@ -131,7 +125,6 @@ class ExpressServer extends base_1.Server {
131
125
  })));
132
126
  return [key, fileArray];
133
127
  })));
134
- // @ts-ignore
135
128
  return req.savedReq || (req.savedReq = new requests_1.Request({
136
129
  ip: req.ip,
137
130
  body: req.body ?? {},
@@ -25,7 +25,6 @@ const rate_limit_1 = __importDefault(require("@fastify/rate-limit"));
25
25
  const static_1 = __importDefault(require("@fastify/static"));
26
26
  const swagger_1 = __importDefault(require("@fastify/swagger"));
27
27
  const fastify_1 = __importDefault(require("fastify"));
28
- // import fastifySlowDown from 'fastify-slow-down'
29
28
  const qs_1 = __importDefault(require("qs"));
30
29
  const errors_1 = require("../../errors");
31
30
  const exit_1 = require("../../exit");
@@ -75,15 +74,9 @@ class FastifyServer extends base_1.Server {
75
74
  data: buffer,
76
75
  duration: await (0, media_1.getMediaDuration)(buffer),
77
76
  };
78
- // @ts-ignore
79
77
  f.value = parsed;
80
78
  }
81
79
  });
82
- /* if (this.settings.useSlowDown) app.register(fastifySlowDown, {
83
- timeWindow: this.settings.slowDownPeriodInMs,
84
- delayAfter: this.settings.slowDownAfter,
85
- delay: this.settings.slowDownDelayInMs
86
- }) */
87
80
  if (this.settings.useRateLimit)
88
81
  app.register(rate_limit_1.default, {
89
82
  max: this.settings.rateLimit,
@@ -147,9 +140,8 @@ class FastifyServer extends base_1.Server {
147
140
  const handler = async (req, reply) => {
148
141
  const rawResponse = await cb(await this.parse(req, reply));
149
142
  const response = rawResponse instanceof requests_1.Response ? rawResponse : new requests_1.Response({ body: rawResponse });
150
- const type = response.shouldJSONify ? 'json' : 'send';
151
143
  if (!response.piped)
152
- reply.status(response.status).headers(response.headers)[type](response.body);
144
+ reply.status(response.status).headers(response.headers).send(response.body);
153
145
  };
154
146
  return handler;
155
147
  }
@@ -5,23 +5,26 @@ import { Defined } from '../types';
5
5
  import { AuthUser, RefreshUser } from '../utils/authUser';
6
6
  import { Api, FileSchema, SupportedStatusCodes } from './types';
7
7
  type HeaderKeys = 'AccessToken' | 'RefreshToken' | 'Referer' | 'ContentType' | 'UserAgent';
8
- type IsFileOrFileArray<T> = T extends FileSchema ? T : T extends FileSchema[] ? T : never;
9
- type ApiToFiles<Def extends Api> = {
10
- [K in keyof Def['body'] as IsFileOrFileArray<Def['body'][K]> extends never ? never : K]: StorageFile[];
8
+ type IsFileOrFileArray<T> = T extends FileSchema ? StorageFile[] : T extends FileSchema[] ? StorageFile[] : T;
9
+ type ApiToBody<Def extends Api> = MappedUnion<Def['body']>;
10
+ type UnionMapper<T> = {
11
+ [K in T extends infer P ? keyof P : never]: T extends infer P ? K extends keyof P ? {
12
+ [Q in keyof T]: IsFileOrFileArray<T[Q]>;
13
+ } : never : never;
11
14
  };
15
+ type MappedUnion<T> = UnionMapper<T>[keyof UnionMapper<T>];
12
16
  export declare class Request<Def extends Api = Api> {
13
17
  #private;
14
18
  private readonly response;
15
19
  readonly ip: string | undefined;
16
20
  readonly method: Def['method'];
17
21
  readonly path: string;
18
- readonly body: Def['body'];
22
+ readonly body: ApiToBody<Def>;
19
23
  readonly params: Defined<Def['params']>;
20
24
  readonly query: Defined<Def['query']>;
21
25
  readonly cookies: Record<string, any>;
22
26
  readonly rawBody: unknown;
23
27
  readonly headers: Record<HeaderKeys, string | null> & Record<string, string | string[] | null>;
24
- readonly files: ApiToFiles<Def>;
25
28
  authUser: null | AuthUser;
26
29
  refreshUser: null | RefreshUser;
27
30
  pendingError: null | CustomError;
@@ -32,7 +35,7 @@ export declare class Request<Def extends Api = Api> {
32
35
  query: Def['query'];
33
36
  cookies: Record<string, any>;
34
37
  headers: Record<HeaderKeys, string | null> & Record<string, string | string[] | null>;
35
- files: ApiToFiles<Def>;
38
+ files: Record<string, StorageFile[]>;
36
39
  method: Def['method'];
37
40
  path: string;
38
41
  }, response: Writable);
@@ -20,8 +20,8 @@ class Request {
20
20
  this.method = method;
21
21
  this.path = path;
22
22
  this.rawBody = body;
23
- this.body = Object.fromEntries(Object.entries(body && typeof body === 'object' ? body : { raw: body })
24
- .map(([key, value]) => [key, (0, json_1.parseJSONValue)(value)]));
23
+ this.body = Object.assign(Object.fromEntries(Object.entries(body && typeof body === 'object' ? body : { raw: body })
24
+ .map(([key, value]) => [key, (0, json_1.parseJSONValue)(value)])), files);
25
25
  this.cookies = cookies;
26
26
  this.params = params;
27
27
  this.query = Object.fromEntries(Object.entries(query && typeof body === 'object' ? query : {})
@@ -31,7 +31,6 @@ class Request {
31
31
  if (this.query?.['authType'])
32
32
  delete this.query['authType'];
33
33
  this.headers = headers;
34
- this.files = files;
35
34
  }
36
35
  pipe(cb) {
37
36
  cb(this.response);
@@ -13,6 +13,7 @@ export declare const Methods: {
13
13
  export type MethodTypes = Enum<typeof Methods>;
14
14
  export declare const StatusCodes: {
15
15
  readonly Ok: 200;
16
+ readonly Found: 302;
16
17
  readonly BadRequest: 400;
17
18
  readonly NotAuthenticated: 401;
18
19
  readonly NotAuthorized: 403;
@@ -22,7 +23,7 @@ export declare const StatusCodes: {
22
23
  readonly AccessTokenExpired: 461;
23
24
  };
24
25
  export type SupportedStatusCodes = Enum<typeof StatusCodes>;
25
- type GoodStatusCodes = 200;
26
+ type GoodStatusCodes = 200 | 302;
26
27
  type ApiErrors = Record<Exclude<SupportedStatusCodes, GoodStatusCodes>, JSONValue<CustomError['serializedErrors']>>;
27
28
  type ApiResponse<T, StatusCode extends SupportedStatusCodes> = Record<StatusCode, JSONValue<T>> | Omit<ApiErrors, StatusCode>;
28
29
  export interface Api<Res = any, Key extends string = string, Method extends MethodTypes = MethodTypes, Body = any, Params extends Record<string, string> = Record<string, string>, Query extends Record<string, any> = Record<string, any>, RequestHeaders extends Record<string, string | string[]> = Record<string, string | string[]>, ResponeHeaders extends Record<string, string | string[]> = Record<string, string | string[]>, DefaultStatus extends SupportedStatusCodes = SupportedStatusCodes> {
@@ -36,11 +37,7 @@ export interface Api<Res = any, Key extends string = string, Method extends Meth
36
37
  responseHeaders?: ResponeHeaders;
37
38
  defaultStatusCode?: DefaultStatus;
38
39
  }
39
- /**
40
- * @format binary
41
- * @example uploaded binary file
42
- */
43
- export type FileSchema = string;
40
+ export type FileSchema = 'equipped-file-schema';
44
41
  export interface ApiDef<T extends Api> {
45
42
  key: Defined<T['key']>;
46
43
  method: Defined<T['method']>;
@@ -10,6 +10,7 @@ exports.Methods = {
10
10
  };
11
11
  exports.StatusCodes = {
12
12
  Ok: 200,
13
+ Found: 302,
13
14
  BadRequest: 400,
14
15
  NotAuthenticated: 401,
15
16
  NotAuthorized: 403,
package/lib/utils/auth.js CHANGED
@@ -36,7 +36,6 @@ const signinWithApple = async (idToken) => {
36
36
  throw new Error('');
37
37
  if (data.exp * 1000 < Date.now())
38
38
  throw new Error('expired idToken');
39
- // TODO: Find out how to get profile data from api
40
39
  return data;
41
40
  }
42
41
  catch (err) {
@@ -28,7 +28,6 @@ const verifyAccessToken = async (token) => {
28
28
  if (!user)
29
29
  throw new errors_1.NotAuthenticatedError();
30
30
  const cachedToken = await (0, exports.getCachedAccessToken)(user.id);
31
- // Cached access token was deleted, e.g. by user roles being modified, so token needs to be treated as expired
32
31
  if (token && token !== cachedToken)
33
32
  throw new errors_1.AccessTokenExpired();
34
33
  return user;
@@ -71,19 +70,9 @@ const exchangeOldForNewTokens = async ({ accessToken, refreshToken }, makeTokens
71
70
  else
72
71
  throw err;
73
72
  });
74
- // If auth token is not expired, get the user id from it
75
73
  if (authUser)
76
74
  return await makeTokens(authUser.id);
77
75
  const refreshUser = await (0, exports.verifyRefreshToken)(refreshToken);
78
- // const cachedRefreshToken = await getCachedRefreshToken(refreshUser.id)
79
- // If no cached value, means someone used your old token for a second time, so current one got deleted from cache
80
- // if (!cachedRefreshToken) throw new NotAuthenticatedError()
81
- // If cached value is not equal, means someone is trying to use an old token for a second time
82
- // if (refreshToken !== cachedRefreshToken) {
83
- // await deleteCachedAccessToken(refreshUser.id)
84
- // await deleteCachedRefreshToken(refreshUser.id)
85
- // throw new RefreshTokenMisusedError()
86
- // }
87
76
  return await makeTokens(refreshUser.id);
88
77
  };
89
78
  exports.exchangeOldForNewTokens = exchangeOldForNewTokens;
@@ -48,9 +48,8 @@ const file = Validate.v.file;
48
48
  Validate.v.file = (...args) => file(...args).addRule(isNotTruncated());
49
49
  exports.Schema = Validate.v;
50
50
  exports.Validation = { ...Validate, isNotTruncated, isValidPhone };
51
- // eslint-disable-next-line no-redeclare
52
51
  function validate(schema, value) {
53
- const validator = /* schema instanceof Validate.VCore ? schema : */ Validate.v.object(schema);
52
+ const validator = Validate.v.object(schema);
54
53
  const validity = validator.parse(value);
55
54
  if (validity.valid)
56
55
  return validity.value;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "equipped",
3
- "version": "5.0.0-alpha.13",
3
+ "version": "5.0.0-alpha.14",
4
4
  "private": false,
5
5
  "description": "",
6
6
  "main": "lib/index.js",