ffc-pay-etl-framework 1.4.1 → 1.4.2-beta.135

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 (31) hide show
  1. package/.npmignore +26 -0
  2. package/app/database-connections/index.js +7 -0
  3. package/app/{database_connections/postgresDatabaseConnection.js → database-connections/postgres-database-connection.js} +2 -2
  4. package/app/{database_connections/providedConnection.js → database-connections/provided-connection.js} +2 -2
  5. package/app/destinations/{consoleDestination.js → console-destination.js} +2 -2
  6. package/app/destinations/csv-file-destination.js +94 -0
  7. package/app/destinations/index.js +4 -4
  8. package/app/destinations/{postgresDestination.js → postgres-destination.js} +11 -9
  9. package/app/destinations/{sqlFileDestination.js → sql-file-destination.js} +5 -18
  10. package/app/lib/index.js +41 -44
  11. package/app/lib/{rowMetaData.js → row-meta-data.js} +3 -3
  12. package/app/loaders/{csvloader.js → csv-loader.js} +12 -12
  13. package/app/loaders/index.js +1 -1
  14. package/app/misc/index.js +5 -0
  15. package/app/misc/{postgresSQLTask.js → postgres-sql-task.js} +3 -9
  16. package/app/transformers/{fakerTransformer.js → faker-transformer.js} +5 -5
  17. package/app/transformers/index.js +3 -3
  18. package/app/transformers/{stringReplaceTransformer.js → string-replace-transformer.js} +8 -5
  19. package/app/transformers/{toUpperCaseTransformer.js → to-upper-case-transformer.js} +8 -6
  20. package/app/validators/index.js +3 -3
  21. package/app/validators/{multiToolValidator.js → multi-tool-validator.js} +7 -6
  22. package/app/validators/{requiredValidator.js → required-validator.js} +3 -3
  23. package/app/validators/{uniqueValidator.js → unique-validator.js} +15 -10
  24. package/azure-pipelines.yml +45 -0
  25. package/index.js +3 -1
  26. package/jest.config.js +42 -0
  27. package/jest.setup.js +2 -0
  28. package/package-lock.json +7990 -0
  29. package/package.json +5 -33
  30. package/app/database_connections/index.js +0 -7
  31. package/app/destinations/csvFileDestination.js +0 -75
package/.npmignore ADDED
@@ -0,0 +1,26 @@
1
+ # Ignore dotfiles explicitly by name
2
+ /.all-contributorsrc
3
+ /.dockerignore
4
+ /.gitignore
5
+ /.snyk
6
+ /CODE_OF_CONDUCT.md
7
+ /CONTRIBUTING.md
8
+ /eslint.config.js
9
+ /ISSUE_TEMPLATE.md
10
+ /Jenkinsfile
11
+ /LICENSE.md
12
+ /PULL_REQUEST_TEMPLATE.md
13
+ /sonar-project.properties
14
+ /snyk_report.html
15
+ /snyk-error.log
16
+ /snyk-monitor-result.json
17
+ /snyk-result.json
18
+
19
+ # Ignore folders recursively
20
+ __mocks__/
21
+ .devcontainer/
22
+ .github/
23
+ jest/
24
+ test/
25
+ snyk-cli/
26
+ test-output/
@@ -0,0 +1,7 @@
1
+ const { PostgresDatabaseConnection } = require('./postgres-database-connection')
2
+ const { ProvidedConnection } = require('./provided-connection')
3
+
4
+ module.exports = {
5
+ PostgresDatabaseConnection,
6
+ ProvidedConnection
7
+ }
@@ -13,7 +13,7 @@ const debug = require('debug')('connection')
13
13
  * @param {Object} options.port
14
14
  * @returns Connection
15
15
  */
16
- async function PostgresDatabaseConnection (options) {
16
+ async function postgresDatabaseConnection (options) {
17
17
  const connectionname = options.connectionname
18
18
  const username = options.username
19
19
  const password = options.password
@@ -43,5 +43,5 @@ async function PostgresDatabaseConnection (options) {
43
43
  }
44
44
 
45
45
  module.exports = {
46
- PostgresDatabaseConnection
46
+ PostgresDatabaseConnection: postgresDatabaseConnection
47
47
  }
@@ -5,7 +5,7 @@
5
5
  * @param {Object} options.sequelize
6
6
  * @returns Connection
7
7
  */
8
- async function ProvidedConnection (options) {
8
+ async function providedConnection (options) {
9
9
  const connectionname = options.connectionname
10
10
  const sequelize = options.sequelize
11
11
  return {
@@ -15,5 +15,5 @@ async function ProvidedConnection (options) {
15
15
  }
16
16
 
17
17
  module.exports = {
18
- ProvidedConnection
18
+ ProvidedConnection: providedConnection
19
19
  }
@@ -6,7 +6,7 @@ const { Writable } = require('node:stream')
6
6
  * @param {String} options.includeErrors
7
7
  * @returns Writable
8
8
  */
9
- function ConsoleDestination (options) {
9
+ function consoleDestination (options) {
10
10
  const includeErrors = options.includeErrors
11
11
  const writable = new Writable({
12
12
  objectMode: true,
@@ -34,5 +34,5 @@ function ConsoleDestination (options) {
34
34
  }
35
35
 
36
36
  module.exports = {
37
- ConsoleDestination
37
+ ConsoleDestination: consoleDestination
38
38
  }
@@ -0,0 +1,94 @@
1
+ const EventEmitter = require('node:events')
2
+ const util = require('node:util')
3
+ const { Writable } = require('node:stream')
4
+ const fs = require('node:fs')
5
+
6
+ /**
7
+ *
8
+ * @param {Object} options
9
+ * @param {String} options.fileName
10
+ * @param {Boolean} options.headers
11
+ * @param {Boolean} options.includeErrors
12
+ * @param {Boolean} options.quotationMarks
13
+ * @returns Writable
14
+ */
15
+ function csvFileDestination (options) {
16
+ EventEmitter.call(this)
17
+ let lastChunk
18
+ const fileName = options.fileName
19
+ const headers = options.headers
20
+ const includeErrors = options.includeErrors
21
+ const quotationMarks = options.quotationMarks
22
+
23
+ let fileHandle
24
+ try {
25
+ fileHandle = fs.openSync(fileName, 'w+')
26
+ } catch (error) {
27
+ throw new Error(`Failed to open file ${fileName}: ${error.message}`)
28
+ }
29
+
30
+ let headersWritten = false
31
+
32
+ const writable = new Writable({
33
+ objectMode: true,
34
+ write (chunk, _, callback) {
35
+ try {
36
+ if (!headersWritten && headers) {
37
+ if (quotationMarks) {
38
+ const quotedColumns = chunk._columns.map(c => `"${c}"`).join(',')
39
+ fs.writeSync(fileHandle, quotedColumns + '\n')
40
+ } else {
41
+ fs.writeSync(fileHandle, `${chunk._columns.join(',')}\n`)
42
+ }
43
+ headersWritten = true
44
+ }
45
+
46
+ const errors = Array.isArray(chunk.errors) ? chunk.errors : []
47
+
48
+ if (errors.length === 0 || includeErrors) {
49
+ const line = chunk.map(c => `"${c}"`).join(',') + '\n'
50
+ fs.writeSync(fileHandle, line)
51
+ }
52
+
53
+ if (this.tasks && Array.isArray(this.tasks)) {
54
+ this.tasks.forEach(task => task.write(chunk))
55
+ }
56
+
57
+ lastChunk = chunk
58
+ callback()
59
+ } catch (err) {
60
+ callback(err)
61
+ }
62
+ },
63
+ final (callback) {
64
+ if (fileHandle) {
65
+ fs.closeSync(fileHandle)
66
+ }
67
+
68
+ this.emit('result', lastChunk)
69
+ callback()
70
+ }
71
+ })
72
+
73
+ Object.assign(writable, {
74
+ setConnection: function (connection) {
75
+ this.connection = connection
76
+ }.bind(writable),
77
+
78
+ getConnectionName: function () {
79
+ return this.connection?.name
80
+ }.bind(writable),
81
+
82
+ setTasks: function (tasks) {
83
+ this.tasks = tasks
84
+ }.bind(writable)
85
+ })
86
+
87
+ return writable
88
+ }
89
+
90
+ util.inherits(csvFileDestination, EventEmitter)
91
+
92
+ module.exports = {
93
+ CSVFileDestination: csvFileDestination
94
+ }
@@ -1,7 +1,7 @@
1
- const { ConsoleDestination } = require('./consoleDestination')
2
- const { SQLFileDestination, SQL_MODE } = require('./sqlFileDestination')
3
- const { CSVFileDestination } = require('./csvFileDestination')
4
- const { PostgresDestination } = require('./postgresDestination')
1
+ const { ConsoleDestination } = require('./console-destination')
2
+ const { SQLFileDestination, SQL_MODE } = require('./sql-file-destination')
3
+ const { CSVFileDestination } = require('./csv-file-destination')
4
+ const { PostgresDestination } = require('./postgres-destination')
5
5
 
6
6
  module.exports = {
7
7
  ConsoleDestination,
@@ -7,13 +7,15 @@ function isKeyWord (column) {
7
7
  }
8
8
 
9
9
  function getMappingForColumn (mapping, column) {
10
- if (mapping.length === 0) { return {} }
11
- const [map] = mapping.filter(m => m.column === column)
12
- return map
10
+ if (mapping.length === 0) {
11
+ return {}
12
+ }
13
+ const map = mapping.find(m => m.column === column)
14
+ return map || {}
13
15
  }
14
16
 
15
17
  function hasReturningColumns (mapping) {
16
- return mapping.filter(m => m.returning === true).length > 0
18
+ return mapping.some(m => m.returning === true)
17
19
  }
18
20
 
19
21
  function getReturningColumns (mapping) {
@@ -33,9 +35,9 @@ function writeInsertStatement (columnMapping, table, chunk, schema, ignoredColum
33
35
  .join(',')}) VALUES (${filteredColumns.map(column => {
34
36
  const index = chunk._columns.indexOf(column)
35
37
  const mapping = getMappingForColumn(columnMapping, column)
36
- if (mapping?.targetType === 'number' && (isNaN(chunk[index]) || chunk[index] === '')) {
37
- debug('Source data is not a number.')
38
- return 0
38
+ if (mapping?.targetType === 'number' && (Number.isNaN(Number(chunk[index])) || chunk[index] === '')) {
39
+ debug('Source data is not a number.')
40
+ return 0
39
41
  }
40
42
  if (mapping?.targetType === 'varchar' || mapping?.targetType === 'char') {
41
43
  return `'${chunk[index]}'`
@@ -65,7 +67,7 @@ function writeInsertStatement (columnMapping, table, chunk, schema, ignoredColum
65
67
  * @param {Array<String>} [options.ignoredColumns]
66
68
  * @returns Transform
67
69
  */
68
- function PostgresDestination (options) {
70
+ function postgresDestination (options) {
69
71
  EventEmitter.call(this)
70
72
  const table = options.table
71
73
  const connectionname = options.connectionname
@@ -130,7 +132,7 @@ function PostgresDestination (options) {
130
132
  }
131
133
 
132
134
  module.exports = {
133
- PostgresDestination,
135
+ PostgresDestination: postgresDestination,
134
136
  writeInsertStatement,
135
137
  isKeyWord,
136
138
  getMappingForColumn,
@@ -1,5 +1,5 @@
1
1
  const { Writable } = require('node:stream')
2
- const fs = require('fs')
2
+ const fs = require('node:fs')
3
3
 
4
4
  /**
5
5
  * @enum {number}
@@ -19,12 +19,11 @@ const SQL_MODE = {
19
19
  * @param {Boolean} options.includeErrors
20
20
  * @returns Transform
21
21
  */
22
- function SQLFileDestination (options) {
22
+ function sqlFileDestination (options) {
23
23
  const fileName = options.fileName
24
24
  const sqlMode = options.mode
25
25
  const table = options.table
26
26
  const mapping = options.mapping
27
- const includeErrors = options.includeErrors
28
27
 
29
28
  function writeInsertStatement (chunk) {
30
29
  const statement = `INSERT INTO ${table} (${mapping.map(m => m.targetColumn)
@@ -41,21 +40,9 @@ function SQLFileDestination (options) {
41
40
  const writable = new Writable({
42
41
  objectMode: true,
43
42
  write (chunk, _, callback) {
44
- if (chunk.errors.length === 0) {
45
- if (sqlMode === SQL_MODE.INSERT_MODE) {
46
- // @ts-ignore
47
- if (chunk.errors.length === 0 | includeErrors) {
48
- writeInsertStatement(chunk)
49
- }
50
- }
43
+ if (chunk.errors.length === 0 && sqlMode === SQL_MODE.INSERT_MODE) {
44
+ writeInsertStatement(chunk)
51
45
  }
52
- // TODO - Disabled because Sonar
53
- // } else if (sqlMode === SQL_MODE.UPDATE_MODE){
54
- // // @ts-ignore
55
- // if(chunk.errors.length === 0 | includeErrors){
56
- // writeUpdateStatement(chunk)
57
- // }
58
- // }
59
46
  callback()
60
47
  }
61
48
  })
@@ -76,6 +63,6 @@ function SQLFileDestination (options) {
76
63
  }
77
64
 
78
65
  module.exports = {
79
- SQLFileDestination,
66
+ SQLFileDestination: sqlFileDestination,
80
67
  SQL_MODE
81
68
  }
package/app/lib/index.js CHANGED
@@ -1,7 +1,5 @@
1
- // @ts-nocheck
2
1
  const EventEmitter = require('node:events')
3
- const util = require('node:util')
4
- const { RowMetaData } = require('./rowMetaData')
2
+ const { RowMetaData } = require('./row-meta-data')
5
3
  const { compose } = require('node:stream')
6
4
 
7
5
  /**
@@ -18,89 +16,88 @@ const { compose } = require('node:stream')
18
16
  *
19
17
  * @returns Etl
20
18
  */
21
- function Etl () {
22
- EventEmitter.call(this)
23
- const self = this
24
- self.store = []
25
- self.beforeETLList = []
26
- self.connectionList = []
27
- self.validatorList = []
28
- self.transformationList = []
29
- self.destinationList = []
19
+ class Etl extends EventEmitter {
20
+ constructor () {
21
+ super()
22
+ this.store = []
23
+ this.beforeETLList = []
24
+ this.connectionList = []
25
+ this.validatorList = []
26
+ this.transformationList = []
27
+ this.destinationList = []
28
+ }
30
29
 
31
- this.loader = (loader) => {
32
- self.loader = loader
33
- return self
30
+ loader (loader) {
31
+ this.loader = loader
32
+ return this
34
33
  }
35
34
 
36
- this.pump = () => {
35
+ pump () {
37
36
  this.beforeETLList.forEach(task => {
38
37
  task.write({})
39
38
  })
39
+
40
40
  this.loader
41
41
  .pump(this.loader)
42
42
  .pipe(
43
43
  compose(
44
44
  RowMetaData(),
45
- ...self.validatorList,
46
- ...self.transformationList,
47
- ...self.destinationList.map(dl => dl.on('result', (data) => self.emit('result', data)))
45
+ ...this.validatorList,
46
+ ...this.transformationList,
47
+ ...this.destinationList.map(dl => dl.on('result', data => this.emit('result', data)))
48
48
  )
49
- // @ts-ignore
50
49
  )
51
- .on('error', (err) => self.emit('error', err))
52
- return self
50
+ .on('error', err => this.emit('error', err))
51
+ return this
53
52
  }
54
53
 
55
- this.beforeETL = (pipelineTask) => {
54
+ beforeETL (pipelineTask) {
56
55
  const connectionname = pipelineTask.getConnectionName()
57
- const connection = this.connectionList.filter(c => c.name === connectionname)[0]
56
+ const connection = this.connectionList.find(c => c.name === connectionname)
58
57
  if (!connection) {
59
58
  throw new Error(`Connection with name ${connectionname} not found`)
60
59
  }
61
60
  pipelineTask.setConnection(connection)
62
- pipelineTask.setETL(self)
63
- self.beforeETLList.push(pipelineTask)
64
- return self
61
+ pipelineTask.setETL(this)
62
+ this.beforeETLList.push(pipelineTask)
63
+ return this
65
64
  }
66
65
 
67
- this.connection = (connection) => {
68
- self.connectionList.push(connection)
69
- return self
66
+ connection (connection) {
67
+ this.connectionList.push(connection)
68
+ return this
70
69
  }
71
70
 
72
- this.validator = (validator) => {
73
- self.validatorList.push(validator)
74
- return self
71
+ validator (validator) {
72
+ this.validatorList.push(validator)
73
+ return this
75
74
  }
76
75
 
77
- this.destination = (destination, ...tasks) => {
76
+ destination (destination, ...tasks) {
78
77
  const connectionname = destination.getConnectionName()
79
- const connection = this.connectionList.filter(c => c.name === connectionname)[0]
78
+ const connection = this.connectionList.find(c => c.name === connectionname)
80
79
  if (!connection && destination.type === 'PostgresDestination') {
81
80
  throw new Error(`No connection could be found with name ${connectionname}`)
82
81
  } else {
83
82
  destination.setConnection(connection)
84
83
  }
85
84
 
86
- if (tasks) {
85
+ if (tasks && tasks.length > 0) {
87
86
  for (const task of tasks) {
88
- task.setETL(self)
87
+ task.setETL(this)
89
88
  }
90
89
  destination.setTasks(tasks)
91
90
  }
92
- self.destinationList.push(destination)
93
- return self
91
+ this.destinationList.push(destination)
92
+ return this
94
93
  }
95
94
 
96
- this.transform = (transform) => {
97
- self.transformationList.push(transform)
98
- return self
95
+ transform (transform) {
96
+ this.transformationList.push(transform)
97
+ return this
99
98
  }
100
99
  }
101
100
 
102
- util.inherits(Etl, EventEmitter)
103
-
104
101
  module.exports = {
105
102
  Etl
106
103
  }
@@ -1,7 +1,7 @@
1
1
  // @ts-nocheck
2
- const { Transform } = require('stream')
2
+ const { Transform } = require('node:stream')
3
3
 
4
- function RowMetaData (options) {
4
+ function rowMetaData () {
5
5
  return new Transform({
6
6
  readableObjectMode: true,
7
7
  writableObjectMode: true,
@@ -20,5 +20,5 @@ function RowMetaData (options) {
20
20
  }
21
21
 
22
22
  module.exports = {
23
- RowMetaData
23
+ RowMetaData: rowMetaData
24
24
  }
@@ -1,6 +1,6 @@
1
1
  // @ts-nocheck
2
- const fs = require('fs')
3
- const { Transform } = require('stream')
2
+ const fs = require('node:fs')
3
+ const { Transform } = require('node:stream')
4
4
  const { parse } = require('csv-parse')
5
5
 
6
6
  /**
@@ -12,18 +12,18 @@ const { parse } = require('csv-parse')
12
12
  * @param {Boolean} [options.relax]
13
13
  * @returns StreamReader
14
14
  */
15
- function CSVLoader (options) {
16
- let csvLoader
15
+ function csvLoader (options) {
16
+ let loader
17
17
  if (options.path) {
18
- csvLoader = fs.createReadStream(options.path)
18
+ loader = fs.createReadStream(options.path)
19
19
  } else {
20
- csvLoader = options.stream
20
+ loader = options.stream
21
21
  }
22
22
  let lineCount = 1
23
23
  const fromLine = options.startingLine ?? 2
24
24
  const relaxQuotes = options.relax ?? false
25
- csvLoader._columns = options.columns
26
- csvLoader.pump = (csvLoader) => {
25
+ loader._columns = options.columns
26
+ loader.pump = () => {
27
27
  const parser = parse({ delimiter: ',', from_line: fromLine, relax_quotes: relaxQuotes })
28
28
  const transformer = new Transform({
29
29
  readableObjectMode: true,
@@ -38,21 +38,21 @@ function CSVLoader (options) {
38
38
  options.columns.forEach((_column, index) => {
39
39
  if (chunk[index]) {
40
40
  // eslint-disable-next-line no-control-regex
41
- chunk[index] = chunk[index].replace(/[\x00-\x1F\x7F-\x9F]/g, '')
41
+ chunk[index] = chunk[index].replaceAll(/[\x00-\x1F\x7F-\x9F]/g, '')
42
42
  }
43
43
  })
44
44
 
45
45
  callback(null, chunk)
46
46
  }
47
47
  })
48
- return csvLoader
48
+ return loader
49
49
  .pipe(parser)
50
50
  .pipe(transformer)
51
51
  }
52
52
 
53
- return csvLoader
53
+ return loader
54
54
  }
55
55
 
56
56
  module.exports = {
57
- CSVLoader
57
+ CSVLoader: csvLoader
58
58
  }
@@ -1,4 +1,4 @@
1
- const { CSVLoader } = require('./csvloader')
1
+ const { CSVLoader } = require('./csv-loader')
2
2
 
3
3
  module.exports = {
4
4
  CSVLoader
package/app/misc/index.js CHANGED
@@ -0,0 +1,5 @@
1
+ const { PostgresSQLTask } = require('./postgres-sql-task')
2
+
3
+ module.exports = {
4
+ PostgresSQLTask
5
+ }
@@ -1,5 +1,5 @@
1
1
  // @ts-nocheck
2
- const { PassThrough } = require('stream')
2
+ const { PassThrough } = require('node:stream')
3
3
  const startPosOffset = 3
4
4
  const endPosOffset = 1
5
5
 
@@ -23,7 +23,7 @@ function doPlaceHolderValueInterpolations (chunk, sql, placeholders) {
23
23
  return sql
24
24
  }
25
25
 
26
- function PostgresSQLTask (options) {
26
+ function postgresSQLTask (options) {
27
27
  const passthrough = new PassThrough({
28
28
  readableObjectMode: true,
29
29
  writableObjectMode: true,
@@ -39,17 +39,11 @@ function PostgresSQLTask (options) {
39
39
  this.connection.db.query(this.sql)
40
40
  } else {
41
41
  const interpolatedSql = doPlaceHolderValueInterpolations(chunk, this.sql, placeholders)
42
- // TODO add more interpolation mechanisms to specify return values
43
- // e.g. 'myReturnVal = SELECT MAX ID FROM TABLE;'
44
- // and write to etl.store.myReturnVal or
45
- // e.g. 'chunk.myReturnVal = SELECT MAX ID FROM TABLE;'
46
- // and write to the chunk in say chunk.store.myReturnVal
47
42
  this.connection.db.query(interpolatedSql)
48
43
  }
49
44
  callback(null, chunk)
50
45
  }
51
46
  })
52
- // Should definately split this out into a mixin
53
47
  Object.assign(passthrough, {
54
48
  setConnection: function (connection) {
55
49
  this.connection = connection
@@ -65,7 +59,7 @@ function PostgresSQLTask (options) {
65
59
  }
66
60
 
67
61
  module.exports = {
68
- PostgresSQLTask,
62
+ PostgresSQLTask: postgresSQLTask,
69
63
  getPlaceHolders,
70
64
  doPlaceHolderValueInterpolations
71
65
  }
@@ -14,9 +14,8 @@ const { Transform } = require('node:stream')
14
14
  * @param {String} options.locale
15
15
  * @returns StreamReader
16
16
  */
17
- function FakerTransformer (options) {
18
- const self = this
19
- self.columns = options.columns
17
+ function fakerTransformer (options) {
18
+ const columns = options.columns
20
19
  let faker
21
20
  if (options.locale) {
22
21
  faker = require(`@faker-js/faker/locale/${options.locale}`).faker
@@ -29,12 +28,13 @@ function FakerTransformer (options) {
29
28
  return Object.keys(a).length === 0 ? faker[b] : a[b]
30
29
  }, {})
31
30
  }
31
+
32
32
  return new Transform({
33
33
  readableObjectMode: true,
34
34
  writableObjectMode: true,
35
35
  transform (chunk, _, callback) {
36
36
  const { _columns } = chunk
37
- self.columns.forEach(column => {
37
+ columns.forEach(column => {
38
38
  const colIndex = _columns.indexOf(column.name)
39
39
  chunk[colIndex] = getFaker(column.faker)()
40
40
  })
@@ -44,5 +44,5 @@ function FakerTransformer (options) {
44
44
  }
45
45
 
46
46
  module.exports = {
47
- FakerTransformer
47
+ FakerTransformer: fakerTransformer
48
48
  }
@@ -1,6 +1,6 @@
1
- const { ToUpperCaseTransformer } = require('./toUpperCaseTransformer')
2
- const { FakerTransformer } = require('./fakerTransformer')
3
- const { StringReplaceTransformer } = require('./stringReplaceTransformer')
1
+ const { ToUpperCaseTransformer } = require('./to-upper-case-transformer')
2
+ const { FakerTransformer } = require('./faker-transformer')
3
+ const { StringReplaceTransformer } = require('./string-replace-transformer')
4
4
 
5
5
  module.exports = {
6
6
  StringReplaceTransformer,
@@ -6,9 +6,8 @@ const { Transform } = require('node:stream')
6
6
  * @param {String} options.column
7
7
  * @returns Writable
8
8
  */
9
- function StringReplaceTransformer (options) {
10
- const self = this
11
- self.replacements = options
9
+ function stringReplaceTransformer (options) {
10
+ const replacements = options
12
11
 
13
12
  return new Transform({
14
13
  readableObjectMode: true,
@@ -16,8 +15,12 @@ function StringReplaceTransformer (options) {
16
15
  transform (chunk, _, callback) {
17
16
  const { _columns } = chunk
18
17
  // @ts-ignore
19
- self.replacements.forEach(r => {
18
+ replacements.forEach(r => {
20
19
  const colIndex = _columns.indexOf(r.column)
20
+ if (colIndex === -1) {
21
+ // column not found, skip
22
+ return
23
+ }
21
24
  if (r.all) {
22
25
  chunk[colIndex] = chunk[colIndex].replaceAll(r.find, r.replace)
23
26
  } else {
@@ -31,5 +34,5 @@ function StringReplaceTransformer (options) {
31
34
  }
32
35
 
33
36
  module.exports = {
34
- StringReplaceTransformer
37
+ StringReplaceTransformer: stringReplaceTransformer
35
38
  }
@@ -6,22 +6,24 @@ const { Transform } = require('node:stream')
6
6
  * @param {String} options.column
7
7
  * @returns Writable
8
8
  */
9
- function ToUpperCaseTransformer (options) {
10
- const self = this
11
- self.column = options.column
9
+ function toUpperCaseTransformer (options) {
10
+ const column = options.column
12
11
 
13
12
  return new Transform({
14
13
  readableObjectMode: true,
15
14
  writableObjectMode: true,
16
15
  transform (chunk, _, callback) {
17
16
  const { _columns } = chunk
18
- const colIndex = _columns.indexOf(self.column)
19
- chunk[colIndex] = chunk[colIndex].toUpperCase()
17
+ const colIndex = _columns.indexOf(column)
18
+
19
+ if (colIndex !== -1 && chunk[colIndex] != null) {
20
+ chunk[colIndex] = chunk[colIndex].toUpperCase()
21
+ }
20
22
  callback(null, chunk)
21
23
  }
22
24
  })
23
25
  }
24
26
 
25
27
  module.exports = {
26
- ToUpperCaseTransformer
28
+ ToUpperCaseTransformer: toUpperCaseTransformer
27
29
  }