@naturalcycles/backend-lib 9.20.0 → 9.22.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/cfg/tsconfig.json CHANGED
@@ -61,8 +61,8 @@
61
61
  // Other
62
62
  "pretty": true,
63
63
  "newLine": "lf",
64
- "experimentalDecorators": true,
65
- },
64
+ "experimentalDecorators": true
65
+ }
66
66
  // Need to be specified in the project tsconfig
67
67
  // "include": ["src"],
68
68
  // "exclude": ["**/__exclude"]
@@ -5,7 +5,7 @@ import { yaml2 } from '@naturalcycles/nodejs-lib/yaml2';
5
5
  import { resourcesDir } from '../paths.cnst.js';
6
6
  const getBackendCfgSchema = _lazyValue(() => {
7
7
  const schemaJson = fs2.readJson(`${resourcesDir}/backendCfg.schema.json`);
8
- return AjvSchema.create(schemaJson, { objectName: 'backend.cfg.yaml' });
8
+ return AjvSchema.create(schemaJson, { inputName: 'backend.cfg.yaml' });
9
9
  });
10
10
  export function getBackendCfg(projectDir = '.') {
11
11
  const backendCfgYamlPath = `${projectDir}/backend.cfg.yaml`;
@@ -1,2 +1 @@
1
- import type { DeployInfo } from './deploy.model.js';
2
- export declare const getDeployInfo: ((projectDir: string) => DeployInfo) & import("@naturalcycles/js-lib/decorators/memoFn.js").MemoizedFunction;
1
+ export declare const getDeployInfo: any;
@@ -5,15 +5,6 @@ declare class AjvValidateRequest {
5
5
  body<T>(req: BackendRequest, schema: AjvSchema<T>, opt?: ReqValidationOptions<AjvValidationError>): T;
6
6
  query<T>(req: BackendRequest, schema: AjvSchema<T>, opt?: ReqValidationOptions<AjvValidationError>): T;
7
7
  params<T>(req: BackendRequest, schema: AjvSchema<T>, opt?: ReqValidationOptions<AjvValidationError>): T;
8
- /**
9
- * Validates `req.headers` against the provided schema.
10
- *
11
- * Note: as opposed to other methods, this method does not mutate `req.headers` in case of success,
12
- * i.e. schemas that cast values will not have any effect.
13
- *
14
- * If you wish to mutate `req.headers` with the validated value, use `keepOriginal: false` option.
15
- * Keep in mind that this will also remove all values that are not in the schema.
16
- */
17
8
  headers<T>(req: BackendRequest, schema: AjvSchema<T>, opt?: ReqValidationOptions<AjvValidationError>): T;
18
9
  private validate;
19
10
  }
@@ -9,30 +9,20 @@ class AjvValidateRequest {
9
9
  params(req, schema, opt = {}) {
10
10
  return this.validate(req, 'params', schema, opt);
11
11
  }
12
- /**
13
- * Validates `req.headers` against the provided schema.
14
- *
15
- * Note: as opposed to other methods, this method does not mutate `req.headers` in case of success,
16
- * i.e. schemas that cast values will not have any effect.
17
- *
18
- * If you wish to mutate `req.headers` with the validated value, use `keepOriginal: false` option.
19
- * Keep in mind that this will also remove all values that are not in the schema.
20
- */
21
12
  headers(req, schema, opt = {}) {
22
13
  return this.validate(req, 'headers', schema, opt);
23
14
  }
24
15
  validate(req, reqProperty, schema, opt = {}) {
25
- const { mutate } = opt;
26
- const originalProperty = req[reqProperty] || {};
27
- const item = mutate ? originalProperty : { ...originalProperty };
28
- // Ajv mutates the input
29
- const error = schema.getValidationError(item, {
30
- objectName: `request ${reqProperty}`,
16
+ const { mutateInput = true } = opt;
17
+ const input = req[reqProperty] || {};
18
+ const [error, output] = schema.getValidationResult(input, {
19
+ mutateInput,
20
+ inputName: `request ${reqProperty}`,
31
21
  });
32
22
  if (error) {
33
- handleValidationError(error, originalProperty, opt);
23
+ handleValidationError(error, input, opt);
34
24
  }
35
- return item;
25
+ return output;
36
26
  }
37
27
  }
38
28
  export const ajvValidateRequest = new AjvValidateRequest();
@@ -23,10 +23,10 @@ class ValidateRequest {
23
23
  return this.validate(req, 'headers', schema, opt);
24
24
  }
25
25
  validate(req, reqProperty, schema, opt = {}) {
26
- const { mutate } = opt;
26
+ const { mutateInput } = opt;
27
27
  const originalProperty = req[reqProperty] || {};
28
28
  // Joi does not mutate the input
29
- const { error, value } = getValidationResult(originalProperty, schema, `request ${reqProperty}`);
29
+ const [error, value] = getValidationResult(originalProperty, schema, `request ${reqProperty}`);
30
30
  if (error) {
31
31
  if (opt.redactPaths) {
32
32
  error.data.joiValidationErrorItems.length = 0; // clears the array
@@ -34,7 +34,7 @@ class ValidateRequest {
34
34
  }
35
35
  handleValidationError(error, originalProperty, opt);
36
36
  }
37
- if (mutate) {
37
+ if (mutateInput) {
38
38
  req[reqProperty] = value;
39
39
  }
40
40
  return value;
@@ -12,11 +12,10 @@ export interface ReqValidationOptions<ERR extends AppError> {
12
12
  */
13
13
  report?: boolean | ((err: ERR) => boolean);
14
14
  /**
15
- * Defaults to false.
15
+ * Defaults to true.
16
16
  *
17
- * When set to true, the validated object will be replaced with the converted value.
18
- *
19
- * To avoid mutation - shallow copy is performed.
17
+ * When set to true, input (body, query, headers or params) will be mutated.
18
+ * So, e.g req.body will already contain mutated data post-validation.
20
19
  */
21
- mutate?: boolean;
20
+ mutateInput?: boolean;
22
21
  }
@@ -1,7 +1,6 @@
1
1
  import { AppError } from '@naturalcycles/js-lib/error/error.util.js';
2
2
  import { _get } from '@naturalcycles/js-lib/object/object.util.js';
3
3
  export function handleValidationError(error, originalProperty, opt = {}) {
4
- // const item: T = opt.mutate ? { ...req[reqProperty] } : (req[reqProperty] || {})
5
4
  let report;
6
5
  if (typeof opt.report === 'boolean') {
7
6
  report = opt.report;
@@ -23,14 +23,14 @@ class ZodValidateRequest {
23
23
  return this.validate(req, 'headers', schema, opt);
24
24
  }
25
25
  validate(req, reqProperty, schema, opt = {}) {
26
- const { mutate } = opt;
26
+ const { mutateInput } = opt;
27
27
  const originalProperty = req[reqProperty] || {};
28
28
  // Zod does not mutate the input
29
- const { error, data } = zSafeValidate(originalProperty, schema);
29
+ const [error, data] = zSafeValidate(originalProperty, schema);
30
30
  if (error) {
31
31
  handleValidationError(error, originalProperty, opt);
32
32
  }
33
- if (mutate) {
33
+ if (mutateInput) {
34
34
  req[reqProperty] = data;
35
35
  }
36
36
  return data;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@naturalcycles/backend-lib",
3
3
  "type": "module",
4
- "version": "9.20.0",
4
+ "version": "9.22.0",
5
5
  "peerDependencies": {
6
6
  "@sentry/node": "^9"
7
7
  },
@@ -29,7 +29,7 @@
29
29
  "@sentry/node": "^9",
30
30
  "@types/ejs": "^3",
31
31
  "fastify": "^5",
32
- "@naturalcycles/dev-lib": "18.4.2"
32
+ "@naturalcycles/dev-lib": "19.22.0"
33
33
  },
34
34
  "exports": {
35
35
  ".": "./dist/index.js",
@@ -42,7 +42,7 @@ export interface BackendCfg {
42
42
 
43
43
  const getBackendCfgSchema = _lazyValue(() => {
44
44
  const schemaJson = fs2.readJson<JsonSchema<BackendCfg>>(`${resourcesDir}/backendCfg.schema.json`)
45
- return AjvSchema.create(schemaJson, { objectName: 'backend.cfg.yaml' })
45
+ return AjvSchema.create(schemaJson, { inputName: 'backend.cfg.yaml' })
46
46
  })
47
47
 
48
48
  export function getBackendCfg(projectDir = '.'): BackendCfg {
@@ -27,15 +27,6 @@ class AjvValidateRequest {
27
27
  return this.validate(req, 'params', schema, opt)
28
28
  }
29
29
 
30
- /**
31
- * Validates `req.headers` against the provided schema.
32
- *
33
- * Note: as opposed to other methods, this method does not mutate `req.headers` in case of success,
34
- * i.e. schemas that cast values will not have any effect.
35
- *
36
- * If you wish to mutate `req.headers` with the validated value, use `keepOriginal: false` option.
37
- * Keep in mind that this will also remove all values that are not in the schema.
38
- */
39
30
  headers<T>(
40
31
  req: BackendRequest,
41
32
  schema: AjvSchema<T>,
@@ -50,20 +41,19 @@ class AjvValidateRequest {
50
41
  schema: AjvSchema<T>,
51
42
  opt: ReqValidationOptions<AjvValidationError> = {},
52
43
  ): T {
53
- const { mutate } = opt
54
- const originalProperty = req[reqProperty] || {}
55
- const item: T = mutate ? originalProperty : { ...originalProperty }
44
+ const { mutateInput = true } = opt
45
+ const input: T = req[reqProperty] || {}
56
46
 
57
- // Ajv mutates the input
58
- const error = schema.getValidationError(item, {
59
- objectName: `request ${reqProperty}`,
47
+ const [error, output] = schema.getValidationResult(input, {
48
+ mutateInput,
49
+ inputName: `request ${reqProperty}`,
60
50
  })
61
51
 
62
52
  if (error) {
63
- handleValidationError(error, originalProperty, opt)
53
+ handleValidationError(error, input, opt)
64
54
  }
65
55
 
66
- return item
56
+ return output
67
57
  }
68
58
  }
69
59
 
@@ -51,11 +51,11 @@ class ValidateRequest {
51
51
  schema: AnySchema<T>,
52
52
  opt: ReqValidationOptions<JoiValidationError> = {},
53
53
  ): T {
54
- const { mutate } = opt
54
+ const { mutateInput } = opt
55
55
  const originalProperty = req[reqProperty] || {}
56
56
 
57
57
  // Joi does not mutate the input
58
- const { error, value } = getValidationResult(originalProperty, schema, `request ${reqProperty}`)
58
+ const [error, value] = getValidationResult(originalProperty, schema, `request ${reqProperty}`)
59
59
 
60
60
  if (error) {
61
61
  if (opt.redactPaths) {
@@ -66,7 +66,7 @@ class ValidateRequest {
66
66
  handleValidationError(error, originalProperty, opt)
67
67
  }
68
68
 
69
- if (mutate) {
69
+ if (mutateInput) {
70
70
  req[reqProperty] = value
71
71
  }
72
72
 
@@ -6,8 +6,6 @@ export function handleValidationError<T, ERR extends AppError>(
6
6
  originalProperty: T,
7
7
  opt: ReqValidationOptions<ERR> = {},
8
8
  ): never {
9
- // const item: T = opt.mutate ? { ...req[reqProperty] } : (req[reqProperty] || {})
10
-
11
9
  let report: boolean | undefined
12
10
  if (typeof opt.report === 'boolean') {
13
11
  report = opt.report
@@ -54,11 +52,10 @@ export interface ReqValidationOptions<ERR extends AppError> {
54
52
  report?: boolean | ((err: ERR) => boolean)
55
53
 
56
54
  /**
57
- * Defaults to false.
58
- *
59
- * When set to true, the validated object will be replaced with the converted value.
55
+ * Defaults to true.
60
56
  *
61
- * To avoid mutation - shallow copy is performed.
57
+ * When set to true, input (body, query, headers or params) will be mutated.
58
+ * So, e.g req.body will already contain mutated data post-validation.
62
59
  */
63
- mutate?: boolean
60
+ mutateInput?: boolean
64
61
  }
@@ -50,11 +50,11 @@ class ZodValidateRequest {
50
50
  schema: ZodType<T>,
51
51
  opt: ReqValidationOptions<ZodValidationError> = {},
52
52
  ): T {
53
- const { mutate } = opt
53
+ const { mutateInput } = opt
54
54
  const originalProperty = req[reqProperty] || {}
55
55
 
56
56
  // Zod does not mutate the input
57
- const { error, data } = zSafeValidate(
57
+ const [error, data] = zSafeValidate(
58
58
  originalProperty,
59
59
  schema,
60
60
  // opt2?.itemName,
@@ -64,7 +64,7 @@ class ZodValidateRequest {
64
64
  handleValidationError(error, originalProperty, opt)
65
65
  }
66
66
 
67
- if (mutate) {
67
+ if (mutateInput) {
68
68
  req[reqProperty] = data
69
69
  }
70
70
 
@@ -1,150 +0,0 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <title>Login</title>
5
-
6
- <meta charset="utf-8" />
7
- <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
8
-
9
- <!-- CSS only -->
10
- <link
11
- href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.2/dist/css/bootstrap.min.css"
12
- rel="stylesheet"
13
- crossorigin="anonymous"
14
- />
15
- </head>
16
- <body>
17
- <div id="app" style="padding: 40px 50px">
18
- <pre v-if="loading">Checking login...</pre>
19
- <div v-else>
20
- <div v-if="user">
21
- <p>Logged in as:</p>
22
- <pre><mark>{{ user.email }}</mark></pre>
23
- <pre><mark>{{ user.token }}</mark></pre>
24
- <button class="btn btn-primary btn-lg" @click="logout">Logout</button>
25
- </div>
26
- <div v-else>
27
- <button class="btn btn-primary btn-lg" @click="login" id="loginBtn">Login</button>
28
- </div>
29
- </div>
30
- </div>
31
-
32
- <script type="module">
33
- import { createApp } from 'https://cdn.jsdelivr.net/npm/vue@3.2.20/dist/vue.esm-browser.prod.js'
34
- import { initializeApp } from 'https://www.gstatic.com/firebasejs/9.1.2/firebase-app.js'
35
- import {
36
- getAuth,
37
- GoogleAuthProvider,
38
- onAuthStateChanged,
39
- signInWithRedirect,
40
- } from 'https://www.gstatic.com/firebasejs/9.1.2/firebase-auth.js'
41
-
42
- const apiKey = '<%= firebaseApiKey %>'
43
- const authDomain = '<%= firebaseAuthDomain %>'
44
- // const authProvider = '<%= firebaseAuthProvider %>'
45
-
46
- if (!apiKey || !authDomain) {
47
- alert(`Error: 'apiKey' or 'authDomain' is missing!`)
48
- }
49
-
50
- // Initialize Firebase
51
- initializeApp({
52
- apiKey,
53
- authDomain,
54
- })
55
-
56
- const auth = getAuth()
57
- const provider = new GoogleAuthProvider()
58
-
59
- onAuthStateChanged(auth, user => {
60
- // console.log('onAuthStateChanged, user: ', JSON.stringify(user, null, 2))
61
- // console.log('onAuthStateChanged, user: ', user)
62
- onUser(user)
63
- })
64
-
65
- const qs = Object.fromEntries(new URLSearchParams(location.search))
66
- const { autoLogin, logout, returnUrl, adminTokenKey = 'admin_token' } = qs
67
- // console.log(qs)
68
-
69
- const app = createApp({
70
- data() {
71
- return {
72
- loading: 'Loading...',
73
- user: undefined,
74
- }
75
- },
76
- methods: {
77
- login: async function () {
78
- try {
79
- await signInWithRedirect(auth, provider)
80
- } catch (err) {
81
- logError(err)
82
- }
83
- },
84
-
85
- logout: async function () {
86
- try {
87
- await auth.signOut()
88
-
89
- await postToken('logout') // magic string
90
-
91
- if (logout && returnUrl) {
92
- alert('Logged out, redurecting back...')
93
- location.href = returnUrl
94
- }
95
- } catch (err) {
96
- logError(err)
97
- }
98
- },
99
- },
100
- }).mount('#app')
101
-
102
- if (logout) app.logout()
103
-
104
- ////
105
-
106
- async function onUser(user) {
107
- try {
108
- // alert('onUser')
109
- app.user = user
110
- app.loading = false
111
- if (!user) {
112
- if (autoLogin) app.login()
113
- } else {
114
- const token = await auth.currentUser.getIdToken()
115
-
116
- // alert('idToken')
117
- // console.log(idToken)
118
- app.user = Object.assign({}, app.user, {
119
- token,
120
- })
121
-
122
- await postToken(token)
123
-
124
- // Redirect if needed
125
- if (returnUrl) {
126
- // alert(`Logged in as ${app.user.email}, redirecting back...`)
127
- location.href = returnUrl
128
- }
129
- }
130
- } catch (err) {
131
- logError(err)
132
- }
133
- }
134
-
135
- function logError(err) {
136
- console.error(err)
137
- alert('Error\n ' + JSON.stringify(err, null, 2))
138
- }
139
-
140
- async function postToken(token) {
141
- await fetch(`/admin/login`, {
142
- method: 'post',
143
- headers: {
144
- Authentication: token,
145
- },
146
- })
147
- }
148
- </script>
149
- </body>
150
- </html>
@@ -1,7 +0,0 @@
1
- #
2
- # .gcloudignore
3
- #
4
- .gcloudignore
5
- .git
6
- .gitignore
7
- node_modules/