@naturalcycles/nodejs-lib 13.31.1 → 13.33.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.
Files changed (44) hide show
  1. package/dist/csv/csvReader.js +2 -2
  2. package/dist/fs/fs2.js +6 -3
  3. package/dist/index.d.ts +2 -2
  4. package/dist/index.js +2 -2
  5. package/dist/slack/slack.service.js +2 -4
  6. package/dist/stream/ndjson/transformJsonParse.js +1 -1
  7. package/dist/stream/transform/transformMap.d.ts +4 -2
  8. package/dist/stream/transform/transformMap.js +2 -2
  9. package/dist/stream/transform/transformSplit.js +2 -2
  10. package/dist/stream/transform/worker/workerClassProxy.js +2 -2
  11. package/dist/stream/writable/writableLimit.js +1 -1
  12. package/dist/stream/writable/writableVoid.js +1 -1
  13. package/dist/util/buildInfo.util.js +5 -5
  14. package/dist/util/exec2.d.ts +167 -0
  15. package/dist/util/exec2.js +204 -0
  16. package/dist/util/git2.d.ts +25 -0
  17. package/dist/util/git2.js +95 -0
  18. package/dist/validation/joi/number.extensions.d.ts +2 -1
  19. package/dist/validation/joi/string.extensions.d.ts +2 -1
  20. package/package.json +3 -2
  21. package/src/csv/csvReader.ts +2 -7
  22. package/src/fs/fs2.ts +11 -7
  23. package/src/index.ts +2 -2
  24. package/src/secret/secrets-decrypt.util.ts +1 -1
  25. package/src/secret/secrets-encrypt.util.ts +1 -1
  26. package/src/slack/slack.service.ts +2 -3
  27. package/src/stream/ndjson/transformJsonParse.ts +1 -1
  28. package/src/stream/stream.model.ts +2 -2
  29. package/src/stream/transform/transformMap.ts +5 -3
  30. package/src/stream/transform/transformSplit.ts +3 -3
  31. package/src/stream/transform/worker/workerClassProxy.js +2 -2
  32. package/src/stream/writable/writableLimit.ts +1 -1
  33. package/src/stream/writable/writableVoid.ts +1 -1
  34. package/src/util/buildInfo.util.ts +5 -10
  35. package/src/util/exec2.ts +326 -0
  36. package/src/util/git2.ts +105 -0
  37. package/src/validation/joi/number.extensions.ts +2 -1
  38. package/src/validation/joi/string.extensions.ts +2 -1
  39. package/dist/util/exec.util.d.ts +0 -10
  40. package/dist/util/exec.util.js +0 -58
  41. package/dist/util/git.util.d.ts +0 -18
  42. package/dist/util/git.util.js +0 -109
  43. package/src/util/exec.util.ts +0 -79
  44. package/src/util/git.util.ts +0 -113
@@ -0,0 +1,95 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.git2 = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const node_child_process_1 = tslib_1.__importDefault(require("node:child_process"));
6
+ const node_path_1 = tslib_1.__importDefault(require("node:path"));
7
+ const colors_1 = require("../colors/colors");
8
+ const exec2_1 = require("./exec2");
9
+ /**
10
+ * Set of utility functions to work with git.
11
+ */
12
+ class Git2 {
13
+ getLastGitCommitMsg() {
14
+ return exec2_1.exec2.exec('git log -1 --pretty=%B');
15
+ }
16
+ commitMessageToTitleMessage(msg) {
17
+ const firstLine = msg.split('\n')[0];
18
+ const [preTitle, title] = firstLine.split(': ');
19
+ return title || preTitle;
20
+ }
21
+ gitHasUncommittedChanges() {
22
+ // git diff-index --quiet HEAD -- || echo "untracked"
23
+ try {
24
+ node_child_process_1.default.execSync('git diff-index --quiet HEAD --', {
25
+ encoding: 'utf8',
26
+ });
27
+ return false;
28
+ }
29
+ catch {
30
+ return true;
31
+ }
32
+ }
33
+ /**
34
+ * Returns true if there were changes
35
+ */
36
+ gitCommitAll(msg) {
37
+ // git commit -a -m "style(lint-all): $GIT_MSG" || true
38
+ const cmd = `git commit -a --no-verify -m "${msg}"`;
39
+ // const cmd = `git`
40
+ // const args = ['commit', '-a', '--no-verify', '-m', msg]
41
+ console.log((0, colors_1.grey)(cmd));
42
+ try {
43
+ node_child_process_1.default.execSync(cmd, {
44
+ stdio: 'inherit',
45
+ });
46
+ return true;
47
+ }
48
+ catch {
49
+ return false;
50
+ }
51
+ }
52
+ /**
53
+ * @returns true if there are not pushed commits.
54
+ */
55
+ gitIsAhead() {
56
+ // ahead=`git rev-list HEAD --not --remotes | wc -l | awk '{print $1}'`
57
+ const cmd = `git rev-list HEAD --not --remotes | wc -l | awk '{print $1}'`;
58
+ const stdout = exec2_1.exec2.exec(cmd);
59
+ // console.log(`gitIsAhead: ${stdout}`)
60
+ return Number(stdout) > 0;
61
+ }
62
+ gitPull() {
63
+ const cmd = 'git pull';
64
+ try {
65
+ node_child_process_1.default.execSync(cmd, {
66
+ stdio: 'inherit',
67
+ });
68
+ }
69
+ catch { }
70
+ }
71
+ gitPush() {
72
+ // git push --set-upstream origin $CIRCLE_BRANCH && echo "pushed, exiting" && exit 0
73
+ let cmd = 'git push';
74
+ const branchName = this.gitCurrentBranchName();
75
+ if (branchName) {
76
+ cmd += ` --set-upstream origin ${branchName}`;
77
+ }
78
+ exec2_1.exec2.spawn(cmd, { logStart: true });
79
+ }
80
+ gitCurrentCommitSha(full = false) {
81
+ const sha = exec2_1.exec2.exec('git rev-parse HEAD');
82
+ return full ? sha : sha.slice(0, 7);
83
+ }
84
+ gitCurrentCommitTimestamp() {
85
+ return Number(exec2_1.exec2.exec('git log -1 --format=%ct'));
86
+ }
87
+ gitCurrentBranchName() {
88
+ return exec2_1.exec2.exec('git rev-parse --abbrev-ref HEAD');
89
+ }
90
+ gitCurrentRepoName() {
91
+ const originUrl = exec2_1.exec2.exec('git config --get remote.origin.url');
92
+ return node_path_1.default.basename(originUrl, '.git');
93
+ }
94
+ }
95
+ exports.git2 = new Git2();
@@ -1,4 +1,5 @@
1
- import Joi, { Extension, NumberSchema as JoiNumberSchema } from 'joi';
1
+ import type Joi from 'joi';
2
+ import { Extension, NumberSchema as JoiNumberSchema } from 'joi';
2
3
  export interface NumberSchema<TSchema = number> extends JoiNumberSchema<TSchema> {
3
4
  dividable: (q: number) => this;
4
5
  }
@@ -1,4 +1,5 @@
1
- import Joi, { Extension, StringSchema as JoiStringSchema } from 'joi';
1
+ import type Joi from 'joi';
2
+ import { Extension, StringSchema as JoiStringSchema } from 'joi';
2
3
  export interface StringSchema<TSchema = string> extends JoiStringSchema<TSchema> {
3
4
  dateString: (min?: string, max?: string) => this;
4
5
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@naturalcycles/nodejs-lib",
3
- "version": "13.31.1",
3
+ "version": "13.33.0",
4
4
  "scripts": {
5
5
  "prepare": "husky",
6
6
  "build": "dev-lib build",
@@ -39,9 +39,10 @@
39
39
  "yargs": "^17.0.0"
40
40
  },
41
41
  "devDependencies": {
42
+ "@biomejs/biome": "^1.8.3",
42
43
  "@naturalcycles/bench-lib": "^3.0.0",
43
44
  "@naturalcycles/dev-lib": "^15.0.3",
44
- "@types/node": "^20.1.0",
45
+ "@types/node": "^22.1.0",
45
46
  "@types/yargs": "^16.0.0",
46
47
  "jest": "^29.0.0"
47
48
  },
@@ -56,10 +56,7 @@ export function csvStringParse<T extends AnyObject = any>(
56
56
  }
57
57
 
58
58
  export function csvStringToArray(str: string): string[][] {
59
- const objPattern = new RegExp(
60
- String.raw`(,|\r?\n|\r|^)(?:"([^"]*(?:""[^"]*)*)"|([^,\r\n]*))`,
61
- 'gi',
62
- )
59
+ const objPattern = /(,|\r?\n|\r|^)(?:"([^"]*(?:""[^"]*)*)"|([^,\r\n]*))/gi
63
60
  let matches: RegExpExecArray | null
64
61
  const arr: any[][] = [[]]
65
62
 
@@ -67,9 +64,7 @@ export function csvStringToArray(str: string): string[][] {
67
64
  if (matches[1]!.length && matches[1] !== ',') {
68
65
  arr.push([])
69
66
  }
70
- arr[arr.length - 1]!.push(
71
- matches[2] ? matches[2].replaceAll(new RegExp('""', 'g'), '"') : matches[3],
72
- )
67
+ arr[arr.length - 1]!.push(matches[2] ? matches[2].replaceAll('""', '"') : matches[3])
73
68
  }
74
69
  return arr
75
70
  }
package/src/fs/fs2.ts CHANGED
@@ -14,7 +14,7 @@ Credit to: fs-extra (https://github.com/jprichardson/node-fs-extra)
14
14
 
15
15
  */
16
16
 
17
- import type { RmOptions } from 'node:fs'
17
+ import type { RmOptions, Stats } from 'node:fs'
18
18
  import fs from 'node:fs'
19
19
  import fsp from 'node:fs/promises'
20
20
  import path from 'node:path'
@@ -182,7 +182,7 @@ class FS2 {
182
182
  }
183
183
 
184
184
  ensureFile(filePath: string): void {
185
- let stats
185
+ let stats: Stats | undefined
186
186
  try {
187
187
  stats = fs.statSync(filePath)
188
188
  } catch {}
@@ -197,7 +197,10 @@ class FS2 {
197
197
  }
198
198
  } catch (err) {
199
199
  // If the stat call above failed because the directory doesn't exist, create it
200
- if ((err as any)?.code === 'ENOENT') return this.ensureDir(dir)
200
+ if ((err as any)?.code === 'ENOENT') {
201
+ this.ensureDir(dir)
202
+ return
203
+ }
201
204
  throw err
202
205
  }
203
206
 
@@ -205,7 +208,7 @@ class FS2 {
205
208
  }
206
209
 
207
210
  async ensureFileAsync(filePath: string): Promise<void> {
208
- let stats
211
+ let stats: Stats | undefined
209
212
  try {
210
213
  stats = await fsp.stat(filePath)
211
214
  } catch {}
@@ -236,18 +239,19 @@ class FS2 {
236
239
  }
237
240
 
238
241
  emptyDir(dirPath: string): void {
239
- let items
242
+ let items: string[]
240
243
  try {
241
244
  items = fs.readdirSync(dirPath)
242
245
  } catch {
243
- return this.ensureDir(dirPath)
246
+ this.ensureDir(dirPath)
247
+ return
244
248
  }
245
249
 
246
250
  items.forEach(item => this.removePath(path.join(dirPath, item)))
247
251
  }
248
252
 
249
253
  async emptyDirAsync(dirPath: string): Promise<void> {
250
- let items
254
+ let items: string[]
251
255
  try {
252
256
  items = await fsp.readdir(dirPath)
253
257
  } catch {
package/src/index.ts CHANGED
@@ -72,8 +72,8 @@ export * from './stream/writable/writableVoid'
72
72
  export * from './string/inspect'
73
73
  export * from './util/buildInfo.util'
74
74
  export * from './util/env.util'
75
- export * from './util/exec.util'
76
- export * from './util/git.util'
75
+ export * from './util/exec2'
76
+ export * from './util/git2'
77
77
  export * from './util/lruMemoCache'
78
78
  export * from './util/zip.util'
79
79
  export * from './validation/ajv/ajv.util'
@@ -37,7 +37,7 @@ export function secretsDecrypt(
37
37
  const filenames = fastGlob.sync(patterns)
38
38
 
39
39
  filenames.forEach(filename => {
40
- let plainFilename
40
+ let plainFilename: string
41
41
 
42
42
  if (jsonMode) {
43
43
  _assert(filename.endsWith('.json'), `${path.basename(filename)} MUST end with '.json'`)
@@ -31,7 +31,7 @@ export function secretsEncrypt(
31
31
  `!**/*.enc`, // excluding already encoded
32
32
  ]
33
33
  const filenames = fastGlob.sync(patterns)
34
- let encFilename
34
+ let encFilename: string
35
35
 
36
36
  filenames.forEach(filename => {
37
37
  if (jsonMode) {
@@ -92,7 +92,7 @@ export class SlackService<CTX = any> {
92
92
  if (msg.kv) {
93
93
  ;(msg.attachments ||= []).push({ fields: this.kvToFields(msg.kv) })
94
94
 
95
- delete msg.kv // to not pass it all the way to Slack Api
95
+ msg.kv = undefined // to not pass it all the way to Slack Api
96
96
  }
97
97
 
98
98
  let text: string
@@ -135,9 +135,8 @@ export class SlackService<CTX = any> {
135
135
  // ignore (unless throwOnError is set)
136
136
  if (msg.throwOnError) {
137
137
  throw err
138
- } else {
139
- console.log(err)
140
138
  }
139
+ console.log(err)
141
140
  })
142
141
  }
143
142
 
@@ -55,7 +55,7 @@ export function transformJsonParse<ROW = any>(
55
55
  }
56
56
 
57
57
  // Based on: https://stackoverflow.com/a/34557997/4919972
58
- export const bufferReviver: Reviver = (k, v) => {
58
+ export const bufferReviver: Reviver = (_k, v) => {
59
59
  if (v !== null && typeof v === 'object' && v.type === 'Buffer' && Array.isArray(v.data)) {
60
60
  return Buffer.from(v.data)
61
61
  }
@@ -45,7 +45,7 @@ export interface ReadableTyped<T> extends Readable {
45
45
  drop: (limit: number, opt?: ReadableSignalOptions) => ReadableTyped<T>
46
46
  }
47
47
 
48
- // eslint-disable-next-line unused-imports/no-unused-vars
48
+ // biome-ignore lint/correctness/noUnusedVariables: ok
49
49
  export interface WritableTyped<T> extends Writable {}
50
50
 
51
51
  /**
@@ -59,7 +59,7 @@ export type ReadableBinary = Readable
59
59
  */
60
60
  export type WritableBinary = Writable
61
61
 
62
- // eslint-disable-next-line unused-imports/no-unused-vars
62
+ // biome-ignore lint/correctness/noUnusedVariables: ok
63
63
  export interface TransformTyped<IN, OUT> extends Transform {}
64
64
 
65
65
  export interface TransformOptions {
@@ -40,10 +40,12 @@ export interface TransformMapOptions<IN = any, OUT = IN> {
40
40
  /**
41
41
  * Number of concurrently pending promises returned by `mapper`.
42
42
  *
43
- * Default is 32.
43
+ * Default is 16.
44
44
  * It was recently changed up from 16, after some testing that shown that
45
45
  * for simple low-cpu mapper functions 32 produces almost 2x throughput.
46
46
  * For example, in scenarios like streaming a query from Datastore.
47
+ * UPD: changed back from 32 to 16, "to be on a safe side", as 32 sometimes
48
+ * causes "Datastore timeout errors".
47
49
  */
48
50
  concurrency?: number
49
51
 
@@ -120,7 +122,7 @@ export interface TransformMapStatsSummary extends TransformMapStats {
120
122
  *
121
123
  * Only works in objectMode (due to through2Concurrent).
122
124
  *
123
- * Concurrency defaults to 32.
125
+ * Concurrency defaults to 16.
124
126
  *
125
127
  * If an Array is returned by `mapper` - it will be flattened and multiple results will be emitted from it. Tested by Array.isArray().
126
128
  */
@@ -129,7 +131,7 @@ export function transformMap<IN = any, OUT = IN>(
129
131
  opt: TransformMapOptions<IN, OUT> = {},
130
132
  ): TransformTyped<IN, OUT> {
131
133
  const {
132
- concurrency = 32,
134
+ concurrency = 16,
133
135
  predicate, // we now default to "no predicate" (meaning pass-everything)
134
136
  errorMode = ErrorMode.THROW_IMMEDIATELY,
135
137
  flattenArrayOutput,
@@ -18,7 +18,7 @@ export function transformSplitOnNewline(): TransformTyped<Buffer, Buffer> {
18
18
  readableObjectMode: true,
19
19
  writableHighWaterMark: 64 * 1024,
20
20
 
21
- transform(buf: Buffer, enc, done) {
21
+ transform(buf: Buffer, _enc, done) {
22
22
  let offset = 0
23
23
  let lastMatch = 0
24
24
  if (buffered) {
@@ -66,7 +66,7 @@ export function transformSplit(separator = '\n'): TransformTyped<Buffer, Buffer>
66
66
  readableObjectMode: true,
67
67
  writableHighWaterMark: 64 * 1024,
68
68
 
69
- transform(buf: Buffer, enc, done) {
69
+ transform(buf: Buffer, _enc, done) {
70
70
  let offset = 0
71
71
  let lastMatch = 0
72
72
  if (buffered) {
@@ -119,7 +119,7 @@ function firstNewlineMatch(buf: Buffer, offset: number): number {
119
119
 
120
120
  function firstMatch(buf: Buffer, offset: number, matcher: Buffer): number {
121
121
  if (offset >= buf.length) return -1
122
- let i
122
+ let i: number
123
123
  for (i = offset; i < buf.length; i++) {
124
124
  if (buf[i] === matcher[0]) {
125
125
  if (matcher.length > 1) {
@@ -1,6 +1,6 @@
1
1
  const started = Date.now()
2
- const { workerData, parentPort } = require('worker_threads')
3
- const { inspect } = require('util')
2
+ const { workerData, parentPort } = require('node:worker_threads')
3
+ const { inspect } = require('node:util')
4
4
  const { workerFile, workerIndex, logEvery = 1000, metric = 'worker' } = workerData || {}
5
5
 
6
6
  if (!workerFile) {
@@ -11,7 +11,7 @@ export function writableLimit<T>(readable: Readable, limit: number): WritableTyp
11
11
 
12
12
  return new Writable({
13
13
  objectMode: true,
14
- write(chunk, _, cb) {
14
+ write(_chunk, _, cb) {
15
15
  if (limit === 0) return cb() // no limit, just passthrough
16
16
 
17
17
  i++
@@ -18,7 +18,7 @@ export function writableVoid(opt: WritableVoidOptions = {}): Writable {
18
18
  return new Writable({
19
19
  objectMode: true,
20
20
  ...opt,
21
- write(chunk, _, cb) {
21
+ write(_chunk, _, cb) {
22
22
  cb()
23
23
  },
24
24
  final(cb) {
@@ -6,12 +6,7 @@ import {
6
6
  UnixTimestampNumber,
7
7
  } from '@naturalcycles/js-lib'
8
8
  import { fs2 } from '../fs/fs2'
9
- import {
10
- gitCurrentBranchName,
11
- gitCurrentCommitSha,
12
- gitCurrentCommitTimestamp,
13
- gitCurrentRepoName,
14
- } from './git.util'
9
+ import { git2 } from './git2'
15
10
 
16
11
  export interface GenerateBuildInfoOptions {
17
12
  /**
@@ -24,10 +19,10 @@ export function generateBuildInfo(opt: GenerateBuildInfoOptions = {}): BuildInfo
24
19
  const now = localTime.orNow(opt.overrideTimestamp)
25
20
  const ts = now.unix
26
21
 
27
- const rev = gitCurrentCommitSha()
28
- const branchName = gitCurrentBranchName()
29
- const repoName = gitCurrentRepoName()
30
- const tsCommit = gitCurrentCommitTimestamp()
22
+ const rev = git2.gitCurrentCommitSha()
23
+ const branchName = git2.gitCurrentBranchName()
24
+ const repoName = git2.gitCurrentRepoName()
25
+ const tsCommit = git2.gitCurrentCommitTimestamp()
31
26
 
32
27
  const ver = [now.toStringCompact(), repoName, branchName, rev].join('_')
33
28