@naturalcycles/nodejs-lib 15.135.1 → 15.137.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/dist/fs/fs2.d.ts CHANGED
@@ -45,8 +45,18 @@ declare class FS2 {
45
45
  ensureFileAsync(filePath: string): Promise<void>;
46
46
  removePath(fileOrDirPath: string, opt?: RmOptions): void;
47
47
  removePathAsync(fileOrDirPath: string, opt?: RmOptions): Promise<void>;
48
- emptyDir(dirPath: string): void;
49
- emptyDirAsync(dirPath: string): Promise<void>;
48
+ /**
49
+ * Ensures that the directory exists and is empty.
50
+ * Creates it (recursively) if it doesn't exist, otherwise deletes all its contents.
51
+ * The directory itself is not deleted.
52
+ */
53
+ ensureEmptyDir(dirPath: string): void;
54
+ /**
55
+ * Ensures that the directory exists and is empty.
56
+ * Creates it (recursively) if it doesn't exist, otherwise deletes all its contents.
57
+ * The directory itself is not deleted.
58
+ */
59
+ ensureEmptyDirAsync(dirPath: string): Promise<void>;
50
60
  /**
51
61
  * Cautious, underlying Node function is currently Experimental.
52
62
  */
package/dist/fs/fs2.js CHANGED
@@ -196,12 +196,19 @@ class FS2 {
196
196
  async removePathAsync(fileOrDirPath, opt) {
197
197
  await fsp.rm(fileOrDirPath, { recursive: true, force: true, ...opt });
198
198
  }
199
- emptyDir(dirPath) {
199
+ /**
200
+ * Ensures that the directory exists and is empty.
201
+ * Creates it (recursively) if it doesn't exist, otherwise deletes all its contents.
202
+ * The directory itself is not deleted.
203
+ */
204
+ ensureEmptyDir(dirPath) {
200
205
  let items;
201
206
  try {
202
207
  items = fs.readdirSync(dirPath);
203
208
  }
204
- catch {
209
+ catch (err) {
210
+ if (err?.code !== 'ENOENT')
211
+ throw err;
205
212
  this.ensureDir(dirPath);
206
213
  return;
207
214
  }
@@ -209,12 +216,19 @@ class FS2 {
209
216
  this.removePath(path.join(dirPath, item));
210
217
  }
211
218
  }
212
- async emptyDirAsync(dirPath) {
219
+ /**
220
+ * Ensures that the directory exists and is empty.
221
+ * Creates it (recursively) if it doesn't exist, otherwise deletes all its contents.
222
+ * The directory itself is not deleted.
223
+ */
224
+ async ensureEmptyDirAsync(dirPath) {
213
225
  let items;
214
226
  try {
215
227
  items = await fsp.readdir(dirPath);
216
228
  }
217
- catch {
229
+ catch (err) {
230
+ if (err?.code !== 'ENOENT')
231
+ throw err;
218
232
  return await this.ensureDirAsync(dirPath);
219
233
  }
220
234
  await Promise.all(items.map(item => this.removePathAsync(path.join(dirPath, item))));
@@ -372,6 +372,25 @@ export function createAjv(opt) {
372
372
  return false;
373
373
  },
374
374
  });
375
+ ajv.addKeyword({
376
+ keyword: 'UtcOffset',
377
+ type: 'string',
378
+ modifying: false,
379
+ errors: true,
380
+ schemaType: 'object',
381
+ validate: function validate(opt, data, _schema, ctx) {
382
+ const isValid = data === 'Z' ? opt.allowZ : data !== '' && isIsoTimezoneValid(data);
383
+ if (isValid)
384
+ return true;
385
+ validate.errors = [
386
+ {
387
+ instancePath: ctx?.instancePath ?? '',
388
+ message: `is an invalid UtcOffset`,
389
+ },
390
+ ];
391
+ return false;
392
+ },
393
+ });
375
394
  // Annotation only (the values are validated by `enum`), registered so strict mode doesn't reject it
376
395
  ajv.addKeyword({
377
396
  keyword: 'enumNames',
@@ -264,6 +264,7 @@ export declare class JString<OUT extends string | undefined = string, Opt extend
264
264
  isoDate(): JIsoDate;
265
265
  isoDateTime(): JString<IsoDateTime, Opt>;
266
266
  isoMonth(): JBuilder<IsoMonth, false>;
267
+ utcOffset(opt?: Partial<JsonSchemaUtcOffsetOptions>): this;
267
268
  /**
268
269
  * Validates the string format to be JWT.
269
270
  * Expects the JWT to be signed!
@@ -315,6 +316,9 @@ export interface JsonSchemaIsoDateOptions {
315
316
  }
316
317
  export interface JsonSchemaIsoMonthOptions {
317
318
  }
319
+ export interface JsonSchemaUtcOffsetOptions {
320
+ allowZ: boolean;
321
+ }
318
322
  export declare class JNumber<OUT extends number | undefined = number, Opt extends boolean = false> extends JBuilder<OUT, Opt> {
319
323
  constructor();
320
324
  integer(): this;
@@ -611,6 +615,7 @@ export interface JsonSchema<OUT = unknown> {
611
615
  IsoDate?: JsonSchemaIsoDateOptions;
612
616
  IsoDateTime?: true;
613
617
  IsoMonth?: JsonSchemaIsoMonthOptions;
618
+ UtcOffset?: JsonSchemaUtcOffsetOptions;
614
619
  instanceof?: string | string[];
615
620
  transform?: {
616
621
  trim?: true;
@@ -618,6 +618,13 @@ export class JString extends JBuilder {
618
618
  IsoMonth: {},
619
619
  });
620
620
  }
621
+ utcOffset(opt) {
622
+ const defaultOptions = { allowZ: false };
623
+ return this.cloneAndUpdateSchema({
624
+ format: 'UtcOffset',
625
+ UtcOffset: { ...defaultOptions, ...opt },
626
+ });
627
+ }
621
628
  /**
622
629
  * Validates the string format to be JWT.
623
630
  * Expects the JWT to be signed!
@@ -17,5 +17,5 @@ export declare function isEveryItemPrimitive(arr: readonly any[]): boolean;
17
17
  *
18
18
  * Annotation only: validation is done by the custom keywords (e.g `IsoDate`), not by the format.
19
19
  */
20
- export declare const jFormats: readonly ['IsoDate', 'IsoDateTime', 'IsoMonth', 'UnixTimestamp', 'UnixTimestampMillis'];
20
+ export declare const jFormats: readonly ['IsoDate', 'IsoDateTime', 'IsoMonth', 'UnixTimestamp', 'UnixTimestampMillis', 'UtcOffset'];
21
21
  export type JFormat = (typeof jFormats)[number];
@@ -100,4 +100,5 @@ export const jFormats = [
100
100
  'IsoMonth',
101
101
  'UnixTimestamp',
102
102
  'UnixTimestampMillis',
103
+ 'UtcOffset',
103
104
  ];
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@naturalcycles/nodejs-lib",
3
3
  "type": "module",
4
- "version": "15.135.1",
4
+ "version": "15.137.0",
5
5
  "dependencies": {
6
6
  "@naturalcycles/js-lib": "^15",
7
7
  "@standard-schema/spec": "^1",
package/src/fs/fs2.ts CHANGED
@@ -230,11 +230,17 @@ class FS2 {
230
230
  await fsp.rm(fileOrDirPath, { recursive: true, force: true, ...opt })
231
231
  }
232
232
 
233
- emptyDir(dirPath: string): void {
233
+ /**
234
+ * Ensures that the directory exists and is empty.
235
+ * Creates it (recursively) if it doesn't exist, otherwise deletes all its contents.
236
+ * The directory itself is not deleted.
237
+ */
238
+ ensureEmptyDir(dirPath: string): void {
234
239
  let items: string[]
235
240
  try {
236
241
  items = fs.readdirSync(dirPath)
237
- } catch {
242
+ } catch (err) {
243
+ if ((err as any)?.code !== 'ENOENT') throw err
238
244
  this.ensureDir(dirPath)
239
245
  return
240
246
  }
@@ -244,11 +250,17 @@ class FS2 {
244
250
  }
245
251
  }
246
252
 
247
- async emptyDirAsync(dirPath: string): Promise<void> {
253
+ /**
254
+ * Ensures that the directory exists and is empty.
255
+ * Creates it (recursively) if it doesn't exist, otherwise deletes all its contents.
256
+ * The directory itself is not deleted.
257
+ */
258
+ async ensureEmptyDirAsync(dirPath: string): Promise<void> {
248
259
  let items: string[]
249
260
  try {
250
261
  items = await fsp.readdir(dirPath)
251
- } catch {
262
+ } catch (err) {
263
+ if ((err as any)?.code !== 'ENOENT') throw err
252
264
  return await this.ensureDirAsync(dirPath)
253
265
  }
254
266
 
@@ -13,6 +13,7 @@ import type {
13
13
  JsonSchemaIsoDateOptions,
14
14
  JsonSchemaIsoMonthOptions,
15
15
  JsonSchemaStringEmailOptions,
16
+ JsonSchemaUtcOffsetOptions,
16
17
  } from './jSchema.js'
17
18
  import { jFormats } from './jsonSchemaBuilder.util.js'
18
19
 
@@ -427,6 +428,25 @@ export function createAjv(opt?: Options): Ajv2020 {
427
428
  },
428
429
  })
429
430
 
431
+ ajv.addKeyword({
432
+ keyword: 'UtcOffset',
433
+ type: 'string',
434
+ modifying: false,
435
+ errors: true,
436
+ schemaType: 'object',
437
+ validate: function validate(opt: JsonSchemaUtcOffsetOptions, data: string, _schema, ctx) {
438
+ const isValid = data === 'Z' ? opt.allowZ : data !== '' && isIsoTimezoneValid(data)
439
+ if (isValid) return true
440
+ ;(validate as any).errors = [
441
+ {
442
+ instancePath: ctx?.instancePath ?? '',
443
+ message: `is an invalid UtcOffset`,
444
+ },
445
+ ]
446
+ return false
447
+ },
448
+ })
449
+
430
450
  // Annotation only (the values are validated by `enum`), registered so strict mode doesn't reject it
431
451
  ajv.addKeyword({
432
452
  keyword: 'enumNames',
@@ -821,6 +821,14 @@ export class JString<
821
821
  })
822
822
  }
823
823
 
824
+ utcOffset(opt?: Partial<JsonSchemaUtcOffsetOptions>): this {
825
+ const defaultOptions: JsonSchemaUtcOffsetOptions = { allowZ: false }
826
+ return this.cloneAndUpdateSchema({
827
+ format: 'UtcOffset',
828
+ UtcOffset: { ...defaultOptions, ...opt },
829
+ })
830
+ }
831
+
824
832
  /**
825
833
  * Validates the string format to be JWT.
826
834
  * Expects the JWT to be signed!
@@ -946,6 +954,10 @@ export interface JsonSchemaIsoDateOptions {
946
954
 
947
955
  export interface JsonSchemaIsoMonthOptions {}
948
956
 
957
+ export interface JsonSchemaUtcOffsetOptions {
958
+ allowZ: boolean
959
+ }
960
+
949
961
  export class JNumber<
950
962
  OUT extends number | undefined = number,
951
963
  Opt extends boolean = false,
@@ -2128,6 +2140,7 @@ export interface JsonSchema<OUT = unknown> {
2128
2140
  IsoDate?: JsonSchemaIsoDateOptions
2129
2141
  IsoDateTime?: true
2130
2142
  IsoMonth?: JsonSchemaIsoMonthOptions
2143
+ UtcOffset?: JsonSchemaUtcOffsetOptions
2131
2144
  instanceof?: string | string[]
2132
2145
  transform?: { trim?: true; toLowerCase?: true; toUpperCase?: true; truncate?: number }
2133
2146
  errorMessages?: StringMap<string>
@@ -115,5 +115,6 @@ export const jFormats = [
115
115
  'IsoMonth',
116
116
  'UnixTimestamp',
117
117
  'UnixTimestampMillis',
118
+ 'UtcOffset',
118
119
  ] as const
119
120
  export type JFormat = (typeof jFormats)[number]