@naturalcycles/js-lib 15.44.0 → 15.46.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.
@@ -31,5 +31,9 @@ export class Map2<K = any, V = any> extends Map<K, V> {
31
31
  return Object.fromEntries(this)
32
32
  }
33
33
 
34
+ override toString(): string {
35
+ return `Map2(${this.size}) ${JSON.stringify(Object.fromEntries(this))}`
36
+ }
37
+
34
38
  // consider more helpful .toString() ?
35
39
  }
@@ -41,8 +41,8 @@ export class Set2<T = any> extends Set<T> {
41
41
  return [...this]
42
42
  }
43
43
 
44
- override get [Symbol.toStringTag](): string {
45
- return 'Set'
44
+ override toString(): string {
45
+ return `Set2(${this.size}) ${JSON.stringify([...this])}`
46
46
  }
47
47
 
48
48
  // todo: consider more helpful .toString() ?
@@ -1,4 +1,5 @@
1
1
  import { _isBackendErrorResponseObject, _isErrorLike, _isErrorObject } from '../error/error.util.js'
2
+ import type { ErrorLike } from '../error/index.js'
2
3
  import type { Reviver } from '../types.js'
3
4
  import { _jsonParseIfPossible } from './json.util.js'
4
5
  import { _safeJsonStringify } from './safeJsonStringify.js'
@@ -101,65 +102,27 @@ export function _stringify(obj: any, opt: StringifyOptions = {}): string {
101
102
  }
102
103
 
103
104
  if (obj instanceof Error || _isErrorLike(obj)) {
104
- const { includeErrorCause = true } = opt
105
- //
106
- // Error or ErrorLike
107
- //
108
-
109
- // Omit "default" error name as non-informative
110
- // UPD: no, it's still important to understand that we're dealing with Error and not just some string
111
- // if (obj?.name === 'Error') {
112
- // s = obj.message
113
- // }
114
- // if (_isErrorObject(obj) && _isHttpErrorObject(obj)) {
115
- // // Printing (0) to avoid ambiguity
116
- // s = `${obj.name}(${obj.data.backendResponseStatusCode}): ${obj.message}`
117
- // }
118
-
119
- s = [obj.name, obj.message].filter(Boolean).join(': ')
120
-
121
- if (typeof (obj as any).code === 'string') {
122
- // Error that has no `data`, but has `code` property
123
- s += `\ncode: ${(obj as any).code}`
124
- }
125
-
126
- if (opt.includeErrorData && _isErrorObject(obj) && Object.keys(obj.data).length) {
127
- s += '\n' + _stringify(obj.data, opt)
128
- }
129
-
130
- if (opt.includeErrorStack && obj.stack) {
131
- // Here we're using the previously-generated "title line" (e.g "Error: some_message"),
132
- // concatenating it with the Stack (but without the title line of the Stack)
133
- // This is to fix the rare error (happened with Got) where `err.message` was changed,
134
- // but err.stack had "old" err.message
135
- // This should "fix" that
136
- const sLines = s.split('\n').length
137
-
138
- s = [s, ...obj.stack.split('\n').slice(sLines)].join('\n')
139
- }
140
-
141
- if (supportsAggregateError && obj instanceof AggregateError && obj.errors.length) {
142
- s = [
143
- s,
144
- `${obj.errors.length} error(s):`,
145
- ...obj.errors.map((err, i) => `${i + 1}. ${_stringify(err, opt)}`),
146
- ].join('\n')
147
- }
148
-
149
- if (obj.cause && includeErrorCause) {
150
- s = s + '\nCaused by: ' + _stringify(obj.cause, opt)
151
- }
105
+ s = stringifyErrorLike(obj, opt)
152
106
  } else if (typeof obj === 'string') {
153
- //
154
- // String
155
- //
156
-
157
107
  s = obj.trim() || 'empty_string'
108
+ // todo: think about it more
109
+ // Stringifying it like a JSON would.
110
+ // To highlight that it's a String (and not a Number) - using double-quotes, JSON-like.
111
+ // s = `"${obj}"`
112
+ } else if (typeof obj === 'number') {
113
+ s = String(obj)
114
+ // todo: support RegExp and Date, when split between Browser and Node stringification is implemented
115
+ // } else if (obj instanceof RegExp) {
116
+ // s = String(obj)
117
+ // } else if (obj instanceof Date) {
118
+ // s = `Date (${obj.toISOString()})`
158
119
  } else {
159
120
  //
160
121
  // Other
161
122
  //
162
123
  if (obj instanceof Map) {
124
+ // todo: double-check it, maybe Node's inspect has good built-in stringification
125
+
163
126
  obj = Object.fromEntries(obj)
164
127
  } else if (obj instanceof Set) {
165
128
  obj = [...obj]
@@ -189,3 +152,42 @@ export function _stringify(obj: any, opt: StringifyOptions = {}): string {
189
152
 
190
153
  return s
191
154
  }
155
+
156
+ function stringifyErrorLike(obj: Error | ErrorLike, opt: StringifyOptions): string {
157
+ const { includeErrorCause = true } = opt
158
+
159
+ let s = [obj.name, obj.message].filter(Boolean).join(': ')
160
+
161
+ if (typeof (obj as any).code === 'string') {
162
+ // Error that has no `data`, but has `code` property
163
+ s += `\ncode: ${(obj as any).code}`
164
+ }
165
+
166
+ if (opt.includeErrorData && _isErrorObject(obj) && Object.keys(obj.data).length) {
167
+ s += '\n' + _stringify(obj.data, opt)
168
+ }
169
+
170
+ if (opt.includeErrorStack && obj.stack) {
171
+ // Here we're using the previously-generated "title line" (e.g "Error: some_message"),
172
+ // concatenating it with the Stack (but without the title line of the Stack)
173
+ // This is to fix the rare error (happened with Got) where `err.message` was changed,
174
+ // but err.stack had "old" err.message
175
+ // This should "fix" that
176
+ const sLines = s.split('\n').length
177
+
178
+ s = [s, ...obj.stack.split('\n').slice(sLines)].join('\n')
179
+ }
180
+
181
+ if (supportsAggregateError && obj instanceof AggregateError && obj.errors.length) {
182
+ s = [
183
+ s,
184
+ `${obj.errors.length} error(s):`,
185
+ ...obj.errors.map((err, i) => `${i + 1}. ${_stringify(err, opt)}`),
186
+ ].join('\n')
187
+ }
188
+
189
+ if (obj.cause && includeErrorCause) {
190
+ s = s + '\nCaused by: ' + _stringify(obj.cause, opt)
191
+ }
192
+ return s
193
+ }
@@ -15,12 +15,12 @@ import type { AppError } from '../error/error.util.js'
15
15
  *
16
16
  * @experimental
17
17
  */
18
- export type ValidationFunction<T, ERR extends AppError> = (
19
- input: T,
18
+ export type ValidationFunction<IN, OUT, ERR extends AppError> = (
19
+ input: IN,
20
20
  opt?: ValidationFunctionOptions,
21
- ) => ValidationFunctionResult<T, ERR>
21
+ ) => ValidationFunctionResult<OUT, ERR>
22
22
 
23
- export type ValidationFunctionResult<T, ERR extends AppError> = [err: ERR | null, output: T]
23
+ export type ValidationFunctionResult<OUT, ERR extends AppError> = [err: ERR | null, output: OUT]
24
24
 
25
25
  export interface ValidationFunctionOptions {
26
26
  /**
@@ -61,10 +61,6 @@ function isoDate(): ZodBrandedString<IsoDate> {
61
61
  .describe('IsoDate') as ZodBrandedString<IsoDate>
62
62
  }
63
63
 
64
- function email(): z.ZodEmail {
65
- return z.email().describe('Email')
66
- }
67
-
68
64
  const BASE62_REGEX = /^[a-zA-Z0-9]+$/
69
65
  const BASE64_REGEX = /^[A-Za-z0-9+/]+={0,2}$/
70
66
  const BASE64URL_REGEX = /^[\w\-/]+$/
@@ -131,7 +127,6 @@ export const customZodSchemas = {
131
127
  base64,
132
128
  base64Url,
133
129
  dbEntity,
134
- email,
135
130
  ianaTimezone,
136
131
  isoDate,
137
132
  jwt,
@@ -7,7 +7,7 @@ import type { ValidationFunction, ValidationFunctionResult } from '../validation
7
7
 
8
8
  export function getZodValidationFunction<T>(
9
9
  schema: ZodType<T>,
10
- ): ValidationFunction<T, ZodValidationError> {
10
+ ): ValidationFunction<T, T, ZodValidationError> {
11
11
  return (input, opt) => {
12
12
  _assert(!opt?.mutateInput, 'mutateInput=true is not yet supported with Zod')
13
13
  return zSafeValidate(input, schema)