@naturalcycles/js-lib 14.128.0 → 14.129.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.
@@ -145,16 +145,22 @@ class Fetcher {
145
145
  if (mode === 'json') {
146
146
  if (res.fetchResponse.body) {
147
147
  const text = await res.fetchResponse.text();
148
- res.body = text;
149
- try {
150
- res.body = JSON.parse(text, req.jsonReviver);
148
+ if (text) {
149
+ try {
150
+ res.body = text;
151
+ res.body = JSON.parse(text, req.jsonReviver);
152
+ }
153
+ catch (err) {
154
+ res.ok = false;
155
+ res.err = (0, error_util_1._anyToError)(err, http_error_1.HttpError, (0, object_util_1._filterNullishValues)({
156
+ httpStatusCode: 0,
157
+ url: req.url,
158
+ }));
159
+ }
151
160
  }
152
- catch (err) {
153
- res.ok = false;
154
- res.err = (0, error_util_1._anyToError)(err, http_error_1.HttpError, (0, object_util_1._filterNullishValues)({
155
- httpStatusCode: 0,
156
- url: req.url,
157
- }));
161
+ else {
162
+ // Body had a '' (empty string)
163
+ res.body = {};
158
164
  }
159
165
  }
160
166
  else {
package/dist/index.d.ts CHANGED
@@ -59,7 +59,6 @@ export * from './is.util';
59
59
  export * from './typeFest';
60
60
  export * from './types';
61
61
  export * from './unit/size.util';
62
- import { is } from './vendor/is';
63
62
  export * from './log/commonLogger';
64
63
  export * from './string/safeJsonStringify';
65
64
  export * from './promise/pQueue';
@@ -77,4 +76,9 @@ export * from './datetime/timeInterval';
77
76
  export * from './http/http.model';
78
77
  export * from './http/fetcher';
79
78
  export * from './http/fetcher.model';
80
- export { is };
79
+ export * from './zod/zod.util';
80
+ export * from './zod/zod.shared.schemas';
81
+ import { z, ZodSchema, ZodError, ZodIssue } from 'zod';
82
+ import { is } from './vendor/is';
83
+ export { is, z, ZodSchema, ZodError };
84
+ export type { ZodIssue };
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.is = void 0;
3
+ exports.ZodError = exports.ZodSchema = exports.z = exports.is = void 0;
4
4
  const tslib_1 = require("tslib");
5
5
  tslib_1.__exportStar(require("./array/array.util"), exports);
6
6
  tslib_1.__exportStar(require("./lazy"), exports);
@@ -63,8 +63,6 @@ tslib_1.__exportStar(require("./is.util"), exports);
63
63
  tslib_1.__exportStar(require("./typeFest"), exports);
64
64
  tslib_1.__exportStar(require("./types"), exports);
65
65
  tslib_1.__exportStar(require("./unit/size.util"), exports);
66
- const is_1 = require("./vendor/is");
67
- Object.defineProperty(exports, "is", { enumerable: true, get: function () { return is_1.is; } });
68
66
  tslib_1.__exportStar(require("./log/commonLogger"), exports);
69
67
  tslib_1.__exportStar(require("./string/safeJsonStringify"), exports);
70
68
  tslib_1.__exportStar(require("./promise/pQueue"), exports);
@@ -82,3 +80,11 @@ tslib_1.__exportStar(require("./datetime/timeInterval"), exports);
82
80
  tslib_1.__exportStar(require("./http/http.model"), exports);
83
81
  tslib_1.__exportStar(require("./http/fetcher"), exports);
84
82
  tslib_1.__exportStar(require("./http/fetcher.model"), exports);
83
+ tslib_1.__exportStar(require("./zod/zod.util"), exports);
84
+ tslib_1.__exportStar(require("./zod/zod.shared.schemas"), exports);
85
+ const zod_1 = require("zod");
86
+ Object.defineProperty(exports, "z", { enumerable: true, get: function () { return zod_1.z; } });
87
+ Object.defineProperty(exports, "ZodSchema", { enumerable: true, get: function () { return zod_1.ZodSchema; } });
88
+ Object.defineProperty(exports, "ZodError", { enumerable: true, get: function () { return zod_1.ZodError; } });
89
+ const is_1 = require("./vendor/is");
90
+ Object.defineProperty(exports, "is", { enumerable: true, get: function () { return is_1.is; } });
@@ -0,0 +1,52 @@
1
+ import { z } from 'zod';
2
+ export declare const TS_2500 = 16725225600;
3
+ export declare const TS_2000 = 946684800;
4
+ export declare const zUnixTimestamp: z.ZodNumber;
5
+ export declare const zUnixTimestamp2000: z.ZodNumber;
6
+ export declare const zUnixTimestampMillis: z.ZodNumber;
7
+ export declare const zUnixTimestampMillis2000: z.ZodNumber;
8
+ export declare const zSemVer: z.ZodString;
9
+ export declare const zIsoDateString: z.ZodEffects<z.ZodString, string, string>;
10
+ export declare const zEmail: z.ZodEffects<z.ZodString, string, string>;
11
+ export declare const BASE62_REGEX: RegExp;
12
+ export declare const BASE64_REGEX: RegExp;
13
+ export declare const BASE64URL_REGEX: RegExp;
14
+ export declare const zBase62: z.ZodString;
15
+ export declare const zBase64: z.ZodString;
16
+ export declare const zBase64Url: z.ZodString;
17
+ export declare const JWT_REGEX: RegExp;
18
+ export declare const zJwt: z.ZodString;
19
+ export declare const zId: z.ZodString;
20
+ export declare const zIdBase62: z.ZodString;
21
+ export declare const zIdBase64: z.ZodString;
22
+ export declare const zIdBase64Url: z.ZodString;
23
+ /**
24
+ * "Slug" - a valid URL, filename, etc.
25
+ */
26
+ export declare const zSlug: z.ZodString;
27
+ export declare const zBaseDBEntity: z.ZodObject<{
28
+ id: z.ZodOptional<z.ZodString>;
29
+ created: z.ZodOptional<z.ZodNumber>;
30
+ updated: z.ZodOptional<z.ZodNumber>;
31
+ }, "strip", z.ZodTypeAny, {
32
+ id?: string | undefined;
33
+ created?: number | undefined;
34
+ updated?: number | undefined;
35
+ }, {
36
+ id?: string | undefined;
37
+ created?: number | undefined;
38
+ updated?: number | undefined;
39
+ }>;
40
+ export declare const zSavedDBEntity: z.ZodObject<{
41
+ id: z.ZodString;
42
+ created: z.ZodNumber;
43
+ updated: z.ZodNumber;
44
+ }, "strip", z.ZodTypeAny, {
45
+ id: string;
46
+ created: number;
47
+ updated: number;
48
+ }, {
49
+ id: string;
50
+ created: number;
51
+ updated: number;
52
+ }>;
@@ -0,0 +1,94 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.zSavedDBEntity = exports.zBaseDBEntity = exports.zSlug = exports.zIdBase64Url = exports.zIdBase64 = exports.zIdBase62 = exports.zId = exports.zJwt = exports.JWT_REGEX = exports.zBase64Url = exports.zBase64 = exports.zBase62 = exports.BASE64URL_REGEX = exports.BASE64_REGEX = exports.BASE62_REGEX = exports.zEmail = exports.zIsoDateString = exports.zSemVer = exports.zUnixTimestampMillis2000 = exports.zUnixTimestampMillis = exports.zUnixTimestamp2000 = exports.zUnixTimestamp = exports.TS_2000 = exports.TS_2500 = void 0;
4
+ const zod_1 = require("zod");
5
+ exports.TS_2500 = 16725225600; // 2500-01-01
6
+ exports.TS_2000 = 946684800; // 2000-01-01
7
+ exports.zUnixTimestamp = zod_1.z
8
+ .number()
9
+ .int()
10
+ .min(0)
11
+ .max(exports.TS_2500, 'Must be a UnixTimestamp number')
12
+ .describe('UnixTimestamp');
13
+ exports.zUnixTimestamp2000 = zod_1.z
14
+ .number()
15
+ .int()
16
+ .min(exports.TS_2000)
17
+ .max(exports.TS_2500, 'Must be a UnixTimestamp number after 2000-01-01')
18
+ .describe('UnixTimestamp2000');
19
+ exports.zUnixTimestampMillis = zod_1.z
20
+ .number()
21
+ .int()
22
+ .min(0)
23
+ .max(exports.TS_2500 * 1000, 'Must be a UnixTimestampMillis number')
24
+ .describe('UnixTimestampMillis');
25
+ exports.zUnixTimestampMillis2000 = zod_1.z
26
+ .number()
27
+ .int()
28
+ .min(exports.TS_2000 * 1000)
29
+ .max(exports.TS_2500 * 1000, 'Must be a UnixTimestampMillis number after 2000-01-01')
30
+ .describe('UnixTimestampMillis2000');
31
+ exports.zSemVer = zod_1.z
32
+ .string()
33
+ .regex(/^[0-9]+\.[0-9]+\.[0-9]+$/, 'Must be a SemVer string')
34
+ .describe('SemVer');
35
+ exports.zIsoDateString = zod_1.z
36
+ .string()
37
+ .refine(v => {
38
+ return /^\d{4}-\d{2}-\d{2}$/.test(v);
39
+ }, 'Must be an IsoDateString')
40
+ .describe('IsoDateString');
41
+ exports.zEmail = zod_1.z
42
+ .string()
43
+ .trim()
44
+ .email()
45
+ .transform(s => s.toLowerCase())
46
+ .describe('Email');
47
+ exports.BASE62_REGEX = /^[a-zA-Z0-9]+$/;
48
+ exports.BASE64_REGEX = /^[A-Za-z0-9+/]+={0,2}$/;
49
+ exports.BASE64URL_REGEX = /^[\w-/]+$/;
50
+ exports.zBase62 = zod_1.z
51
+ .string()
52
+ .regex(exports.BASE62_REGEX, 'Must be a base62 string')
53
+ .describe('Base62String');
54
+ exports.zBase64 = zod_1.z
55
+ .string()
56
+ .regex(exports.BASE64_REGEX, 'Must be a base64 string')
57
+ .describe('Base64String');
58
+ exports.zBase64Url = zod_1.z
59
+ .string()
60
+ .regex(exports.BASE64URL_REGEX, 'Must be a base64url string')
61
+ .describe('Base64UrlString');
62
+ exports.JWT_REGEX = /^[\w-]+\.[\w-]+\.[\w-]+$/;
63
+ exports.zJwt = zod_1.z.string().regex(exports.JWT_REGEX, 'Must be a JWT string').describe('JWTString');
64
+ exports.zId = zod_1.z
65
+ .string()
66
+ .regex(/^[a-zA-Z0-9_]{6,64}$/, 'Must be an id string')
67
+ .describe('IdString');
68
+ exports.zIdBase62 = zod_1.z
69
+ .string()
70
+ .regex(/^[a-zA-Z0-9]{8,64}$/, 'Must be a base62 id string')
71
+ .describe('Base62Id');
72
+ exports.zIdBase64 = zod_1.z
73
+ .string()
74
+ .regex(/^[A-Za-z0-9+/]{6,62}={0,2}$/, 'Must be a base64 id string')
75
+ .describe('Base64Id');
76
+ exports.zIdBase64Url = zod_1.z
77
+ .string()
78
+ .regex(/^[\w-/]{8,64}$/, 'Must be a base64url id string')
79
+ .describe('Base64UrlId');
80
+ /**
81
+ * "Slug" - a valid URL, filename, etc.
82
+ */
83
+ exports.zSlug = zod_1.z
84
+ .string()
85
+ .regex(/^[a-z0-9-]{1,255}$/, 'Must be a slug string')
86
+ .describe('Slug');
87
+ exports.zBaseDBEntity = zod_1.z
88
+ .object({
89
+ id: zod_1.z.string().optional(),
90
+ created: exports.zUnixTimestamp2000.optional(),
91
+ updated: exports.zUnixTimestamp2000.optional(),
92
+ })
93
+ .describe('BaseDBEntity');
94
+ exports.zSavedDBEntity = exports.zBaseDBEntity.required().describe('SavedDBEntity');
@@ -0,0 +1,21 @@
1
+ import { ZodError, ZodSchema, ZodIssue } from 'zod';
2
+ export interface ZodErrorResult<T> {
3
+ success: false;
4
+ data?: T;
5
+ error: ZodValidationError<T>;
6
+ }
7
+ export interface ZodSuccessResult<T> {
8
+ success: true;
9
+ data: T;
10
+ error?: ZodValidationError<T>;
11
+ }
12
+ export declare function zIsValid<T>(value: T, schema: ZodSchema<T>): boolean;
13
+ export declare function zValidate<T>(value: T, schema: ZodSchema<T>): T;
14
+ export declare function zSafeValidate<T>(value: T, schema: ZodSchema<T>): ZodSuccessResult<T> | ZodErrorResult<T>;
15
+ export declare class ZodValidationError<T> extends ZodError<T> {
16
+ value: T;
17
+ schema: ZodSchema<T>;
18
+ constructor(issues: ZodIssue[], value: T, schema: ZodSchema<T>);
19
+ get message(): string;
20
+ annotate(): string;
21
+ }
@@ -0,0 +1,59 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ZodValidationError = exports.zSafeValidate = exports.zValidate = exports.zIsValid = void 0;
4
+ const zod_1 = require("zod");
5
+ const stringifyAny_1 = require("../string/stringifyAny");
6
+ function zIsValid(value, schema) {
7
+ const { success } = schema.safeParse(value);
8
+ return success;
9
+ }
10
+ exports.zIsValid = zIsValid;
11
+ function zValidate(value, schema) {
12
+ const r = zSafeValidate(value, schema);
13
+ if (r.success) {
14
+ return r.data;
15
+ }
16
+ throw r.error;
17
+ }
18
+ exports.zValidate = zValidate;
19
+ function zSafeValidate(value, schema) {
20
+ const r = schema.safeParse(value);
21
+ if (r.success) {
22
+ return r;
23
+ }
24
+ return {
25
+ success: false,
26
+ error: new ZodValidationError(r.error.issues, value, schema),
27
+ };
28
+ }
29
+ exports.zSafeValidate = zSafeValidate;
30
+ class ZodValidationError extends zod_1.ZodError {
31
+ constructor(issues, value, schema) {
32
+ super(issues);
33
+ this.value = value;
34
+ this.schema = schema;
35
+ }
36
+ get message() {
37
+ return this.annotate();
38
+ }
39
+ annotate() {
40
+ let objectTitle = this.schema.description;
41
+ if (typeof this.value === 'object' && this.value) {
42
+ const objectName = this.schema.description || this.value.constructor?.name;
43
+ const objectId = this.value['id'];
44
+ objectTitle = [objectName, objectId].filter(Boolean).join('.');
45
+ }
46
+ objectTitle ||= 'data';
47
+ return [
48
+ `Invalid ${objectTitle}`,
49
+ '',
50
+ 'Input:',
51
+ (0, stringifyAny_1._stringifyAny)(this.value),
52
+ this.issues.length > 1 ? `\n${this.issues.length} issues:` : '',
53
+ ...this.issues.slice(0, 100).map(i => {
54
+ return [i.path.join('.'), i.message].filter(Boolean).join(': ');
55
+ }),
56
+ ].join('\n');
57
+ }
58
+ }
59
+ exports.ZodValidationError = ZodValidationError;
@@ -153,16 +153,22 @@ export class Fetcher {
153
153
  if (mode === 'json') {
154
154
  if (res.fetchResponse.body) {
155
155
  const text = await res.fetchResponse.text();
156
- res.body = text;
157
- try {
158
- res.body = JSON.parse(text, req.jsonReviver);
156
+ if (text) {
157
+ try {
158
+ res.body = text;
159
+ res.body = JSON.parse(text, req.jsonReviver);
160
+ }
161
+ catch (err) {
162
+ res.ok = false;
163
+ res.err = _anyToError(err, HttpError, _filterNullishValues({
164
+ httpStatusCode: 0,
165
+ url: req.url,
166
+ }));
167
+ }
159
168
  }
160
- catch (err) {
161
- res.ok = false;
162
- res.err = _anyToError(err, HttpError, _filterNullishValues({
163
- httpStatusCode: 0,
164
- url: req.url,
165
- }));
169
+ else {
170
+ // Body had a '' (empty string)
171
+ res.body = {};
166
172
  }
167
173
  }
168
174
  else {
package/dist-esm/index.js CHANGED
@@ -59,7 +59,6 @@ export * from './is.util';
59
59
  export * from './typeFest';
60
60
  export * from './types';
61
61
  export * from './unit/size.util';
62
- import { is } from './vendor/is';
63
62
  export * from './log/commonLogger';
64
63
  export * from './string/safeJsonStringify';
65
64
  export * from './promise/pQueue';
@@ -77,4 +76,8 @@ export * from './datetime/timeInterval';
77
76
  export * from './http/http.model';
78
77
  export * from './http/fetcher';
79
78
  export * from './http/fetcher.model';
80
- export { is };
79
+ export * from './zod/zod.util';
80
+ export * from './zod/zod.shared.schemas';
81
+ import { z, ZodSchema, ZodError } from 'zod';
82
+ import { is } from './vendor/is';
83
+ export { is, z, ZodSchema, ZodError };
@@ -0,0 +1,91 @@
1
+ import { z } from 'zod';
2
+ export const TS_2500 = 16725225600; // 2500-01-01
3
+ export const TS_2000 = 946684800; // 2000-01-01
4
+ export const zUnixTimestamp = z
5
+ .number()
6
+ .int()
7
+ .min(0)
8
+ .max(TS_2500, 'Must be a UnixTimestamp number')
9
+ .describe('UnixTimestamp');
10
+ export const zUnixTimestamp2000 = z
11
+ .number()
12
+ .int()
13
+ .min(TS_2000)
14
+ .max(TS_2500, 'Must be a UnixTimestamp number after 2000-01-01')
15
+ .describe('UnixTimestamp2000');
16
+ export const zUnixTimestampMillis = z
17
+ .number()
18
+ .int()
19
+ .min(0)
20
+ .max(TS_2500 * 1000, 'Must be a UnixTimestampMillis number')
21
+ .describe('UnixTimestampMillis');
22
+ export const zUnixTimestampMillis2000 = z
23
+ .number()
24
+ .int()
25
+ .min(TS_2000 * 1000)
26
+ .max(TS_2500 * 1000, 'Must be a UnixTimestampMillis number after 2000-01-01')
27
+ .describe('UnixTimestampMillis2000');
28
+ export const zSemVer = z
29
+ .string()
30
+ .regex(/^[0-9]+\.[0-9]+\.[0-9]+$/, 'Must be a SemVer string')
31
+ .describe('SemVer');
32
+ export const zIsoDateString = z
33
+ .string()
34
+ .refine(v => {
35
+ return /^\d{4}-\d{2}-\d{2}$/.test(v);
36
+ }, 'Must be an IsoDateString')
37
+ .describe('IsoDateString');
38
+ export const zEmail = z
39
+ .string()
40
+ .trim()
41
+ .email()
42
+ .transform(s => s.toLowerCase())
43
+ .describe('Email');
44
+ export const BASE62_REGEX = /^[a-zA-Z0-9]+$/;
45
+ export const BASE64_REGEX = /^[A-Za-z0-9+/]+={0,2}$/;
46
+ export const BASE64URL_REGEX = /^[\w-/]+$/;
47
+ export const zBase62 = z
48
+ .string()
49
+ .regex(BASE62_REGEX, 'Must be a base62 string')
50
+ .describe('Base62String');
51
+ export const zBase64 = z
52
+ .string()
53
+ .regex(BASE64_REGEX, 'Must be a base64 string')
54
+ .describe('Base64String');
55
+ export const zBase64Url = z
56
+ .string()
57
+ .regex(BASE64URL_REGEX, 'Must be a base64url string')
58
+ .describe('Base64UrlString');
59
+ export const JWT_REGEX = /^[\w-]+\.[\w-]+\.[\w-]+$/;
60
+ export const zJwt = z.string().regex(JWT_REGEX, 'Must be a JWT string').describe('JWTString');
61
+ export const zId = z
62
+ .string()
63
+ .regex(/^[a-zA-Z0-9_]{6,64}$/, 'Must be an id string')
64
+ .describe('IdString');
65
+ export const zIdBase62 = z
66
+ .string()
67
+ .regex(/^[a-zA-Z0-9]{8,64}$/, 'Must be a base62 id string')
68
+ .describe('Base62Id');
69
+ export const zIdBase64 = z
70
+ .string()
71
+ .regex(/^[A-Za-z0-9+/]{6,62}={0,2}$/, 'Must be a base64 id string')
72
+ .describe('Base64Id');
73
+ export const zIdBase64Url = z
74
+ .string()
75
+ .regex(/^[\w-/]{8,64}$/, 'Must be a base64url id string')
76
+ .describe('Base64UrlId');
77
+ /**
78
+ * "Slug" - a valid URL, filename, etc.
79
+ */
80
+ export const zSlug = z
81
+ .string()
82
+ .regex(/^[a-z0-9-]{1,255}$/, 'Must be a slug string')
83
+ .describe('Slug');
84
+ export const zBaseDBEntity = z
85
+ .object({
86
+ id: z.string().optional(),
87
+ created: zUnixTimestamp2000.optional(),
88
+ updated: zUnixTimestamp2000.optional(),
89
+ })
90
+ .describe('BaseDBEntity');
91
+ export const zSavedDBEntity = zBaseDBEntity.required().describe('SavedDBEntity');
@@ -0,0 +1,53 @@
1
+ import { ZodError } from 'zod';
2
+ import { _stringifyAny } from '../string/stringifyAny';
3
+ export function zIsValid(value, schema) {
4
+ const { success } = schema.safeParse(value);
5
+ return success;
6
+ }
7
+ export function zValidate(value, schema) {
8
+ const r = zSafeValidate(value, schema);
9
+ if (r.success) {
10
+ return r.data;
11
+ }
12
+ throw r.error;
13
+ }
14
+ export function zSafeValidate(value, schema) {
15
+ const r = schema.safeParse(value);
16
+ if (r.success) {
17
+ return r;
18
+ }
19
+ return {
20
+ success: false,
21
+ error: new ZodValidationError(r.error.issues, value, schema),
22
+ };
23
+ }
24
+ export class ZodValidationError extends ZodError {
25
+ constructor(issues, value, schema) {
26
+ super(issues);
27
+ this.value = value;
28
+ this.schema = schema;
29
+ }
30
+ get message() {
31
+ return this.annotate();
32
+ }
33
+ annotate() {
34
+ var _a;
35
+ let objectTitle = this.schema.description;
36
+ if (typeof this.value === 'object' && this.value) {
37
+ const objectName = this.schema.description || ((_a = this.value.constructor) === null || _a === void 0 ? void 0 : _a.name);
38
+ const objectId = this.value['id'];
39
+ objectTitle = [objectName, objectId].filter(Boolean).join('.');
40
+ }
41
+ objectTitle || (objectTitle = 'data');
42
+ return [
43
+ `Invalid ${objectTitle}`,
44
+ '',
45
+ 'Input:',
46
+ _stringifyAny(this.value),
47
+ this.issues.length > 1 ? `\n${this.issues.length} issues:` : '',
48
+ ...this.issues.slice(0, 100).map(i => {
49
+ return [i.path.join('.'), i.message].filter(Boolean).join(': ');
50
+ }),
51
+ ].join('\n');
52
+ }
53
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@naturalcycles/js-lib",
3
- "version": "14.128.0",
3
+ "version": "14.129.0",
4
4
  "scripts": {
5
5
  "prepare": "husky install",
6
6
  "build-prod": "build-prod-esm-cjs",
@@ -8,7 +8,8 @@
8
8
  "docs-build": "NODE_OPTIONS=--openssl-legacy-provider vuepress build docs"
9
9
  },
10
10
  "dependencies": {
11
- "tslib": "^2.0.0"
11
+ "tslib": "^2.0.0",
12
+ "zod": "^3.20.2"
12
13
  },
13
14
  "devDependencies": {
14
15
  "@naturalcycles/bench-lib": "^1.5.0",
@@ -213,20 +213,25 @@ export class Fetcher {
213
213
  if (mode === 'json') {
214
214
  if (res.fetchResponse.body) {
215
215
  const text = await res.fetchResponse.text()
216
- res.body = text
217
-
218
- try {
219
- res.body = JSON.parse(text, req.jsonReviver)
220
- } catch (err) {
221
- res.ok = false
222
- res.err = _anyToError(
223
- err,
224
- HttpError,
225
- _filterNullishValues({
226
- httpStatusCode: 0,
227
- url: req.url,
228
- }),
229
- )
216
+
217
+ if (text) {
218
+ try {
219
+ res.body = text
220
+ res.body = JSON.parse(text, req.jsonReviver)
221
+ } catch (err) {
222
+ res.ok = false
223
+ res.err = _anyToError(
224
+ err,
225
+ HttpError,
226
+ _filterNullishValues({
227
+ httpStatusCode: 0,
228
+ url: req.url,
229
+ }),
230
+ )
231
+ }
232
+ } else {
233
+ // Body had a '' (empty string)
234
+ res.body = {}
230
235
  }
231
236
  } else {
232
237
  // if no body: set responseBody as {}
package/src/index.ts CHANGED
@@ -59,7 +59,6 @@ export * from './is.util'
59
59
  export * from './typeFest'
60
60
  export * from './types'
61
61
  export * from './unit/size.util'
62
- import { is } from './vendor/is'
63
62
  export * from './log/commonLogger'
64
63
  export * from './string/safeJsonStringify'
65
64
  export * from './promise/pQueue'
@@ -77,5 +76,10 @@ export * from './datetime/timeInterval'
77
76
  export * from './http/http.model'
78
77
  export * from './http/fetcher'
79
78
  export * from './http/fetcher.model'
79
+ export * from './zod/zod.util'
80
+ export * from './zod/zod.shared.schemas'
81
+ import { z, ZodSchema, ZodError, ZodIssue } from 'zod'
82
+ import { is } from './vendor/is'
80
83
 
81
- export { is }
84
+ export { is, z, ZodSchema, ZodError }
85
+ export type { ZodIssue }
@@ -0,0 +1,102 @@
1
+ import { z } from 'zod'
2
+
3
+ export const TS_2500 = 16725225600 // 2500-01-01
4
+ export const TS_2000 = 946684800 // 2000-01-01
5
+
6
+ export const zUnixTimestamp = z
7
+ .number()
8
+ .int()
9
+ .min(0)
10
+ .max(TS_2500, 'Must be a UnixTimestamp number')
11
+ .describe('UnixTimestamp')
12
+ export const zUnixTimestamp2000 = z
13
+ .number()
14
+ .int()
15
+ .min(TS_2000)
16
+ .max(TS_2500, 'Must be a UnixTimestamp number after 2000-01-01')
17
+ .describe('UnixTimestamp2000')
18
+ export const zUnixTimestampMillis = z
19
+ .number()
20
+ .int()
21
+ .min(0)
22
+ .max(TS_2500 * 1000, 'Must be a UnixTimestampMillis number')
23
+ .describe('UnixTimestampMillis')
24
+ export const zUnixTimestampMillis2000 = z
25
+ .number()
26
+ .int()
27
+ .min(TS_2000 * 1000)
28
+ .max(TS_2500 * 1000, 'Must be a UnixTimestampMillis number after 2000-01-01')
29
+ .describe('UnixTimestampMillis2000')
30
+
31
+ export const zSemVer = z
32
+ .string()
33
+ .regex(/^[0-9]+\.[0-9]+\.[0-9]+$/, 'Must be a SemVer string')
34
+ .describe('SemVer')
35
+
36
+ export const zIsoDateString = z
37
+ .string()
38
+ .refine(v => {
39
+ return /^\d{4}-\d{2}-\d{2}$/.test(v)
40
+ }, 'Must be an IsoDateString')
41
+ .describe('IsoDateString')
42
+
43
+ export const zEmail = z
44
+ .string()
45
+ .trim()
46
+ .email()
47
+ .transform(s => s.toLowerCase())
48
+ .describe('Email')
49
+
50
+ export const BASE62_REGEX = /^[a-zA-Z0-9]+$/
51
+ export const BASE64_REGEX = /^[A-Za-z0-9+/]+={0,2}$/
52
+ export const BASE64URL_REGEX = /^[\w-/]+$/
53
+ export const zBase62 = z
54
+ .string()
55
+ .regex(BASE62_REGEX, 'Must be a base62 string')
56
+ .describe('Base62String')
57
+ export const zBase64 = z
58
+ .string()
59
+ .regex(BASE64_REGEX, 'Must be a base64 string')
60
+ .describe('Base64String')
61
+ export const zBase64Url = z
62
+ .string()
63
+ .regex(BASE64URL_REGEX, 'Must be a base64url string')
64
+ .describe('Base64UrlString')
65
+
66
+ export const JWT_REGEX = /^[\w-]+\.[\w-]+\.[\w-]+$/
67
+ export const zJwt = z.string().regex(JWT_REGEX, 'Must be a JWT string').describe('JWTString')
68
+
69
+ export const zId = z
70
+ .string()
71
+ .regex(/^[a-zA-Z0-9_]{6,64}$/, 'Must be an id string')
72
+ .describe('IdString')
73
+ export const zIdBase62 = z
74
+ .string()
75
+ .regex(/^[a-zA-Z0-9]{8,64}$/, 'Must be a base62 id string')
76
+ .describe('Base62Id')
77
+ export const zIdBase64 = z
78
+ .string()
79
+ .regex(/^[A-Za-z0-9+/]{6,62}={0,2}$/, 'Must be a base64 id string')
80
+ .describe('Base64Id')
81
+ export const zIdBase64Url = z
82
+ .string()
83
+ .regex(/^[\w-/]{8,64}$/, 'Must be a base64url id string')
84
+ .describe('Base64UrlId')
85
+
86
+ /**
87
+ * "Slug" - a valid URL, filename, etc.
88
+ */
89
+ export const zSlug = z
90
+ .string()
91
+ .regex(/^[a-z0-9-]{1,255}$/, 'Must be a slug string')
92
+ .describe('Slug')
93
+
94
+ export const zBaseDBEntity = z
95
+ .object({
96
+ id: z.string().optional(),
97
+ created: zUnixTimestamp2000.optional(),
98
+ updated: zUnixTimestamp2000.optional(),
99
+ })
100
+ .describe('BaseDBEntity')
101
+
102
+ export const zSavedDBEntity = zBaseDBEntity.required().describe('SavedDBEntity')
@@ -0,0 +1,77 @@
1
+ import { ZodError, ZodSchema, ZodIssue } from 'zod'
2
+ import { _stringifyAny } from '../string/stringifyAny'
3
+
4
+ export interface ZodErrorResult<T> {
5
+ success: false
6
+ data?: T
7
+ error: ZodValidationError<T>
8
+ }
9
+
10
+ export interface ZodSuccessResult<T> {
11
+ success: true
12
+ data: T
13
+ error?: ZodValidationError<T>
14
+ }
15
+
16
+ export function zIsValid<T>(value: T, schema: ZodSchema<T>): boolean {
17
+ const { success } = schema.safeParse(value)
18
+ return success
19
+ }
20
+
21
+ export function zValidate<T>(value: T, schema: ZodSchema<T>): T {
22
+ const r = zSafeValidate(value, schema)
23
+ if (r.success) {
24
+ return r.data
25
+ }
26
+
27
+ throw r.error
28
+ }
29
+
30
+ export function zSafeValidate<T>(
31
+ value: T,
32
+ schema: ZodSchema<T>,
33
+ // objectName?: string,
34
+ ): ZodSuccessResult<T> | ZodErrorResult<T> {
35
+ const r = schema.safeParse(value)
36
+ if (r.success) {
37
+ return r
38
+ }
39
+
40
+ return {
41
+ success: false,
42
+ error: new ZodValidationError<T>(r.error.issues, value, schema),
43
+ }
44
+ }
45
+
46
+ export class ZodValidationError<T> extends ZodError<T> {
47
+ constructor(issues: ZodIssue[], public value: T, public schema: ZodSchema<T>) {
48
+ super(issues)
49
+ }
50
+
51
+ override get message(): string {
52
+ return this.annotate()
53
+ }
54
+
55
+ annotate(): string {
56
+ let objectTitle = this.schema.description
57
+
58
+ if (typeof this.value === 'object' && this.value) {
59
+ const objectName = this.schema.description || this.value.constructor?.name
60
+ const objectId = (this.value as any)['id'] as string
61
+ objectTitle = [objectName, objectId].filter(Boolean).join('.')
62
+ }
63
+
64
+ objectTitle ||= 'data'
65
+
66
+ return [
67
+ `Invalid ${objectTitle}`,
68
+ '',
69
+ 'Input:',
70
+ _stringifyAny(this.value),
71
+ this.issues.length > 1 ? `\n${this.issues.length} issues:` : '',
72
+ ...this.issues.slice(0, 100).map(i => {
73
+ return [i.path.join('.'), i.message].filter(Boolean).join(': ')
74
+ }),
75
+ ].join('\n')
76
+ }
77
+ }