@0xtorch/csv 0.0.34 → 0.0.35

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 (49) hide show
  1. package/.DS_Store +0 -0
  2. package/_cjs/detectFileEncoding.js +16 -0
  3. package/_cjs/detectFileEncoding.js.map +1 -0
  4. package/_cjs/formatCsvRows.js +3 -2
  5. package/_cjs/formatCsvRows.js.map +1 -1
  6. package/_cjs/hasValidHeaderRow.js +29 -0
  7. package/_cjs/hasValidHeaderRow.js.map +1 -0
  8. package/_cjs/index.js +7 -1
  9. package/_cjs/index.js.map +1 -1
  10. package/_cjs/parseRowsToActions/index.js +5 -4
  11. package/_cjs/parseRowsToActions/index.js.map +1 -1
  12. package/_cjs/streamCsvToActions.js +143 -0
  13. package/_cjs/streamCsvToActions.js.map +1 -0
  14. package/_esm/detectFileEncoding.js +12 -0
  15. package/_esm/detectFileEncoding.js.map +1 -0
  16. package/_esm/formatCsvRows.js +1 -1
  17. package/_esm/formatCsvRows.js.map +1 -1
  18. package/_esm/hasValidHeaderRow.js +25 -0
  19. package/_esm/hasValidHeaderRow.js.map +1 -0
  20. package/_esm/index.js +3 -0
  21. package/_esm/index.js.map +1 -1
  22. package/_esm/parseRowsToActions/index.js +1 -1
  23. package/_esm/parseRowsToActions/index.js.map +1 -1
  24. package/_esm/streamCsvToActions.js +145 -0
  25. package/_esm/streamCsvToActions.js.map +1 -0
  26. package/_types/detectFileEncoding.d.ts +3 -0
  27. package/_types/detectFileEncoding.d.ts.map +1 -0
  28. package/_types/formatCsvRows.d.ts +1 -0
  29. package/_types/formatCsvRows.d.ts.map +1 -1
  30. package/_types/hasValidHeaderRow.d.ts +9 -0
  31. package/_types/hasValidHeaderRow.d.ts.map +1 -0
  32. package/_types/index.d.ts +3 -0
  33. package/_types/index.d.ts.map +1 -1
  34. package/_types/parseRowsToActions/index.d.ts +13 -1
  35. package/_types/parseRowsToActions/index.d.ts.map +1 -1
  36. package/_types/streamCsvToActions.d.ts +20 -0
  37. package/_types/streamCsvToActions.d.ts.map +1 -0
  38. package/detectFileEncoding.ts +15 -0
  39. package/formatCsvRows.ts +1 -1
  40. package/hasValidHeaderRow.ts +36 -0
  41. package/index.ts +3 -0
  42. package/package.json +1 -1
  43. package/parseRowsToActions/index.ts +1 -1
  44. package/streamCsvToActions.ts +181 -0
  45. package/tests/bitbank-huge.csv +144245 -0
  46. package/tests/bitbank.ts +7 -0
  47. package/tests/cryptoCurrencies.json +187471 -0
  48. package/tests/cryptoCurrency.ts +10 -0
  49. package/tests/shift-jis.csv +53 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hasValidHeaderRow.d.ts","sourceRoot":"","sources":["../hasValidHeaderRow.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAA;AAExC,KAAK,2BAA2B,GAAG;IACjC,IAAI,EAAE,IAAI,GAAG,MAAM,CAAA;IACnB,QAAQ,EAAE,MAAM,CAAA;IAChB,MAAM,EAAE,SAAS,CAAA;CAClB,CAAA;AAED,eAAO,MAAM,iBAAiB,gCAI3B,2BAA2B,qBAsB7B,CAAA"}
package/_types/index.d.ts CHANGED
@@ -1,8 +1,11 @@
1
+ export { detectFileEncoding } from './detectFileEncoding';
1
2
  export { formatCsvRows } from './formatCsvRows';
3
+ export { hasValidHeaderRow } from './hasValidHeaderRow';
2
4
  export { parseCsvFileToActions } from './parseCsvFileToActions';
3
5
  export { parseCsvFileToText } from './parseCsvFileToText';
4
6
  export { parseCsvText } from './parseCsvText';
5
7
  export { parseRowsToActions } from './parseRowsToActions';
6
8
  export { csvFormatSchema } from './schemas';
9
+ export { streamCsvToActions } from './streamCsvToActions';
7
10
  export type { CsvFormat } from './types';
8
11
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAA;AAC/C,OAAO,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAA;AAC/D,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAA;AACzD,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAA;AAC7C,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAA;AACzD,OAAO,EAAE,eAAe,EAAE,MAAM,WAAW,CAAA;AAC3C,YAAY,EAAE,SAAS,EAAE,MAAM,SAAS,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAA;AACzD,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAA;AAC/C,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAA;AACvD,OAAO,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAA;AAC/D,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAA;AACzD,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAA;AAC7C,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAA;AACzD,OAAO,EAAE,eAAe,EAAE,MAAM,WAAW,CAAA;AAC3C,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAA;AACzD,YAAY,EAAE,SAAS,EAAE,MAAM,SAAS,CAAA"}
@@ -1,6 +1,6 @@
1
1
  import type { Action, CryptoCurrency, FiatCurrency } from '@0xtorch/core';
2
2
  import type { z } from 'zod';
3
- import type { parsersWithKeySchema } from '../schemas/parser';
3
+ import type { parserSchema, parsersWithKeySchema } from '../schemas/parser';
4
4
  import type { FormattedRow } from '../types';
5
5
  type ParseRowsToActionsParameters = {
6
6
  rows: readonly FormattedRow[];
@@ -18,5 +18,17 @@ type ParseRowsToActionsReturnTypes = {
18
18
  ignoreRowCount: number;
19
19
  };
20
20
  export declare const parseRowsToActions: ({ rows, parser, symbolAssetMap, service, cryptoes, fiats, errorLogger, }: ParseRowsToActionsParameters) => ParseRowsToActionsReturnTypes;
21
+ type ParseTargetRowsToActionsParameters = {
22
+ rows: readonly FormattedRow[];
23
+ parsers: readonly z.infer<typeof parserSchema>[];
24
+ symbolAssetMap?: {
25
+ readonly [symbol: string]: string;
26
+ };
27
+ service: string;
28
+ cryptoes: readonly CryptoCurrency[];
29
+ fiats: readonly FiatCurrency[];
30
+ errorLogger?: (error: unknown) => void;
31
+ };
32
+ export declare const parseTargetRowsToActions: (parameters: ParseTargetRowsToActionsParameters) => readonly Action[];
21
33
  export {};
22
34
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../parseRowsToActions/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AACzE,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAC5B,OAAO,KAAK,EAAgB,oBAAoB,EAAE,MAAM,mBAAmB,CAAA;AAC3E,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,UAAU,CAAA;AAI5C,KAAK,4BAA4B,GAAG;IAClC,IAAI,EAAE,SAAS,YAAY,EAAE,CAAA;IAC7B,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAA;IAC5C,cAAc,CAAC,EAAE;QAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAAA;KAAE,CAAA;IACtD,OAAO,EAAE,MAAM,CAAA;IACf,QAAQ,EAAE,SAAS,cAAc,EAAE,CAAA;IACnC,KAAK,EAAE,SAAS,YAAY,EAAE,CAAA;IAC9B,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAA;CACvC,CAAA;AAED,KAAK,6BAA6B,GAAG;IACnC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAA;IAC1B,cAAc,EAAE,MAAM,CAAA;CACvB,CAAA;AAED,eAAO,MAAM,kBAAkB,6EAQ5B,4BAA4B,KAAG,6BAuEjC,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../parseRowsToActions/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AACzE,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAC5B,OAAO,KAAK,EAAE,YAAY,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAA;AAC3E,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,UAAU,CAAA;AAI5C,KAAK,4BAA4B,GAAG;IAClC,IAAI,EAAE,SAAS,YAAY,EAAE,CAAA;IAC7B,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAA;IAC5C,cAAc,CAAC,EAAE;QAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAAA;KAAE,CAAA;IACtD,OAAO,EAAE,MAAM,CAAA;IACf,QAAQ,EAAE,SAAS,cAAc,EAAE,CAAA;IACnC,KAAK,EAAE,SAAS,YAAY,EAAE,CAAA;IAC9B,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAA;CACvC,CAAA;AAED,KAAK,6BAA6B,GAAG;IACnC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAA;IAC1B,cAAc,EAAE,MAAM,CAAA;CACvB,CAAA;AAED,eAAO,MAAM,kBAAkB,6EAQ5B,4BAA4B,KAAG,6BAuEjC,CAAA;AAED,KAAK,kCAAkC,GAAG;IACxC,IAAI,EAAE,SAAS,YAAY,EAAE,CAAA;IAC7B,OAAO,EAAE,SAAS,CAAC,CAAC,KAAK,CAAC,OAAO,YAAY,CAAC,EAAE,CAAA;IAChD,cAAc,CAAC,EAAE;QAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAAA;KAAE,CAAA;IACtD,OAAO,EAAE,MAAM,CAAA;IACf,QAAQ,EAAE,SAAS,cAAc,EAAE,CAAA;IACnC,KAAK,EAAE,SAAS,YAAY,EAAE,CAAA;IAC9B,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAA;CACvC,CAAA;AAED,eAAO,MAAM,wBAAwB,eACvB,kCAAkC,KAC7C,SAAS,MAAM,EAoBjB,CAAA"}
@@ -0,0 +1,20 @@
1
+ import type { Action, CryptoCurrency, FiatCurrency } from '@0xtorch/core';
2
+ import type { CsvFormat } from './types';
3
+ type StreamCsvToActionsParameters = {
4
+ file: File | string;
5
+ encoding: string;
6
+ format: CsvFormat;
7
+ cryptoes: readonly CryptoCurrency[];
8
+ fiats: readonly FiatCurrency[];
9
+ save: (actions: readonly Action[]) => Promise<void>;
10
+ chunkSize?: number;
11
+ };
12
+ type StreamCsvToActionsReturnTypes = {
13
+ success: boolean;
14
+ error?: unknown;
15
+ rowCount: number;
16
+ ignoreRowCount: number;
17
+ };
18
+ export declare const streamCsvToActions: ({ file, encoding, format, cryptoes, fiats, save, chunkSize, }: StreamCsvToActionsParameters) => Promise<StreamCsvToActionsReturnTypes>;
19
+ export {};
20
+ //# sourceMappingURL=streamCsvToActions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"streamCsvToActions.d.ts","sourceRoot":"","sources":["../streamCsvToActions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AAMzE,OAAO,KAAK,EAAE,SAAS,EAAgB,MAAM,SAAS,CAAA;AAEtD,KAAK,4BAA4B,GAAG;IAClC,IAAI,EAAE,IAAI,GAAG,MAAM,CAAA;IACnB,QAAQ,EAAE,MAAM,CAAA;IAChB,MAAM,EAAE,SAAS,CAAA;IACjB,QAAQ,EAAE,SAAS,cAAc,EAAE,CAAA;IACnC,KAAK,EAAE,SAAS,YAAY,EAAE,CAAA;IAC9B,IAAI,EAAE,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IACnD,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB,CAAA;AAID,KAAK,6BAA6B,GAAG;IACnC,OAAO,EAAE,OAAO,CAAA;IAChB,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,QAAQ,EAAE,MAAM,CAAA;IAChB,cAAc,EAAE,MAAM,CAAA;CACvB,CAAA;AAED,eAAO,MAAM,kBAAkB,kEAQ5B,4BAA4B,KAAG,OAAO,CAAC,6BAA6B,CAsItE,CAAA"}
@@ -0,0 +1,15 @@
1
+ import type { ReadableStream } from 'node:stream/web'
2
+ import { detect } from 'encoding-japanese'
3
+
4
+ export const detectFileEncoding = async (
5
+ fileStream: ReadableStream,
6
+ ): Promise<string> => {
7
+ const reader = fileStream.getReader()
8
+ const result = await reader.read()
9
+ await reader.cancel()
10
+ const detected = detect(result.value)
11
+ if (typeof detected === 'string') {
12
+ return detected
13
+ }
14
+ return detected.valueOf().toString()
15
+ }
package/formatCsvRows.ts CHANGED
@@ -25,7 +25,7 @@ export const formatCsvRows = ({
25
25
  }: FormatCsvRowsParameters): readonly FormattedRow[] =>
26
26
  rows.map((row) => formatCsvRow(row, formatter))
27
27
 
28
- const formatCsvRow = (row: RawRow, formatter: Formatter): FormattedRow =>
28
+ export const formatCsvRow = (row: RawRow, formatter: Formatter): FormattedRow =>
29
29
  Object.fromEntries(
30
30
  Object.entries(formatter).map(
31
31
  ([column, schemaTypes]): [string, FormatterValue] => {
@@ -0,0 +1,36 @@
1
+ import { parse } from 'papaparse'
2
+ import type { CsvFormat } from './types'
3
+
4
+ type HasValidHeaderRowParameters = {
5
+ file: File | string
6
+ encoding: string
7
+ format: CsvFormat
8
+ }
9
+
10
+ export const hasValidHeaderRow = async ({
11
+ file,
12
+ encoding,
13
+ format,
14
+ }: HasValidHeaderRowParameters) => {
15
+ let isValid = false
16
+ parse(file, {
17
+ header: false,
18
+ skipEmptyLines: true,
19
+ encoding,
20
+ step: (result, parser) => {
21
+ if (!Array.isArray(result.data)) {
22
+ parser.abort()
23
+ return
24
+ }
25
+ for (const key of Object.keys(format.formatter)) {
26
+ if (!result.data.includes(key)) {
27
+ parser.abort()
28
+ return
29
+ }
30
+ }
31
+ isValid = true
32
+ parser.abort()
33
+ },
34
+ })
35
+ return isValid
36
+ }
package/index.ts CHANGED
@@ -1,7 +1,10 @@
1
+ export { detectFileEncoding } from './detectFileEncoding'
1
2
  export { formatCsvRows } from './formatCsvRows'
3
+ export { hasValidHeaderRow } from './hasValidHeaderRow'
2
4
  export { parseCsvFileToActions } from './parseCsvFileToActions'
3
5
  export { parseCsvFileToText } from './parseCsvFileToText'
4
6
  export { parseCsvText } from './parseCsvText'
5
7
  export { parseRowsToActions } from './parseRowsToActions'
6
8
  export { csvFormatSchema } from './schemas'
9
+ export { streamCsvToActions } from './streamCsvToActions'
7
10
  export type { CsvFormat } from './types'
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@0xtorch/csv",
3
- "version": "0.0.34",
3
+ "version": "0.0.35",
4
4
  "description": "Cryptorch CSV extension",
5
5
  "keywords": [
6
6
  "cryptorch",
@@ -111,7 +111,7 @@ type ParseTargetRowsToActionsParameters = {
111
111
  errorLogger?: (error: unknown) => void
112
112
  }
113
113
 
114
- const parseTargetRowsToActions = (
114
+ export const parseTargetRowsToActions = (
115
115
  parameters: ParseTargetRowsToActionsParameters,
116
116
  ): readonly Action[] => {
117
117
  const { parsers, errorLogger, ...otherParameters } = parameters
@@ -0,0 +1,181 @@
1
+ import type { Action, CryptoCurrency, FiatCurrency } from '@0xtorch/core'
2
+ import { parse } from 'papaparse'
3
+ import { z } from 'zod'
4
+ import { formatCsvRow } from './formatCsvRows'
5
+ import { parseTargetRowsToActions } from './parseRowsToActions'
6
+ import { createKeyValue } from './parseRowsToActions/key'
7
+ import type { CsvFormat, FormattedRow } from './types'
8
+
9
+ type StreamCsvToActionsParameters = {
10
+ file: File | string
11
+ encoding: string
12
+ format: CsvFormat
13
+ cryptoes: readonly CryptoCurrency[]
14
+ fiats: readonly FiatCurrency[]
15
+ save: (actions: readonly Action[]) => Promise<void>
16
+ chunkSize?: number
17
+ }
18
+
19
+ const recordSchema = z.record(z.string())
20
+
21
+ type StreamCsvToActionsReturnTypes = {
22
+ success: boolean
23
+ error?: unknown
24
+ rowCount: number
25
+ ignoreRowCount: number
26
+ }
27
+
28
+ export const streamCsvToActions = async ({
29
+ file,
30
+ encoding,
31
+ format,
32
+ cryptoes,
33
+ fiats,
34
+ save,
35
+ chunkSize = 100,
36
+ }: StreamCsvToActionsParameters): Promise<StreamCsvToActionsReturnTypes> => {
37
+ // stream で処理
38
+ let success = true
39
+ let error: unknown
40
+ let ignoreRowCount = 0
41
+ let rowCount = 0
42
+ let currentKeyValue: string | undefined
43
+ let currentRows: FormattedRow[] = []
44
+ let actions: Action[] = []
45
+ parse(file, {
46
+ header: true,
47
+ skipEmptyLines: true,
48
+ encoding,
49
+ step: async (result, parsers) => {
50
+ rowCount += 1
51
+
52
+ // format
53
+ const row = recordSchema.parse(result.data)
54
+ const formattedRow = formatRow(row, format.formatter)
55
+ if (formattedRow === undefined) {
56
+ console.log('ignore:', row)
57
+ ignoreRowCount += 1
58
+ return
59
+ }
60
+
61
+ // parse
62
+ if (format.parser.key === undefined) {
63
+ currentRows = []
64
+ const rowActions = parseTargetRowsToActions({
65
+ rows: [formattedRow],
66
+ parsers: format.parser.parsers,
67
+ symbolAssetMap: format.symbolAssetMap,
68
+ service: format.service,
69
+ cryptoes,
70
+ fiats,
71
+ })
72
+ if (rowActions.length === 0) {
73
+ console.log('ignore:', row)
74
+ ignoreRowCount += 1
75
+ }
76
+ actions.push(...rowActions)
77
+ } else {
78
+ const keyValue = createKeyValue({
79
+ row: formattedRow,
80
+ key: format.parser.key,
81
+ })
82
+ if (
83
+ currentKeyValue !== undefined &&
84
+ currentKeyValue !== keyValue &&
85
+ currentRows.length > 0
86
+ ) {
87
+ const keyActions = parseTargetRowsToActions({
88
+ rows: currentRows,
89
+ parsers: format.parser.parsers,
90
+ symbolAssetMap: format.symbolAssetMap,
91
+ service: format.service,
92
+ cryptoes,
93
+ fiats,
94
+ })
95
+ if (keyActions.length === 0) {
96
+ ignoreRowCount += currentRows.length
97
+ }
98
+ actions.push(...keyActions)
99
+ currentRows = []
100
+ }
101
+ currentKeyValue = keyValue
102
+ currentRows.push(formattedRow)
103
+ }
104
+
105
+ // actions が chunkSize に達したら最後の action source と同一 source を除いて actions を保存
106
+ if (actions.length >= chunkSize + 1) {
107
+ const lastActionSource = actions[actions.length - 1].source
108
+ const savingActions: Action[] = []
109
+ const remainingActions: Action[] = []
110
+ for (const action of actions) {
111
+ if (action.source === lastActionSource) {
112
+ remainingActions.push(action)
113
+ } else {
114
+ savingActions.push(action)
115
+ }
116
+ }
117
+ actions = remainingActions
118
+ try {
119
+ await save(savingActions)
120
+ } catch (e) {
121
+ success = false
122
+ error = e
123
+ parsers.abort()
124
+ }
125
+ }
126
+ },
127
+ })
128
+
129
+ if (!success) {
130
+ return {
131
+ success: false,
132
+ error,
133
+ rowCount,
134
+ ignoreRowCount,
135
+ }
136
+ }
137
+
138
+ // currentRows が残っていたら actions に追加
139
+ if (currentRows.length > 0) {
140
+ const keyActions = parseTargetRowsToActions({
141
+ rows: currentRows,
142
+ parsers: format.parser.parsers,
143
+ symbolAssetMap: format.symbolAssetMap,
144
+ service: format.service,
145
+ cryptoes,
146
+ fiats,
147
+ })
148
+ if (keyActions.length === 0) {
149
+ ignoreRowCount += currentRows.length
150
+ }
151
+ actions.push(...keyActions)
152
+ }
153
+
154
+ // actions が残っていたら actions 保存処理
155
+ if (actions.length > 0) {
156
+ try {
157
+ await save(actions)
158
+ } catch (e) {
159
+ success = false
160
+ error = e
161
+ }
162
+ }
163
+
164
+ return {
165
+ success,
166
+ error,
167
+ rowCount,
168
+ ignoreRowCount,
169
+ }
170
+ }
171
+
172
+ const formatRow = (
173
+ row: Record<string, string>,
174
+ formatter: CsvFormat['formatter'],
175
+ ): FormattedRow | undefined => {
176
+ try {
177
+ return formatCsvRow(row, formatter)
178
+ } catch (error) {
179
+ return undefined
180
+ }
181
+ }