@naturalcycles/js-lib 15.34.0 → 15.36.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.
@@ -5,7 +5,7 @@ import type { AbortablePredicate, FalsyValue, Mapper, Predicate, SortDirection,
5
5
  *
6
6
  * @param array The array to process.
7
7
  * @param size The length of each chunk.
8
- * @return Returns the new array containing chunks.
8
+ * @returns Returns the new array containing chunks.
9
9
  *
10
10
  * https://lodash.com/docs#chunk
11
11
  *
@@ -6,7 +6,7 @@ import { END } from '../types.js';
6
6
  *
7
7
  * @param array The array to process.
8
8
  * @param size The length of each chunk.
9
- * @return Returns the new array containing chunks.
9
+ * @returns Returns the new array containing chunks.
10
10
  *
11
11
  * https://lodash.com/docs#chunk
12
12
  *
@@ -79,8 +79,7 @@ export function _pushUniqBy(a, mapper, ...items) {
79
79
  */
80
80
  export function _uniqBy(arr, mapper) {
81
81
  const map = new Map();
82
- for (let i = 0; i < arr.length; i++) {
83
- const item = arr[i];
82
+ for (const item of arr) {
84
83
  const key = item === undefined || item === null ? item : mapper(item);
85
84
  if (!map.has(key))
86
85
  map.set(key, item);
@@ -109,8 +108,7 @@ export function _uniqBy(arr, mapper) {
109
108
  */
110
109
  export function _by(items, mapper) {
111
110
  const map = {};
112
- for (let i = 0; i < items.length; i++) {
113
- const v = items[i];
111
+ for (const v of items) {
114
112
  const k = mapper(v);
115
113
  if (k !== undefined) {
116
114
  map[k] = v;
@@ -123,8 +121,7 @@ export function _by(items, mapper) {
123
121
  */
124
122
  export function _mapBy(items, mapper) {
125
123
  const map = new Map();
126
- for (let i = 0; i < items.length; i++) {
127
- const item = items[i];
124
+ for (const item of items) {
128
125
  const key = mapper(item);
129
126
  if (key !== undefined) {
130
127
  map.set(key, item);
@@ -145,8 +142,7 @@ export function _mapBy(items, mapper) {
145
142
  */
146
143
  export function _groupBy(items, mapper) {
147
144
  const map = {};
148
- for (let i = 0; i < items.length; i++) {
149
- const item = items[i];
145
+ for (const item of items) {
150
146
  const key = mapper(item);
151
147
  if (key !== undefined) {
152
148
  ;
@@ -444,8 +440,7 @@ export function _maxByOrUndefined(array, mapper) {
444
440
  return;
445
441
  let maxItem;
446
442
  let max;
447
- for (let i = 0; i < array.length; i++) {
448
- const item = array[i];
443
+ for (const item of array) {
449
444
  const v = mapper(item);
450
445
  if (v !== undefined && (max === undefined || v > max)) {
451
446
  maxItem = item;
@@ -459,8 +454,7 @@ export function _minByOrUndefined(array, mapper) {
459
454
  return;
460
455
  let minItem;
461
456
  let min;
462
- for (let i = 0; i < array.length; i++) {
463
- const item = array[i];
457
+ for (const item of array) {
464
458
  const v = mapper(item);
465
459
  if (v !== undefined && (min === undefined || v < min)) {
466
460
  minItem = item;
@@ -25,12 +25,12 @@ export interface AsyncMemoInstance {
25
25
  getCache: (instance: AnyAsyncFunction) => AsyncMemoCache | undefined;
26
26
  }
27
27
  /**
28
- * Like @_Memo, but allowing async MemoCache implementation.
28
+ * Like `@_Memo`, but allowing async MemoCache implementation.
29
29
  *
30
- * Implementation is more complex than @_Memo, because it needs to handle "in-flight" Promises
30
+ * Implementation is more complex than `@_Memo`, because it needs to handle "in-flight" Promises
31
31
  * while waiting for cache to resolve, to prevent "async swarm" issue.
32
32
  *
33
- * @experimental consider normal @_Memo for most of the cases, it's stable and predictable
33
+ * @experimental consider normal `@_Memo` for most of the cases, it's stable and predictable
34
34
  */
35
35
  export declare const _AsyncMemo: <FN>(opt: AsyncMemoOptions<FN>) => MethodDecorator<FN>;
36
36
  /**
@@ -3,12 +3,12 @@ import { _objectAssign, MISS } from '../types.js';
3
3
  import { _getTargetMethodSignature } from './decorator.util.js';
4
4
  import { jsonMemoSerializer } from './memo.util.js';
5
5
  /**
6
- * Like @_Memo, but allowing async MemoCache implementation.
6
+ * Like `@_Memo`, but allowing async MemoCache implementation.
7
7
  *
8
- * Implementation is more complex than @_Memo, because it needs to handle "in-flight" Promises
8
+ * Implementation is more complex than `@_Memo`, because it needs to handle "in-flight" Promises
9
9
  * while waiting for cache to resolve, to prevent "async swarm" issue.
10
10
  *
11
- * @experimental consider normal @_Memo for most of the cases, it's stable and predictable
11
+ * @experimental consider normal `@_Memo` for most of the cases, it's stable and predictable
12
12
  */
13
13
  // eslint-disable-next-line @typescript-eslint/naming-convention
14
14
  export const _AsyncMemo = (opt) => (target, key, descriptor) => {
@@ -70,7 +70,7 @@ export function _LogMethod(opt = {}) {
70
70
  return descriptor;
71
71
  };
72
72
  }
73
- // eslint-disable-next-line max-params
73
+ // oxlint-disable-next-line max-params
74
74
  function logFinished(logger, callSignature, started, sma, logResultFn, res, err) {
75
75
  const millis = Date.now() - started;
76
76
  const t = ['<<', callSignature, 'took', _ms(millis)];
@@ -83,7 +83,7 @@ export interface AppErrorOptions {
83
83
  * data - optional "any" payload.
84
84
  * data.userFriendly - if present, will be displayed to the User as is.
85
85
  *
86
- * Based on: https://medium.com/@xpl/javascript-deriving-from-error-properly-8d2f8f315801
86
+ * Based on: `https://medium.com/@xpl/javascript-deriving-from-error-properly-8d2f8f315801`
87
87
  */
88
88
  export declare class AppError<DATA_TYPE extends ErrorData = ErrorData> extends Error {
89
89
  data: DATA_TYPE;
@@ -221,7 +221,7 @@ export function _errorDataAppend(err, data) {
221
221
  * data - optional "any" payload.
222
222
  * data.userFriendly - if present, will be displayed to the User as is.
223
223
  *
224
- * Based on: https://medium.com/@xpl/javascript-deriving-from-error-properly-8d2f8f315801
224
+ * Based on: `https://medium.com/@xpl/javascript-deriving-from-error-properly-8d2f8f315801`
225
225
  */
226
226
  export class AppError extends Error {
227
227
  data;
@@ -1,5 +1,5 @@
1
1
  import type { AnyObject, StringMap } from '../types.js';
2
- export type JsonSchema<T = unknown> = JsonSchemaAny<T> | JsonSchemaOneOf<T> | JsonSchemaAllOf<T> | JsonSchemaAnyOf<T> | JsonSchemaNot<T> | JsonSchemaRef<T> | JsonSchemaConst<T> | JsonSchemaEnum<T> | JsonSchemaString | JsonSchemaNumber | JsonSchemaBoolean | JsonSchemaNull | JsonSchemaObject | JsonSchemaArray<T> | JsonSchemaTuple<T>;
2
+ export type JsonSchema<T = unknown> = JsonSchemaAny<T> | JsonSchemaOneOf<T> | JsonSchemaAllOf<T> | JsonSchemaAnyOf<T> | JsonSchemaNot<T> | JsonSchemaRef<T> | JsonSchemaConst<T> | JsonSchemaEnum<T> | JsonSchemaString | JsonSchemaNumber | JsonSchemaBoolean | JsonSchemaNull | JsonSchemaObject<T extends AnyObject ? T : AnyObject> | JsonSchemaArray<T> | JsonSchemaTuple<T>;
3
3
  export interface JsonSchemaAny<T = unknown> {
4
4
  $schema?: string;
5
5
  $id?: string;
@@ -8,7 +8,7 @@ export interface JsonSchemaAny<T = unknown> {
8
8
  deprecated?: boolean;
9
9
  readOnly?: boolean;
10
10
  writeOnly?: boolean;
11
- type?: string;
11
+ type?: string | string[];
12
12
  default?: T;
13
13
  if?: JsonSchema;
14
14
  then?: JsonSchema;
@@ -1,4 +1,4 @@
1
- import type { AnyObject, BaseDBEntity, IsoDate, UnixTimestamp } from '../types.js';
1
+ import type { AnyObject, BaseDBEntity, IsoDate, IsoDateTime, UnixTimestamp } from '../types.js';
2
2
  import type { JsonSchema, JsonSchemaAllOf, JsonSchemaAny, JsonSchemaArray, JsonSchemaBoolean, JsonSchemaConst, JsonSchemaEnum, JsonSchemaNull, JsonSchemaNumber, JsonSchemaObject, JsonSchemaOneOf, JsonSchemaRef, JsonSchemaString, JsonSchemaTuple } from './jsonSchema.model.js';
3
3
  export interface JsonSchemaBuilder<T = unknown> {
4
4
  build: () => JsonSchema<T>;
@@ -8,27 +8,36 @@ export interface JsonSchemaBuilder<T = unknown> {
8
8
  * Inspired by Joi and Zod.
9
9
  */
10
10
  export declare const j: {
11
- any<T = unknown>(): JsonSchemaAnyBuilder<T, JsonSchemaAny<T>>;
12
- const<T = unknown>(value: T): JsonSchemaAnyBuilder<T, JsonSchemaConst<T>>;
13
- null(): JsonSchemaAnyBuilder<null, JsonSchemaNull>;
14
- ref<T = unknown>($ref: string): JsonSchemaAnyBuilder<T, JsonSchemaRef<T>>;
15
- enum<T = unknown>(enumValues: T[]): JsonSchemaAnyBuilder<T, JsonSchemaEnum<T>>;
16
- boolean(): JsonSchemaAnyBuilder<boolean, JsonSchemaBoolean>;
17
- buffer(): JsonSchemaAnyBuilder<Buffer<ArrayBufferLike>, JsonSchemaAny<Buffer<ArrayBufferLike>>>;
18
- number<T extends number = number>(): JsonSchemaNumberBuilder<T>;
19
- integer<T extends number = number>(): JsonSchemaNumberBuilder<T>;
20
- unixTimestamp(): JsonSchemaNumberBuilder<UnixTimestamp>;
21
- unixTimestamp2000(): JsonSchemaNumberBuilder<UnixTimestamp>;
22
- string<T extends string = string>(): JsonSchemaStringBuilder<T>;
23
- isoDate(): JsonSchemaStringBuilder<IsoDate>;
11
+ any<T = unknown>(): JsonSchemaAnyBuilder<T, JsonSchemaAny<T>, false>;
12
+ const<T = unknown>(value: T): JsonSchemaAnyBuilder<T, JsonSchemaConst<T>, false>;
13
+ null(): JsonSchemaAnyBuilder<null, JsonSchemaNull, false>;
14
+ ref<T = unknown>($ref: string): JsonSchemaAnyBuilder<T, JsonSchemaRef<T>, false>;
15
+ enum<T = unknown>(enumValues: T[]): JsonSchemaAnyBuilder<T, JsonSchemaEnum<T>, false>;
16
+ boolean(): JsonSchemaAnyBuilder<boolean, JsonSchemaBoolean, false>;
17
+ buffer(): JsonSchemaAnyBuilder<Buffer<ArrayBufferLike>, JsonSchemaAny<Buffer<ArrayBufferLike>>, false>;
18
+ number<T extends number = number>(): JsonSchemaNumberBuilder<T, false>;
19
+ integer<T extends number = number>(): JsonSchemaNumberBuilder<T, false>;
20
+ unixTimestamp(): JsonSchemaNumberBuilder<UnixTimestamp, false>;
21
+ unixTimestamp2000(): JsonSchemaNumberBuilder<UnixTimestamp, false>;
22
+ string<T extends string = string>(): JsonSchemaStringBuilder<T, false>;
23
+ /**
24
+ * Accepts only the `YYYY-MM-DD` shape from all ISO 8601 variants.
25
+ */
26
+ isoDate(): JsonSchemaStringBuilder<IsoDate, false>;
27
+ /**
28
+ * Accepts strings that start with the `YYYY-MM-DDTHH:MM:SS` shape
29
+ * and optionally end with either a `Z` or a `+/-hh:mm` timezone part.
30
+ */
31
+ isoDateTime(): JsonSchemaStringBuilder<IsoDateTime, false>;
24
32
  object: typeof object;
25
- rootObject<T extends AnyObject>(props: { [K in keyof T]: JsonSchemaAnyBuilder<T[K]>; }): JsonSchemaObjectBuilder<T>;
26
- array<T extends JsonSchemaAnyBuilder<any>>(itemSchema: T): JsonSchemaArrayBuilder<T["infer"]>;
33
+ dbEntity<T extends AnyObject>(props: T): JsonSchemaObjectBuilder<BaseDBEntity & ({ [K in keyof T as T[K] extends JsonSchemaAnyBuilder<any, any, infer Opt extends boolean> ? Opt extends true ? never : K : never]: T[K] extends JsonSchemaAnyBuilder<infer U, any, any> ? U : never; } & { [K_1 in keyof T as T[K_1] extends JsonSchemaAnyBuilder<any, any, infer Opt extends boolean> ? Opt extends true ? K_1 : never : never]?: (T[K_1] extends JsonSchemaAnyBuilder<infer U, any, any> ? U : never) | undefined; } extends infer O ? { [K_2 in keyof O]: O[K_2]; } : never) extends infer O_1 ? { [K_3 in keyof O_1]: O_1[K_3]; } : never, false>;
34
+ rootObject<T extends AnyObject>(props: { [K in keyof T]: JsonSchemaAnyBuilder<T[K]>; }): JsonSchemaObjectBuilder<T, false>;
35
+ array<T extends JsonSchemaAnyBuilder<any>>(itemSchema: T): JsonSchemaArrayBuilder<T["infer"], false>;
27
36
  tuple<T extends any[] = unknown[]>(items: JsonSchemaAnyBuilder[]): JsonSchemaTupleBuilder<T>;
28
- oneOf<T = unknown>(items: JsonSchemaAnyBuilder[]): JsonSchemaAnyBuilder<T, JsonSchemaOneOf<T>>;
29
- allOf<T = unknown>(items: JsonSchemaAnyBuilder[]): JsonSchemaAnyBuilder<T, JsonSchemaAllOf<T>>;
37
+ oneOf<Builders extends JsonSchemaAnyBuilder<any, any, any>[]>(items: [...Builders]): JsonSchemaAnyBuilder<Builders[number] extends JsonSchemaAnyBuilder<infer U, any, any> ? U : never, JsonSchemaOneOf<Builders[number] extends JsonSchemaAnyBuilder<infer U, any, any> ? U : never>>;
38
+ allOf<T = unknown>(items: JsonSchemaAnyBuilder[]): JsonSchemaAnyBuilder<T, JsonSchemaAllOf<T>, false>;
30
39
  };
31
- export declare class JsonSchemaAnyBuilder<T = unknown, SCHEMA_TYPE extends JsonSchema<T> = JsonSchema<T>> implements JsonSchemaBuilder<T> {
40
+ export declare class JsonSchemaAnyBuilder<T = unknown, SCHEMA_TYPE extends JsonSchema<T> = JsonSchema<T>, Opt extends boolean = false> implements JsonSchemaBuilder<T> {
32
41
  protected schema: SCHEMA_TYPE;
33
42
  constructor(schema: SCHEMA_TYPE);
34
43
  /**
@@ -43,24 +52,23 @@ export declare class JsonSchemaAnyBuilder<T = unknown, SCHEMA_TYPE extends JsonS
43
52
  deprecated(deprecated?: boolean): this;
44
53
  type(type: string): this;
45
54
  default(v: any): this;
46
- oneOf(schemas: JsonSchema[]): this;
47
- allOf(schemas: JsonSchema[]): this;
48
55
  instanceof(of: string): this;
49
- optional(): JsonSchemaAnyBuilder<T | undefined, JsonSchema<T | undefined>>;
50
- optional(optional: true): JsonSchemaAnyBuilder<T | undefined, JsonSchema<T | undefined>>;
51
- optional(optional: false): JsonSchemaAnyBuilder<Exclude<T, undefined>, JsonSchema<Exclude<T, undefined>>>;
56
+ optional(): JsonSchemaAnyBuilder<T | undefined, JsonSchema<T | undefined>, true>;
57
+ optional(optional: true): JsonSchemaAnyBuilder<T | undefined, JsonSchema<T | undefined>, true>;
58
+ optional(optional: false): JsonSchemaAnyBuilder<Exclude<T, undefined>, JsonSchema<Exclude<T, undefined>>, false>;
59
+ nullable(): JsonSchemaAnyBuilder<T | null, JsonSchema<T | null>, Opt>;
52
60
  /**
53
61
  * Produces a "clean schema object" without methods.
54
62
  * Same as if it would be JSON.stringified.
55
63
  */
56
64
  build(): SCHEMA_TYPE;
57
- clone(): JsonSchemaAnyBuilder<T, SCHEMA_TYPE>;
65
+ clone(): JsonSchemaAnyBuilder<T, SCHEMA_TYPE, Opt>;
58
66
  /**
59
67
  * @experimental
60
68
  */
61
69
  infer: T;
62
70
  }
63
- export declare class JsonSchemaNumberBuilder<T extends number = number> extends JsonSchemaAnyBuilder<T, JsonSchemaNumber<T>> {
71
+ export declare class JsonSchemaNumberBuilder<T extends number = number, Opt extends boolean = false> extends JsonSchemaAnyBuilder<T, JsonSchemaNumber<T>, Opt> {
64
72
  constructor();
65
73
  integer(): this;
66
74
  multipleOf(multipleOf: number): this;
@@ -85,15 +93,15 @@ export declare class JsonSchemaNumberBuilder<T extends number = number> extends
85
93
  utcOffsetHours: () => this;
86
94
  branded<B extends number>(): JsonSchemaNumberBuilder<B>;
87
95
  }
88
- export declare class JsonSchemaStringBuilder<T extends string = string> extends JsonSchemaAnyBuilder<T, JsonSchemaString<T>> {
96
+ export declare class JsonSchemaStringBuilder<T extends string = string, Opt extends boolean = false> extends JsonSchemaAnyBuilder<T, JsonSchemaString<T>, Opt> {
89
97
  constructor();
98
+ regex(pattern: RegExp): this;
90
99
  pattern(pattern: string): this;
91
100
  min(minLength: number): this;
92
101
  max(maxLength: number): this;
93
102
  length(minLength: number, maxLength: number): this;
94
103
  format(format: string): this;
95
104
  email: () => this;
96
- isoDate: () => this;
97
105
  url: () => this;
98
106
  ipv4: () => this;
99
107
  ipv6: () => this;
@@ -108,9 +116,18 @@ export declare class JsonSchemaStringBuilder<T extends string = string> extends
108
116
  toLowerCase: (toLowerCase?: boolean) => this;
109
117
  toUpperCase: (toUpperCase?: boolean) => this;
110
118
  branded<B extends string>(): JsonSchemaStringBuilder<B>;
119
+ /**
120
+ * Accepts only the `YYYY-MM-DD` shape from all ISO 8601 variants.
121
+ */
122
+ isoDate(): JsonSchemaStringBuilder<IsoDate>;
123
+ /**
124
+ * Accepts strings that start with the `YYYY-MM-DDTHH:MM:SS` shape
125
+ * and optionally end with either a `Z` or a `+/-hh:mm` timezone part.
126
+ */
127
+ isoDateTime(): JsonSchemaStringBuilder<IsoDateTime>;
111
128
  private transformModify;
112
129
  }
113
- export declare class JsonSchemaObjectBuilder<T extends AnyObject> extends JsonSchemaAnyBuilder<T, JsonSchemaObject<T>> {
130
+ export declare class JsonSchemaObjectBuilder<T extends AnyObject, Opt extends boolean = false> extends JsonSchemaAnyBuilder<T, JsonSchemaObject<T>, Opt> {
114
131
  constructor();
115
132
  addProperties(props: {
116
133
  [k in keyof T]: JsonSchemaBuilder<T[k]>;
@@ -124,9 +141,11 @@ export declare class JsonSchemaObjectBuilder<T extends AnyObject> extends JsonSc
124
141
  maxProps(maxProperties: number): this;
125
142
  additionalProps(additionalProperties: boolean): this;
126
143
  baseDBEntity(): JsonSchemaObjectBuilder<T & BaseDBEntity>;
127
- extend<T2 extends AnyObject>(s2: JsonSchemaObjectBuilder<T2>): JsonSchemaObjectBuilder<T & T2>;
144
+ extend<T2 extends AnyObject>(s2: JsonSchemaObjectBuilder<T2>): JsonSchemaObjectBuilder<T & T2 extends infer O ? {
145
+ [K in keyof O]: O[K];
146
+ } : never>;
128
147
  }
129
- export declare class JsonSchemaArrayBuilder<ITEM> extends JsonSchemaAnyBuilder<ITEM[], JsonSchemaArray<ITEM>> {
148
+ export declare class JsonSchemaArrayBuilder<ITEM, Opt extends boolean = false> extends JsonSchemaAnyBuilder<ITEM[], JsonSchemaArray<ITEM>, Opt> {
130
149
  constructor(itemsSchema: JsonSchemaBuilder<ITEM>);
131
150
  min(minItems: number): this;
132
151
  max(maxItems: number): this;
@@ -135,9 +154,13 @@ export declare class JsonSchemaArrayBuilder<ITEM> extends JsonSchemaAnyBuilder<I
135
154
  export declare class JsonSchemaTupleBuilder<T extends any[]> extends JsonSchemaAnyBuilder<T, JsonSchemaTuple<T>> {
136
155
  constructor(items: JsonSchemaBuilder[]);
137
156
  }
138
- declare function object<P extends Record<string, JsonSchemaAnyBuilder<any, any>>>(props: P): JsonSchemaObjectBuilder<{
139
- [K in keyof P]: P[K] extends JsonSchemaAnyBuilder<infer U, any> ? U : never;
140
- }>;
157
+ declare function object<P extends Record<string, JsonSchemaAnyBuilder<any, any, any>>>(props: P): JsonSchemaObjectBuilder<{
158
+ [K in keyof P as P[K] extends JsonSchemaAnyBuilder<any, any, infer Opt> ? Opt extends true ? never : K : never]: P[K] extends JsonSchemaAnyBuilder<infer U, any, any> ? U : never;
159
+ } & {
160
+ [K in keyof P as P[K] extends JsonSchemaAnyBuilder<any, any, infer Opt> ? Opt extends true ? K : never : never]?: P[K] extends JsonSchemaAnyBuilder<infer U, any, any> ? U : never;
161
+ } extends infer O ? {
162
+ [K in keyof O]: O[K];
163
+ } : never>;
141
164
  declare function object<T extends AnyObject>(props: {
142
165
  [K in keyof T]: JsonSchemaAnyBuilder<T[K]>;
143
166
  }): JsonSchemaObjectBuilder<T>;
@@ -56,12 +56,31 @@ export const j = {
56
56
  string() {
57
57
  return new JsonSchemaStringBuilder();
58
58
  },
59
+ /**
60
+ * Accepts only the `YYYY-MM-DD` shape from all ISO 8601 variants.
61
+ */
59
62
  isoDate() {
60
63
  return new JsonSchemaStringBuilder().isoDate();
61
64
  },
65
+ /**
66
+ * Accepts strings that start with the `YYYY-MM-DDTHH:MM:SS` shape
67
+ * and optionally end with either a `Z` or a `+/-hh:mm` timezone part.
68
+ */
69
+ isoDateTime() {
70
+ return new JsonSchemaStringBuilder().isoDateTime();
71
+ },
62
72
  // email: () => new JsonSchemaStringBuilder().email(),
63
73
  // complex types
64
74
  object,
75
+ dbEntity(props) {
76
+ return j
77
+ .object({
78
+ id: j.string(),
79
+ created: j.unixTimestamp2000(),
80
+ updated: j.unixTimestamp2000(),
81
+ })
82
+ .extend(j.object(props));
83
+ },
65
84
  rootObject(props) {
66
85
  return new JsonSchemaObjectBuilder().addProperties(props).$schemaDraft7();
67
86
  },
@@ -125,14 +144,6 @@ export class JsonSchemaAnyBuilder {
125
144
  Object.assign(this.schema, { default: v });
126
145
  return this;
127
146
  }
128
- oneOf(schemas) {
129
- Object.assign(this.schema, { oneOf: schemas });
130
- return this;
131
- }
132
- allOf(schemas) {
133
- Object.assign(this.schema, { allOf: schemas });
134
- return this;
135
- }
136
147
  instanceof(of) {
137
148
  this.schema.instanceof = of;
138
149
  return this;
@@ -146,6 +157,11 @@ export class JsonSchemaAnyBuilder {
146
157
  }
147
158
  return this;
148
159
  }
160
+ nullable() {
161
+ return new JsonSchemaAnyBuilder({
162
+ anyOf: [this.build(), { type: 'null' }],
163
+ });
164
+ }
149
165
  /**
150
166
  * Produces a "clean schema object" without methods.
151
167
  * Same as if it would be JSON.stringified.
@@ -222,6 +238,9 @@ export class JsonSchemaStringBuilder extends JsonSchemaAnyBuilder {
222
238
  type: 'string',
223
239
  });
224
240
  }
241
+ regex(pattern) {
242
+ return this.pattern(pattern.source);
243
+ }
225
244
  pattern(pattern) {
226
245
  Object.assign(this.schema, { pattern });
227
246
  return this;
@@ -243,7 +262,6 @@ export class JsonSchemaStringBuilder extends JsonSchemaAnyBuilder {
243
262
  return this;
244
263
  }
245
264
  email = () => this.format('email');
246
- isoDate = () => this.format('date').description('IsoDate'); // todo: make it custom isoDate instead
247
265
  url = () => this.format('url');
248
266
  ipv4 = () => this.format('ipv4');
249
267
  ipv6 = () => this.format('ipv6');
@@ -260,6 +278,19 @@ export class JsonSchemaStringBuilder extends JsonSchemaAnyBuilder {
260
278
  branded() {
261
279
  return this;
262
280
  }
281
+ /**
282
+ * Accepts only the `YYYY-MM-DD` shape from all ISO 8601 variants.
283
+ */
284
+ isoDate() {
285
+ return this.format('IsoDate').branded().description('IsoDate');
286
+ }
287
+ /**
288
+ * Accepts strings that start with the `YYYY-MM-DDTHH:MM:SS` shape
289
+ * and optionally end with either a `Z` or a `+/-hh:mm` timezone part.
290
+ */
291
+ isoDateTime() {
292
+ return this.format('IsoDateTime').branded().description('IsoDateTime');
293
+ }
263
294
  transformModify(t, add) {
264
295
  if (add) {
265
296
  this.schema.transform = _uniq([...(this.schema.transform || []), t]);
@@ -1,2 +1,2 @@
1
1
  import type { BaseDBEntity } from '../types.js';
2
- export declare const baseDBEntityJsonSchema: import("./jsonSchemaBuilder.js").JsonSchemaObjectBuilder<BaseDBEntity>;
2
+ export declare const baseDBEntityJsonSchema: import("./jsonSchemaBuilder.js").JsonSchemaObjectBuilder<BaseDBEntity, false>;
@@ -126,7 +126,6 @@ export declare function _filterEmptyValues<T extends AnyObject>(obj: T, opt?: Mu
126
126
  *
127
127
  * **Note:** This method mutates `object`.
128
128
  *
129
- * @category Object
130
129
  * @param target The destination object.
131
130
  * @param sources The source objects.
132
131
  * @returns Returns `object`.
@@ -177,7 +176,7 @@ type PropertyPath = Many<PropertyKey>;
177
176
  * @param obj The object to modify.
178
177
  * @param path The path of the property to set.
179
178
  * @param value The value to set.
180
- * @return Returns object.
179
+ * @returns Returns object.
181
180
  *
182
181
  * Based on: https://stackoverflow.com/a/54733755/4919972
183
182
  */
@@ -185,7 +184,6 @@ export declare function _set<T extends AnyObject>(obj: T, path: PropertyPath, va
185
184
  /**
186
185
  * Checks if `path` is a direct property of `object` (not null, not undefined).
187
186
  *
188
- * @category Object
189
187
  * @param obj The object to query.
190
188
  * @param path The path to check.
191
189
  * @returns Returns `true` if `path` exists, else `false`.
@@ -236,7 +236,6 @@ export function _filterEmptyValues(obj, opt = {}) {
236
236
  *
237
237
  * **Note:** This method mutates `object`.
238
238
  *
239
- * @category Object
240
239
  * @param target The destination object.
241
240
  * @param sources The source objects.
242
241
  * @returns Returns `object`.
@@ -340,7 +339,7 @@ export function _get(obj = {}, path = '') {
340
339
  return (path
341
340
  .replaceAll(/\[([^\]]+)]/g, '.$1')
342
341
  .split('.')
343
- // eslint-disable-next-line unicorn/no-array-reduce
342
+ // oxlint-disable-next-line unicorn/no-array-reduce
344
343
  .reduce((o, p) => o?.[p], obj));
345
344
  }
346
345
  /**
@@ -350,7 +349,7 @@ export function _get(obj = {}, path = '') {
350
349
  * @param obj The object to modify.
351
350
  * @param path The path of the property to set.
352
351
  * @param value The value to set.
353
- * @return Returns object.
352
+ * @returns Returns object.
354
353
  *
355
354
  * Based on: https://stackoverflow.com/a/54733755/4919972
356
355
  */
@@ -365,7 +364,7 @@ export function _set(obj, path, value) {
365
364
  else if (!path.length) {
366
365
  return obj;
367
366
  }
368
- // eslint-disable-next-line unicorn/no-array-reduce
367
+ // oxlint-disable-next-line unicorn/no-array-reduce
369
368
  ;
370
369
  path.slice(0, -1).reduce((a, c, i) =>
371
370
  // biome-ignore lint/style/useConsistentBuiltinInstantiation: ok
@@ -384,7 +383,6 @@ export function _set(obj, path, value) {
384
383
  /**
385
384
  * Checks if `path` is a direct property of `object` (not null, not undefined).
386
385
  *
387
- * @category Object
388
386
  * @param obj The object to query.
389
387
  * @param path The path to check.
390
388
  * @returns Returns `true` if `path` exists, else `false`.
@@ -85,7 +85,7 @@ export async function pMap(iterable, mapper, opt = {}) {
85
85
  }
86
86
  ret[i] = value;
87
87
  resolvingCount--;
88
- next();
88
+ next(); // oxlint-disable-line no-callback-in-promise
89
89
  }, (err) => {
90
90
  if (errorMode === ErrorMode.THROW_IMMEDIATELY) {
91
91
  isSettled = true;
@@ -100,7 +100,7 @@ export async function pMap(iterable, mapper, opt = {}) {
100
100
  logger?.error(err);
101
101
  }
102
102
  resolvingCount--;
103
- next();
103
+ next(); // oxlint-disable-line no-callback-in-promise
104
104
  }
105
105
  });
106
106
  };
@@ -13,7 +13,8 @@ export function _safeJsonStringify(obj, replacer, spaces, cycleReplacer) {
13
13
  return JSON.stringify(obj, serializer(replacer, cycleReplacer), spaces);
14
14
  }
15
15
  }
16
- /* eslint-disable @typescript-eslint/no-unused-expressions, no-bitwise, no-implicit-coercion */
16
+ /* eslint-disable no-bitwise, no-implicit-coercion */
17
+ // oxlint-disable no-unused-expressions
17
18
  function serializer(replacer, cycleReplacer) {
18
19
  const stack = [];
19
20
  const keys = [];
@@ -16,7 +16,7 @@ export declare function _lowerFirst(s: string): Uncapitalize<string>;
16
16
  /**
17
17
  * Like String.split(), but with limit, returning the tail together with last element.
18
18
  *
19
- * @return Returns the new array of string segments.
19
+ * @returns Returns the new array of string segments.
20
20
  */
21
21
  export declare function _split(str: string, separator: string, limit: number): string[];
22
22
  export declare function _removeWhitespace(s: string): string;
@@ -26,7 +26,7 @@ export function _lowerFirst(s) {
26
26
  /**
27
27
  * Like String.split(), but with limit, returning the tail together with last element.
28
28
  *
29
- * @return Returns the new array of string segments.
29
+ * @returns Returns the new array of string segments.
30
30
  */
31
31
  export function _split(str, separator, limit) {
32
32
  const parts = str.split(separator);
@@ -37,8 +37,6 @@ type Filter<KeyType, ExcludeType> = IsEqual<KeyType, ExcludeType> extends true ?
37
37
  type FooWithoutA = Except<Foo, 'a' | 'c'>;
38
38
  //=> {b: string};
39
39
  ```
40
-
41
- @category Utilities
42
40
  */
43
41
  export type Except<ObjectType, KeysType extends keyof ObjectType> = {
44
42
  [KeyType in keyof ObjectType as Filter<KeyType, KeysType>]: ObjectType[KeyType];
@@ -82,8 +80,6 @@ export type OmitIndexSignature<ObjectType> = {
82
80
 
83
81
  const ab: Merge<Foo, Bar> = {a: 1, b: 2};
84
82
  ```
85
-
86
- @category Utilities
87
83
  */
88
84
  export type Merge<Destination, Source> = {
89
85
  [Key in keyof OmitIndexSignature<Destination & Source>]: Key extends keyof Source ? Source[Key] : Key extends keyof Destination ? Destination[Key] : never;
@@ -109,7 +105,6 @@ export type Merge<Destination, Source> = {
109
105
  type StringKeysAndUndefined = ConditionalKeys<Example, string | undefined>;
110
106
  //=> 'a' | 'c'
111
107
  ```
112
- @category Utilities
113
108
  */
114
109
  export type ConditionalKeys<Base, Condition> = NonNullable<{
115
110
  [Key in keyof Base]: Base[Key] extends Condition ? Key : never;
@@ -141,7 +136,6 @@ export type ConditionalKeys<Base, Condition> = NonNullable<{
141
136
  type NonStringKeysOnly = ConditionalExcept<Example, string>;
142
137
  //=> {b: string | number; c: () => void; d: {}}
143
138
  ```
144
- @category Utilities
145
139
  */
146
140
  export type ConditionalExcept<Base, Condition> = Except<Base, ConditionalKeys<Base, Condition>>;
147
141
  /**
@@ -171,7 +165,6 @@ export type ConditionalExcept<Base, Condition> = Except<Base, ConditionalKeys<Ba
171
165
  type StringKeysOnly = ConditionalPick<Example, string>;
172
166
  //=> {a: string}
173
167
  ```
174
- @category Utilities
175
168
  */
176
169
  export type ConditionalPick<Base, Condition> = Pick<Base, ConditionalKeys<Base, Condition>>;
177
170
  /**
package/dist/types.d.ts CHANGED
@@ -310,7 +310,7 @@ export type FalsyValue = false | '' | 0 | null | undefined;
310
310
  * err.data = {} // can be done, because it was casted
311
311
  * }
312
312
  */
313
- export declare function _typeCast<T>(v: any): asserts v is T;
313
+ export declare function _typeCast<T>(_v: any): asserts _v is T;
314
314
  /**
315
315
  * Type-safe Object.assign that checks that part is indeed a Partial<T>
316
316
  */
@@ -371,8 +371,6 @@ export type Class<T = any> = new (...args: any[]) => T;
371
371
  data.foo.push('bar');
372
372
  //=> error TS2339: Property 'push' does not exist on type 'readonly string[]'
373
373
  ```
374
-
375
- @category Utilities
376
374
  */
377
375
  export type ReadonlyDeep<T> = T extends Primitive | ((...args: any[]) => unknown) ? T : T extends ReadonlyMap<infer KeyType, infer ValueType> ? ReadonlyMapDeep<KeyType, ValueType> : T extends ReadonlySet<infer ItemType> ? ReadonlySetDeep<ItemType> : T extends object ? ReadonlyObjectDeep<T> : unknown;
378
376
  /**
package/dist/types.js CHANGED
@@ -59,7 +59,7 @@ export const _objectEntries = Object.entries;
59
59
  * err.data = {} // can be done, because it was casted
60
60
  * }
61
61
  */
62
- export function _typeCast(v) { }
62
+ export function _typeCast(_v) { }
63
63
  /**
64
64
  * Type-safe Object.assign that checks that part is indeed a Partial<T>
65
65
  */
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@naturalcycles/js-lib",
3
3
  "type": "module",
4
- "version": "15.34.0",
4
+ "version": "15.36.0",
5
5
  "dependencies": {
6
6
  "tslib": "^2",
7
7
  "undici": "^7",
@@ -15,7 +15,7 @@ import { END } from '../types.js'
15
15
  *
16
16
  * @param array The array to process.
17
17
  * @param size The length of each chunk.
18
- * @return Returns the new array containing chunks.
18
+ * @returns Returns the new array containing chunks.
19
19
  *
20
20
  * https://lodash.com/docs#chunk
21
21
  *
@@ -91,8 +91,7 @@ export function _pushUniqBy<T>(a: T[], mapper: Mapper<T, any>, ...items: T[]): T
91
91
  */
92
92
  export function _uniqBy<T>(arr: readonly T[], mapper: Mapper<T, any>): T[] {
93
93
  const map = new Map<any, T>()
94
- for (let i = 0; i < arr.length; i++) {
95
- const item = arr[i]!
94
+ for (const item of arr) {
96
95
  const key = item === undefined || item === null ? item : mapper(item)
97
96
  if (!map.has(key)) map.set(key, item)
98
97
  }
@@ -121,8 +120,7 @@ export function _uniqBy<T>(arr: readonly T[], mapper: Mapper<T, any>): T[] {
121
120
  */
122
121
  export function _by<T>(items: readonly T[], mapper: Mapper<T, any>): StringMap<T> {
123
122
  const map: StringMap<T> = {}
124
- for (let i = 0; i < items.length; i++) {
125
- const v = items[i]!
123
+ for (const v of items) {
126
124
  const k = mapper(v)
127
125
  if (k !== undefined) {
128
126
  map[k] = v
@@ -140,8 +138,7 @@ export function _mapBy<ITEM, KEY>(
140
138
  mapper: Mapper<ITEM, KEY>,
141
139
  ): Map<KEY, ITEM> {
142
140
  const map = new Map<KEY, ITEM>()
143
- for (let i = 0; i < items.length; i++) {
144
- const item = items[i]!
141
+ for (const item of items) {
145
142
  const key = mapper(item)
146
143
  if (key !== undefined) {
147
144
  map.set(key, item)
@@ -164,8 +161,7 @@ export function _mapBy<ITEM, KEY>(
164
161
  export function _groupBy<T>(items: readonly T[], mapper: Mapper<T, any>): StringMap<T[]> {
165
162
  const map: StringMap<T[]> = {}
166
163
 
167
- for (let i = 0; i < items.length; i++) {
168
- const item = items[i]!
164
+ for (const item of items) {
169
165
  const key = mapper(item)
170
166
  if (key !== undefined) {
171
167
  ;(map[key] ||= []).push(item)
@@ -531,8 +527,7 @@ export function _maxByOrUndefined<T>(
531
527
  let maxItem: T | undefined
532
528
  let max: number | string | undefined
533
529
 
534
- for (let i = 0; i < array.length; i++) {
535
- const item = array[i]!
530
+ for (const item of array) {
536
531
  const v = mapper(item)
537
532
  if (v !== undefined && (max === undefined || v > max)) {
538
533
  maxItem = item
@@ -551,8 +546,7 @@ export function _minByOrUndefined<T>(
551
546
  let minItem: T | undefined
552
547
  let min: number | string | undefined
553
548
 
554
- for (let i = 0; i < array.length; i++) {
555
- const item = array[i]!
549
+ for (const item of array) {
556
550
  const v = mapper(item)
557
551
  if (v !== undefined && (min === undefined || v < min)) {
558
552
  minItem = item
@@ -1,6 +1,8 @@
1
1
  import { isServerSide } from '../env.js'
2
2
  import { loadScript } from './script.util.js'
3
3
 
4
+ // oxlint-disable no-var
5
+
4
6
  declare global {
5
7
  var dataLayer: any[]
6
8
  var gtag: (...args: any[]) => void
@@ -36,12 +36,12 @@ export interface AsyncMemoInstance {
36
36
  }
37
37
 
38
38
  /**
39
- * Like @_Memo, but allowing async MemoCache implementation.
39
+ * Like `@_Memo`, but allowing async MemoCache implementation.
40
40
  *
41
- * Implementation is more complex than @_Memo, because it needs to handle "in-flight" Promises
41
+ * Implementation is more complex than `@_Memo`, because it needs to handle "in-flight" Promises
42
42
  * while waiting for cache to resolve, to prevent "async swarm" issue.
43
43
  *
44
- * @experimental consider normal @_Memo for most of the cases, it's stable and predictable
44
+ * @experimental consider normal `@_Memo` for most of the cases, it's stable and predictable
45
45
  */
46
46
  // eslint-disable-next-line @typescript-eslint/naming-convention
47
47
  export const _AsyncMemo =
@@ -138,7 +138,7 @@ export function _LogMethod(opt: LogMethodOptions = {}): MethodDecorator {
138
138
  }
139
139
  }
140
140
 
141
- // eslint-disable-next-line max-params
141
+ // oxlint-disable-next-line max-params
142
142
  function logFinished(
143
143
  logger: CommonLogger,
144
144
  callSignature: string,
@@ -304,7 +304,7 @@ export interface AppErrorOptions {
304
304
  * data - optional "any" payload.
305
305
  * data.userFriendly - if present, will be displayed to the User as is.
306
306
  *
307
- * Based on: https://medium.com/@xpl/javascript-deriving-from-error-properly-8d2f8f315801
307
+ * Based on: `https://medium.com/@xpl/javascript-deriving-from-error-properly-8d2f8f315801`
308
308
  */
309
309
  export class AppError<DATA_TYPE extends ErrorData = ErrorData> extends Error {
310
310
  data!: DATA_TYPE
@@ -13,7 +13,7 @@ export type JsonSchema<T = unknown> =
13
13
  | JsonSchemaNumber
14
14
  | JsonSchemaBoolean
15
15
  | JsonSchemaNull
16
- | JsonSchemaObject // cannot use <T>, because T needs to extend AnyObject
16
+ | JsonSchemaObject<T extends AnyObject ? T : AnyObject>
17
17
  | JsonSchemaArray<T>
18
18
  | JsonSchemaTuple<T>
19
19
 
@@ -28,7 +28,7 @@ export interface JsonSchemaAny<T = unknown> {
28
28
  readOnly?: boolean
29
29
  writeOnly?: boolean
30
30
 
31
- type?: string
31
+ type?: string | string[]
32
32
 
33
33
  default?: T
34
34
 
@@ -1,7 +1,7 @@
1
1
  import { _uniq } from '../array/array.util.js'
2
2
  import { _deepCopy } from '../object/object.util.js'
3
3
  import { _sortObject } from '../object/sortObject.js'
4
- import type { AnyObject, BaseDBEntity, IsoDate, UnixTimestamp } from '../types.js'
4
+ import type { AnyObject, BaseDBEntity, IsoDate, IsoDateTime, UnixTimestamp } from '../types.js'
5
5
  import { JSON_SCHEMA_ORDER } from './jsonSchema.cnst.js'
6
6
  import type {
7
7
  JsonSchema,
@@ -77,16 +77,40 @@ export const j = {
77
77
  unixTimestamp2000() {
78
78
  return new JsonSchemaNumberBuilder<UnixTimestamp>().unixTimestamp2000()
79
79
  },
80
+
80
81
  // string types
81
82
  string<T extends string = string>() {
82
83
  return new JsonSchemaStringBuilder<T>()
83
84
  },
85
+
86
+ /**
87
+ * Accepts only the `YYYY-MM-DD` shape from all ISO 8601 variants.
88
+ */
84
89
  isoDate() {
85
90
  return new JsonSchemaStringBuilder<IsoDate>().isoDate()
86
91
  },
92
+
93
+ /**
94
+ * Accepts strings that start with the `YYYY-MM-DDTHH:MM:SS` shape
95
+ * and optionally end with either a `Z` or a `+/-hh:mm` timezone part.
96
+ */
97
+ isoDateTime() {
98
+ return new JsonSchemaStringBuilder<IsoDateTime>().isoDateTime()
99
+ },
100
+
87
101
  // email: () => new JsonSchemaStringBuilder().email(),
88
102
  // complex types
89
103
  object,
104
+ dbEntity<T extends AnyObject>(props: T) {
105
+ return j
106
+ .object<BaseDBEntity>({
107
+ id: j.string(),
108
+ created: j.unixTimestamp2000(),
109
+ updated: j.unixTimestamp2000(),
110
+ })
111
+ .extend(j.object(props))
112
+ },
113
+
90
114
  rootObject<T extends AnyObject>(props: {
91
115
  [K in keyof T]: JsonSchemaAnyBuilder<T[K]>
92
116
  }) {
@@ -98,10 +122,15 @@ export const j = {
98
122
  tuple<T extends any[] = unknown[]>(items: JsonSchemaAnyBuilder[]) {
99
123
  return new JsonSchemaTupleBuilder<T>(items)
100
124
  },
101
- oneOf<T = unknown>(items: JsonSchemaAnyBuilder[]) {
102
- return new JsonSchemaAnyBuilder<T, JsonSchemaOneOf<T>>({
125
+ oneOf<Builders extends JsonSchemaAnyBuilder<any, any, any>[]>(
126
+ items: [...Builders],
127
+ ): JsonSchemaAnyBuilder<
128
+ Builders[number] extends JsonSchemaAnyBuilder<infer U, any, any> ? U : never,
129
+ JsonSchemaOneOf<Builders[number] extends JsonSchemaAnyBuilder<infer U, any, any> ? U : never>
130
+ > {
131
+ return new JsonSchemaAnyBuilder({
103
132
  oneOf: items.map(b => b.build()),
104
- })
133
+ }) as any
105
134
  },
106
135
  allOf<T = unknown>(items: JsonSchemaAnyBuilder[]) {
107
136
  return new JsonSchemaAnyBuilder<T, JsonSchemaAllOf<T>>({
@@ -110,8 +139,11 @@ export const j = {
110
139
  },
111
140
  }
112
141
 
113
- export class JsonSchemaAnyBuilder<T = unknown, SCHEMA_TYPE extends JsonSchema<T> = JsonSchema<T>>
114
- implements JsonSchemaBuilder<T>
142
+ export class JsonSchemaAnyBuilder<
143
+ T = unknown,
144
+ SCHEMA_TYPE extends JsonSchema<T> = JsonSchema<T>,
145
+ Opt extends boolean = false,
146
+ > implements JsonSchemaBuilder<T>
115
147
  {
116
148
  constructor(protected schema: SCHEMA_TYPE) {}
117
149
 
@@ -162,27 +194,17 @@ export class JsonSchemaAnyBuilder<T = unknown, SCHEMA_TYPE extends JsonSchema<T>
162
194
  return this
163
195
  }
164
196
 
165
- oneOf(schemas: JsonSchema[]): this {
166
- Object.assign(this.schema, { oneOf: schemas })
167
- return this
168
- }
169
-
170
- allOf(schemas: JsonSchema[]): this {
171
- Object.assign(this.schema, { allOf: schemas })
172
- return this
173
- }
174
-
175
197
  instanceof(of: string): this {
176
198
  this.schema.instanceof = of
177
199
  return this
178
200
  }
179
201
 
180
- optional(): JsonSchemaAnyBuilder<T | undefined, JsonSchema<T | undefined>>
181
- optional(optional: true): JsonSchemaAnyBuilder<T | undefined, JsonSchema<T | undefined>>
202
+ optional(): JsonSchemaAnyBuilder<T | undefined, JsonSchema<T | undefined>, true>
203
+ optional(optional: true): JsonSchemaAnyBuilder<T | undefined, JsonSchema<T | undefined>, true>
182
204
  optional(
183
205
  optional: false,
184
- ): JsonSchemaAnyBuilder<Exclude<T, undefined>, JsonSchema<Exclude<T, undefined>>>
185
- optional(optional = true): JsonSchemaAnyBuilder<any, JsonSchema<any>> {
206
+ ): JsonSchemaAnyBuilder<Exclude<T, undefined>, JsonSchema<Exclude<T, undefined>>, false>
207
+ optional(optional = true): JsonSchemaAnyBuilder<any, JsonSchema<any>, false> {
186
208
  if (optional) {
187
209
  this.schema.optionalField = true
188
210
  } else {
@@ -191,6 +213,12 @@ export class JsonSchemaAnyBuilder<T = unknown, SCHEMA_TYPE extends JsonSchema<T>
191
213
  return this
192
214
  }
193
215
 
216
+ nullable(): JsonSchemaAnyBuilder<T | null, JsonSchema<T | null>, Opt> {
217
+ return new JsonSchemaAnyBuilder<T | null, JsonSchema<T | null>, Opt>({
218
+ anyOf: [this.build(), { type: 'null' }],
219
+ })
220
+ }
221
+
194
222
  /**
195
223
  * Produces a "clean schema object" without methods.
196
224
  * Same as if it would be JSON.stringified.
@@ -199,8 +227,8 @@ export class JsonSchemaAnyBuilder<T = unknown, SCHEMA_TYPE extends JsonSchema<T>
199
227
  return _sortObject(JSON.parse(JSON.stringify(this.schema)), JSON_SCHEMA_ORDER)
200
228
  }
201
229
 
202
- clone(): JsonSchemaAnyBuilder<T, SCHEMA_TYPE> {
203
- return new JsonSchemaAnyBuilder<T, SCHEMA_TYPE>(_deepCopy(this.schema))
230
+ clone(): JsonSchemaAnyBuilder<T, SCHEMA_TYPE, Opt> {
231
+ return new JsonSchemaAnyBuilder<T, SCHEMA_TYPE, Opt>(_deepCopy(this.schema))
204
232
  }
205
233
 
206
234
  /**
@@ -209,10 +237,10 @@ export class JsonSchemaAnyBuilder<T = unknown, SCHEMA_TYPE extends JsonSchema<T>
209
237
  infer!: T
210
238
  }
211
239
 
212
- export class JsonSchemaNumberBuilder<T extends number = number> extends JsonSchemaAnyBuilder<
213
- T,
214
- JsonSchemaNumber<T>
215
- > {
240
+ export class JsonSchemaNumberBuilder<
241
+ T extends number = number,
242
+ Opt extends boolean = false,
243
+ > extends JsonSchemaAnyBuilder<T, JsonSchemaNumber<T>, Opt> {
216
244
  constructor() {
217
245
  super({
218
246
  type: 'number',
@@ -282,16 +310,20 @@ export class JsonSchemaNumberBuilder<T extends number = number> extends JsonSche
282
310
  }
283
311
  }
284
312
 
285
- export class JsonSchemaStringBuilder<T extends string = string> extends JsonSchemaAnyBuilder<
286
- T,
287
- JsonSchemaString<T>
288
- > {
313
+ export class JsonSchemaStringBuilder<
314
+ T extends string = string,
315
+ Opt extends boolean = false,
316
+ > extends JsonSchemaAnyBuilder<T, JsonSchemaString<T>, Opt> {
289
317
  constructor() {
290
318
  super({
291
319
  type: 'string',
292
320
  })
293
321
  }
294
322
 
323
+ regex(pattern: RegExp): this {
324
+ return this.pattern(pattern.source)
325
+ }
326
+
295
327
  pattern(pattern: string): this {
296
328
  Object.assign(this.schema, { pattern })
297
329
  return this
@@ -318,7 +350,6 @@ export class JsonSchemaStringBuilder<T extends string = string> extends JsonSche
318
350
  }
319
351
 
320
352
  email = (): this => this.format('email')
321
- isoDate = (): this => this.format('date').description('IsoDate') // todo: make it custom isoDate instead
322
353
  url = (): this => this.format('url')
323
354
  ipv4 = (): this => this.format('ipv4')
324
355
  ipv6 = (): this => this.format('ipv6')
@@ -338,6 +369,21 @@ export class JsonSchemaStringBuilder<T extends string = string> extends JsonSche
338
369
  return this as unknown as JsonSchemaStringBuilder<B>
339
370
  }
340
371
 
372
+ /**
373
+ * Accepts only the `YYYY-MM-DD` shape from all ISO 8601 variants.
374
+ */
375
+ isoDate(): JsonSchemaStringBuilder<IsoDate> {
376
+ return this.format('IsoDate').branded<IsoDate>().description('IsoDate')
377
+ }
378
+
379
+ /**
380
+ * Accepts strings that start with the `YYYY-MM-DDTHH:MM:SS` shape
381
+ * and optionally end with either a `Z` or a `+/-hh:mm` timezone part.
382
+ */
383
+ isoDateTime(): JsonSchemaStringBuilder<IsoDateTime> {
384
+ return this.format('IsoDateTime').branded<IsoDateTime>().description('IsoDateTime')
385
+ }
386
+
341
387
  private transformModify(t: 'trim' | 'toLowerCase' | 'toUpperCase', add: boolean): this {
342
388
  if (add) {
343
389
  this.schema.transform = _uniq([...(this.schema.transform || []), t])
@@ -351,10 +397,10 @@ export class JsonSchemaStringBuilder<T extends string = string> extends JsonSche
351
397
  // contentEncoding?: string
352
398
  }
353
399
 
354
- export class JsonSchemaObjectBuilder<T extends AnyObject> extends JsonSchemaAnyBuilder<
355
- T,
356
- JsonSchemaObject<T>
357
- > {
400
+ export class JsonSchemaObjectBuilder<
401
+ T extends AnyObject,
402
+ Opt extends boolean = false,
403
+ > extends JsonSchemaAnyBuilder<T, JsonSchemaObject<T>, Opt> {
358
404
  constructor() {
359
405
  super({
360
406
  type: 'object',
@@ -418,17 +464,20 @@ export class JsonSchemaObjectBuilder<T extends AnyObject> extends JsonSchemaAnyB
418
464
  return this.addRequired(['id', 'created', 'updated']) as any
419
465
  }
420
466
 
421
- extend<T2 extends AnyObject>(s2: JsonSchemaObjectBuilder<T2>): JsonSchemaObjectBuilder<T & T2> {
422
- const builder = new JsonSchemaObjectBuilder<T & T2>()
467
+ extend<T2 extends AnyObject>(
468
+ s2: JsonSchemaObjectBuilder<T2>,
469
+ ): JsonSchemaObjectBuilder<T & T2 extends infer O ? { [K in keyof O]: O[K] } : never> {
470
+ const builder = new JsonSchemaObjectBuilder<any>()
423
471
  Object.assign(builder.schema, _deepCopy(this.schema))
424
472
  mergeJsonSchemaObjects(builder.schema, s2.schema)
425
473
  return builder
426
474
  }
427
475
  }
428
476
 
429
- export class JsonSchemaArrayBuilder<ITEM> extends JsonSchemaAnyBuilder<
477
+ export class JsonSchemaArrayBuilder<ITEM, Opt extends boolean = false> extends JsonSchemaAnyBuilder<
430
478
  ITEM[],
431
- JsonSchemaArray<ITEM>
479
+ JsonSchemaArray<ITEM>,
480
+ Opt
432
481
  > {
433
482
  constructor(itemsSchema: JsonSchemaBuilder<ITEM>) {
434
483
  super({
@@ -467,16 +516,25 @@ export class JsonSchemaTupleBuilder<T extends any[]> extends JsonSchemaAnyBuilde
467
516
  }
468
517
  }
469
518
 
470
- // TODO and Notes
471
- // The issue is that in `j` we mix two approaches:
472
- // 1) the builder driven approach
473
- // 2) the type driven approach.
474
-
475
- function object<P extends Record<string, JsonSchemaAnyBuilder<any, any>>>(
519
+ function object<P extends Record<string, JsonSchemaAnyBuilder<any, any, any>>>(
476
520
  props: P,
477
- ): JsonSchemaObjectBuilder<{
478
- [K in keyof P]: P[K] extends JsonSchemaAnyBuilder<infer U, any> ? U : never
479
- }>
521
+ ): JsonSchemaObjectBuilder<
522
+ {
523
+ [K in keyof P as P[K] extends JsonSchemaAnyBuilder<any, any, infer Opt>
524
+ ? Opt extends true
525
+ ? never
526
+ : K
527
+ : never]: P[K] extends JsonSchemaAnyBuilder<infer U, any, any> ? U : never
528
+ } & {
529
+ [K in keyof P as P[K] extends JsonSchemaAnyBuilder<any, any, infer Opt>
530
+ ? Opt extends true
531
+ ? K
532
+ : never
533
+ : never]?: P[K] extends JsonSchemaAnyBuilder<infer U, any, any> ? U : never
534
+ } extends infer O
535
+ ? { [K in keyof O]: O[K] }
536
+ : never
537
+ >
480
538
  function object<T extends AnyObject>(props: {
481
539
  [K in keyof T]: JsonSchemaAnyBuilder<T[K]>
482
540
  }): JsonSchemaObjectBuilder<T>
@@ -292,7 +292,6 @@ export function _filterEmptyValues<T extends AnyObject>(obj: T, opt: MutateOptio
292
292
  *
293
293
  * **Note:** This method mutates `object`.
294
294
  *
295
- * @category Object
296
295
  * @param target The destination object.
297
296
  * @param sources The source objects.
298
297
  * @returns Returns `object`.
@@ -402,7 +401,7 @@ export function _get<T extends AnyObject>(obj = {} as T, path = ''): unknown {
402
401
  path
403
402
  .replaceAll(/\[([^\]]+)]/g, '.$1')
404
403
  .split('.')
405
- // eslint-disable-next-line unicorn/no-array-reduce
404
+ // oxlint-disable-next-line unicorn/no-array-reduce
406
405
  .reduce((o, p) => o?.[p], obj)
407
406
  )
408
407
  }
@@ -417,7 +416,7 @@ type PropertyPath = Many<PropertyKey>
417
416
  * @param obj The object to modify.
418
417
  * @param path The path of the property to set.
419
418
  * @param value The value to set.
420
- * @return Returns object.
419
+ * @returns Returns object.
421
420
  *
422
421
  * Based on: https://stackoverflow.com/a/54733755/4919972
423
422
  */
@@ -432,7 +431,7 @@ export function _set<T extends AnyObject>(obj: T, path: PropertyPath, value: any
432
431
  return obj as any
433
432
  }
434
433
 
435
- // eslint-disable-next-line unicorn/no-array-reduce
434
+ // oxlint-disable-next-line unicorn/no-array-reduce
436
435
  ;(path as any[]).slice(0, -1).reduce(
437
436
  (
438
437
  a,
@@ -458,7 +457,6 @@ export function _set<T extends AnyObject>(obj: T, path: PropertyPath, value: any
458
457
  /**
459
458
  * Checks if `path` is a direct property of `object` (not null, not undefined).
460
459
  *
461
- * @category Object
462
460
  * @param obj The object to query.
463
461
  * @param path The path to check.
464
462
  * @returns Returns `true` if `path` exists, else `false`.
@@ -126,7 +126,7 @@ export async function pMap<IN, OUT>(
126
126
 
127
127
  ret[i] = value
128
128
  resolvingCount--
129
- next()
129
+ next() // oxlint-disable-line no-callback-in-promise
130
130
  },
131
131
  (err: Error) => {
132
132
  if (errorMode === ErrorMode.THROW_IMMEDIATELY) {
@@ -140,7 +140,7 @@ export async function pMap<IN, OUT>(
140
140
  logger?.error(err)
141
141
  }
142
142
  resolvingCount--
143
- next()
143
+ next() // oxlint-disable-line no-callback-in-promise
144
144
  }
145
145
  },
146
146
  )
@@ -20,8 +20,8 @@ export function _safeJsonStringify(
20
20
  }
21
21
  }
22
22
 
23
- /* eslint-disable @typescript-eslint/no-unused-expressions, no-bitwise, no-implicit-coercion */
24
-
23
+ /* eslint-disable no-bitwise, no-implicit-coercion */
24
+ // oxlint-disable no-unused-expressions
25
25
  function serializer(replacer?: Reviver, cycleReplacer?: Reviver): Reviver {
26
26
  const stack: any[] = []
27
27
  const keys: string[] = []
@@ -31,7 +31,7 @@ export function _lowerFirst(s: string): Uncapitalize<string> {
31
31
  /**
32
32
  * Like String.split(), but with limit, returning the tail together with last element.
33
33
  *
34
- * @return Returns the new array of string segments.
34
+ * @returns Returns the new array of string segments.
35
35
  */
36
36
  export function _split(str: string, separator: string, limit: number): string[] {
37
37
  const parts = str.split(separator)
package/src/typeFest.ts CHANGED
@@ -42,8 +42,6 @@ type Filter<KeyType, ExcludeType> =
42
42
  type FooWithoutA = Except<Foo, 'a' | 'c'>;
43
43
  //=> {b: string};
44
44
  ```
45
-
46
- @category Utilities
47
45
  */
48
46
  export type Except<ObjectType, KeysType extends keyof ObjectType> = {
49
47
  [KeyType in keyof ObjectType as Filter<KeyType, KeysType>]: ObjectType[KeyType]
@@ -94,8 +92,6 @@ export type OmitIndexSignature<ObjectType> = {
94
92
 
95
93
  const ab: Merge<Foo, Bar> = {a: 1, b: 2};
96
94
  ```
97
-
98
- @category Utilities
99
95
  */
100
96
  export type Merge<Destination, Source> = {
101
97
  [Key in keyof OmitIndexSignature<Destination & Source>]: Key extends keyof Source
@@ -126,7 +122,6 @@ export type Merge<Destination, Source> = {
126
122
  type StringKeysAndUndefined = ConditionalKeys<Example, string | undefined>;
127
123
  //=> 'a' | 'c'
128
124
  ```
129
- @category Utilities
130
125
  */
131
126
  export type ConditionalKeys<Base, Condition> = NonNullable<
132
127
  // Wrap in `NonNullable` to strip away the `undefined` type from the produced union.
@@ -169,7 +164,6 @@ export type ConditionalKeys<Base, Condition> = NonNullable<
169
164
  type NonStringKeysOnly = ConditionalExcept<Example, string>;
170
165
  //=> {b: string | number; c: () => void; d: {}}
171
166
  ```
172
- @category Utilities
173
167
  */
174
168
  export type ConditionalExcept<Base, Condition> = Except<Base, ConditionalKeys<Base, Condition>>
175
169
 
@@ -200,7 +194,6 @@ export type ConditionalExcept<Base, Condition> = Except<Base, ConditionalKeys<Ba
200
194
  type StringKeysOnly = ConditionalPick<Example, string>;
201
195
  //=> {a: string}
202
196
  ```
203
- @category Utilities
204
197
  */
205
198
  export type ConditionalPick<Base, Condition> = Pick<Base, ConditionalKeys<Base, Condition>>
206
199
 
package/src/types.ts CHANGED
@@ -396,7 +396,7 @@ export type FalsyValue = false | '' | 0 | null | undefined
396
396
  * err.data = {} // can be done, because it was casted
397
397
  * }
398
398
  */
399
- export function _typeCast<T>(v: any): asserts v is T {}
399
+ export function _typeCast<T>(_v: any): asserts _v is T {}
400
400
 
401
401
  /**
402
402
  * Type-safe Object.assign that checks that part is indeed a Partial<T>
@@ -469,8 +469,6 @@ export type Class<T = any> = new (...args: any[]) => T
469
469
  data.foo.push('bar');
470
470
  //=> error TS2339: Property 'push' does not exist on type 'readonly string[]'
471
471
  ```
472
-
473
- @category Utilities
474
472
  */
475
473
  /* eslint-disable @typescript-eslint/no-restricted-types */
476
474
  export type ReadonlyDeep<T> = T extends Primitive | ((...args: any[]) => unknown)