datera 0.1.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 (159) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +132 -0
  3. package/dist/api/clean.d.ts +138 -0
  4. package/dist/api/clean.js +138 -0
  5. package/dist/api/formats.d.ts +19 -0
  6. package/dist/api/formats.js +106 -0
  7. package/dist/api/index.d.ts +8 -0
  8. package/dist/api/index.js +27 -0
  9. package/dist/api/inspect.d.ts +13 -0
  10. package/dist/api/inspect.js +44 -0
  11. package/dist/api/parse.d.ts +2 -0
  12. package/dist/api/parse.js +19 -0
  13. package/dist/api/rows.d.ts +23 -0
  14. package/dist/api/rows.js +63 -0
  15. package/dist/cli.d.ts +62 -0
  16. package/dist/cli.js +225 -0
  17. package/dist/commands.d.ts +3 -0
  18. package/dist/commands.js +169 -0
  19. package/dist/config/check.d.ts +11 -0
  20. package/dist/config/check.js +99 -0
  21. package/dist/config/index.d.ts +7 -0
  22. package/dist/config/index.js +16 -0
  23. package/dist/config/ioSchema.d.ts +97 -0
  24. package/dist/config/ioSchema.js +111 -0
  25. package/dist/config/load.d.ts +5 -0
  26. package/dist/config/load.js +68 -0
  27. package/dist/config/mergeSchema.d.ts +36 -0
  28. package/dist/config/mergeSchema.js +46 -0
  29. package/dist/config/messages.d.ts +7 -0
  30. package/dist/config/messages.js +33 -0
  31. package/dist/config/prepareSchema.d.ts +12 -0
  32. package/dist/config/prepareSchema.js +15 -0
  33. package/dist/config/schema.d.ts +195 -0
  34. package/dist/config/schema.js +44 -0
  35. package/dist/config/validationSchema.d.ts +27 -0
  36. package/dist/config/validationSchema.js +50 -0
  37. package/dist/env.d.ts +2 -0
  38. package/dist/env.js +15 -0
  39. package/dist/filters/combine.d.ts +10 -0
  40. package/dist/filters/combine.js +60 -0
  41. package/dist/filters/combineTypes.d.ts +6 -0
  42. package/dist/filters/combineTypes.js +2 -0
  43. package/dist/filters/dedupe.d.ts +12 -0
  44. package/dist/filters/dedupe.js +45 -0
  45. package/dist/filters/fillEmpty.d.ts +9 -0
  46. package/dist/filters/fillEmpty.js +32 -0
  47. package/dist/filters/fillEmptyTypes.d.ts +5 -0
  48. package/dist/filters/fillEmptyTypes.js +2 -0
  49. package/dist/filters/merge.d.ts +15 -0
  50. package/dist/filters/merge.js +113 -0
  51. package/dist/filters/mergeStrategies.d.ts +10 -0
  52. package/dist/filters/mergeStrategies.js +69 -0
  53. package/dist/filters/mergeTypes.d.ts +32 -0
  54. package/dist/filters/mergeTypes.js +2 -0
  55. package/dist/filters/mergeUnkeyed.d.ts +3 -0
  56. package/dist/filters/mergeUnkeyed.js +21 -0
  57. package/dist/filters/types.d.ts +3 -0
  58. package/dist/filters/types.js +2 -0
  59. package/dist/filters/validate.d.ts +16 -0
  60. package/dist/filters/validate.js +79 -0
  61. package/dist/filters/validateTypes.d.ts +28 -0
  62. package/dist/filters/validateTypes.js +2 -0
  63. package/dist/index.d.ts +13 -0
  64. package/dist/index.js +40 -0
  65. package/dist/io/csv/csvFormat.d.ts +4 -0
  66. package/dist/io/csv/csvFormat.js +94 -0
  67. package/dist/io/csv/csvSink.d.ts +14 -0
  68. package/dist/io/csv/csvSink.js +32 -0
  69. package/dist/io/csv/csvSource.d.ts +13 -0
  70. package/dist/io/csv/csvSource.js +29 -0
  71. package/dist/io/custom/loader.d.ts +1 -0
  72. package/dist/io/custom/loader.js +36 -0
  73. package/dist/io/custom/registry.d.ts +13 -0
  74. package/dist/io/custom/registry.js +50 -0
  75. package/dist/io/excel/excelCell.d.ts +4 -0
  76. package/dist/io/excel/excelCell.js +29 -0
  77. package/dist/io/excel/excelSink.d.ts +14 -0
  78. package/dist/io/excel/excelSink.js +57 -0
  79. package/dist/io/excel/excelSource.d.ts +11 -0
  80. package/dist/io/excel/excelSource.js +47 -0
  81. package/dist/io/excel/workbook.d.ts +4 -0
  82. package/dist/io/excel/workbook.js +20 -0
  83. package/dist/io/factory.d.ts +5 -0
  84. package/dist/io/factory.js +106 -0
  85. package/dist/io/files.d.ts +7 -0
  86. package/dist/io/files.js +48 -0
  87. package/dist/io/header.d.ts +2 -0
  88. package/dist/io/header.js +42 -0
  89. package/dist/io/json/jsonSink.d.ts +12 -0
  90. package/dist/io/json/jsonSink.js +19 -0
  91. package/dist/io/json/jsonSource.d.ts +11 -0
  92. package/dist/io/json/jsonSource.js +66 -0
  93. package/dist/io/mysql/client.d.ts +13 -0
  94. package/dist/io/mysql/client.js +73 -0
  95. package/dist/io/mysql/mysqlSource.d.ts +13 -0
  96. package/dist/io/mysql/mysqlSource.js +25 -0
  97. package/dist/io/optional.d.ts +2 -0
  98. package/dist/io/optional.js +36 -0
  99. package/dist/io/parquet/parquetLibrary.d.ts +11 -0
  100. package/dist/io/parquet/parquetLibrary.js +20 -0
  101. package/dist/io/parquet/parquetSink.d.ts +15 -0
  102. package/dist/io/parquet/parquetSink.js +58 -0
  103. package/dist/io/parquet/parquetSource.d.ts +13 -0
  104. package/dist/io/parquet/parquetSource.js +68 -0
  105. package/dist/io/postgres/postgresSource.d.ts +26 -0
  106. package/dist/io/postgres/postgresSource.js +42 -0
  107. package/dist/io/safety.d.ts +4 -0
  108. package/dist/io/safety.js +83 -0
  109. package/dist/io/sheets/client.d.ts +10 -0
  110. package/dist/io/sheets/client.js +141 -0
  111. package/dist/io/sheets/sheetsSink.d.ts +12 -0
  112. package/dist/io/sheets/sheetsSink.js +24 -0
  113. package/dist/io/sheets/sheetsSource.d.ts +10 -0
  114. package/dist/io/sheets/sheetsSource.js +47 -0
  115. package/dist/io/sql/cell.d.ts +5 -0
  116. package/dist/io/sql/cell.js +38 -0
  117. package/dist/io/sql/driver.d.ts +1 -0
  118. package/dist/io/sql/driver.js +15 -0
  119. package/dist/io/sql/names.d.ts +1 -0
  120. package/dist/io/sql/names.js +12 -0
  121. package/dist/io/sqlite/sqliteSource.d.ts +11 -0
  122. package/dist/io/sqlite/sqliteSource.js +52 -0
  123. package/dist/io/sqlserver/sqlServerSource.d.ts +24 -0
  124. package/dist/io/sqlserver/sqlServerSource.js +49 -0
  125. package/dist/io/types.d.ts +11 -0
  126. package/dist/io/types.js +2 -0
  127. package/dist/io/xml/xmlParse.d.ts +9 -0
  128. package/dist/io/xml/xmlParse.js +178 -0
  129. package/dist/io/xml/xmlSink.d.ts +18 -0
  130. package/dist/io/xml/xmlSink.js +79 -0
  131. package/dist/io/xml/xmlSource.d.ts +11 -0
  132. package/dist/io/xml/xmlSource.js +93 -0
  133. package/dist/logger.d.ts +11 -0
  134. package/dist/logger.js +29 -0
  135. package/dist/main.d.ts +2 -0
  136. package/dist/main.js +18 -0
  137. package/dist/normalizers/builtin.d.ts +5 -0
  138. package/dist/normalizers/builtin.js +23 -0
  139. package/dist/normalizers/index.d.ts +2 -0
  140. package/dist/normalizers/index.js +14 -0
  141. package/dist/normalizers/loader.d.ts +1 -0
  142. package/dist/normalizers/loader.js +30 -0
  143. package/dist/normalizers/registry.d.ts +9 -0
  144. package/dist/normalizers/registry.js +29 -0
  145. package/dist/pipeline/index.d.ts +7 -0
  146. package/dist/pipeline/index.js +17 -0
  147. package/dist/pipeline/modes.d.ts +6 -0
  148. package/dist/pipeline/modes.js +48 -0
  149. package/dist/pipeline/report.d.ts +3 -0
  150. package/dist/pipeline/report.js +32 -0
  151. package/dist/pipeline/runEtl.d.ts +24 -0
  152. package/dist/pipeline/runEtl.js +98 -0
  153. package/dist/pipeline/steps.d.ts +4 -0
  154. package/dist/pipeline/steps.js +36 -0
  155. package/dist/pipeline/types.d.ts +34 -0
  156. package/dist/pipeline/types.js +2 -0
  157. package/dist/types.d.ts +3 -0
  158. package/dist/types.js +2 -0
  159. package/package.json +128 -0
@@ -0,0 +1,44 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.etlConfigSchema = void 0;
4
+ const zod_1 = require("zod");
5
+ const ioSchema_1 = require("./ioSchema");
6
+ const mergeSchema_1 = require("./mergeSchema");
7
+ const prepareSchema_1 = require("./prepareSchema");
8
+ const validationSchema_1 = require("./validationSchema");
9
+ exports.etlConfigSchema = zod_1.z
10
+ .object({
11
+ source: ioSchema_1.sourceSchema.optional(),
12
+ destination: ioSchema_1.destinationSchema.optional(),
13
+ tableName: zod_1.z.string().optional(),
14
+ spreadsheetId: zod_1.z.string().optional(),
15
+ credentialsPath: zod_1.z.string().optional(),
16
+ mode: zod_1.z.enum(["raw", "dedupe", "merge"]),
17
+ dbHost: zod_1.z.string().optional(),
18
+ dbPort: zod_1.z.number().optional(),
19
+ dbUser: zod_1.z.string().optional(),
20
+ dbPassword: zod_1.z.string().optional(),
21
+ dbName: zod_1.z.string().optional(),
22
+ dedupeColumn: zod_1.z.string().optional(),
23
+ dedupeStrategy: zod_1.z.enum(["keep-first", "keep-last"]).optional(),
24
+ dedupeKeyNormalizer: zod_1.z.string().optional(),
25
+ mergeKeyColumn: zod_1.z.string().optional(),
26
+ mergeColumns: zod_1.z.array(mergeSchema_1.mergeColumnSchema).optional(),
27
+ mergeEmptyKeyLabel: zod_1.z.string().optional(),
28
+ mergeRejectedKeyLabel: zod_1.z.string().optional(),
29
+ mergeKeyNormalizer: zod_1.z.string().optional(),
30
+ normalizerModules: zod_1.z.array(zod_1.z.string()).optional(),
31
+ adapterModules: zod_1.z.array(zod_1.z.string()).optional(),
32
+ fillEmpty: zod_1.z.array(prepareSchema_1.fillEmptySchema).optional(),
33
+ combineColumns: zod_1.z.array(prepareSchema_1.combineColumnsSchema).optional(),
34
+ validation: validationSchema_1.validationSchema.optional(),
35
+ })
36
+ .refine((config) => config.source !== undefined || config.tableName !== undefined, {
37
+ message: 'Faltou dizer de onde ler: coloque "source" ou os campos antigos do MySQL.',
38
+ path: ["source"],
39
+ })
40
+ .refine((config) => config.destination !== undefined ||
41
+ config.spreadsheetId !== undefined, {
42
+ message: 'Faltou dizer onde escrever: coloque "destination" ou o "spreadsheetId".',
43
+ path: ["destination"],
44
+ });
@@ -0,0 +1,27 @@
1
+ import { z } from "zod";
2
+ export declare const validationSchema: z.ZodObject<{
3
+ rules: z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
4
+ message: z.ZodOptional<z.ZodString>;
5
+ column: z.ZodString;
6
+ rule: z.ZodLiteral<"required">;
7
+ }, z.core.$strip>, z.ZodObject<{
8
+ message: z.ZodOptional<z.ZodString>;
9
+ column: z.ZodString;
10
+ rule: z.ZodLiteral<"pattern">;
11
+ pattern: z.ZodString;
12
+ flags: z.ZodOptional<z.ZodString>;
13
+ }, z.core.$strip>, z.ZodObject<{
14
+ message: z.ZodOptional<z.ZodString>;
15
+ column: z.ZodString;
16
+ rule: z.ZodLiteral<"oneOf">;
17
+ values: z.ZodArray<z.ZodString>;
18
+ ignoreCase: z.ZodOptional<z.ZodBoolean>;
19
+ }, z.core.$strip>, z.ZodObject<{
20
+ message: z.ZodOptional<z.ZodString>;
21
+ column: z.ZodString;
22
+ rule: z.ZodLiteral<"normalizer">;
23
+ normalizer: z.ZodString;
24
+ }, z.core.$strip>], "rule">>;
25
+ pendingSheet: z.ZodOptional<z.ZodString>;
26
+ reasonColumn: z.ZodOptional<z.ZodString>;
27
+ }, z.core.$strip>;
@@ -0,0 +1,50 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.validationSchema = void 0;
4
+ const zod_1 = require("zod");
5
+ const ruleMessage = { message: zod_1.z.string().optional() };
6
+ const validationRuleSchema = zod_1.z.discriminatedUnion("rule", [
7
+ zod_1.z.object({
8
+ column: zod_1.z.string(),
9
+ rule: zod_1.z.literal("required"),
10
+ ...ruleMessage,
11
+ }),
12
+ zod_1.z
13
+ .object({
14
+ column: zod_1.z.string(),
15
+ rule: zod_1.z.literal("pattern"),
16
+ pattern: zod_1.z.string(),
17
+ flags: zod_1.z.string().optional(),
18
+ ...ruleMessage,
19
+ })
20
+ .refine((value) => {
21
+ try {
22
+ new RegExp(value.pattern, value.flags);
23
+ return true;
24
+ }
25
+ catch {
26
+ return false;
27
+ }
28
+ }, {
29
+ message: "pattern não é uma expressão regular válida.",
30
+ path: ["pattern"],
31
+ }),
32
+ zod_1.z.object({
33
+ column: zod_1.z.string(),
34
+ rule: zod_1.z.literal("oneOf"),
35
+ values: zod_1.z.array(zod_1.z.string()).min(1),
36
+ ignoreCase: zod_1.z.boolean().optional(),
37
+ ...ruleMessage,
38
+ }),
39
+ zod_1.z.object({
40
+ column: zod_1.z.string(),
41
+ rule: zod_1.z.literal("normalizer"),
42
+ normalizer: zod_1.z.string(),
43
+ ...ruleMessage,
44
+ }),
45
+ ]);
46
+ exports.validationSchema = zod_1.z.object({
47
+ rules: zod_1.z.array(validationRuleSchema).min(1),
48
+ pendingSheet: zod_1.z.string().min(1).optional(),
49
+ reasonColumn: zod_1.z.string().min(1).optional(),
50
+ });
package/dist/env.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ export declare function requireEnv(name: string): string;
2
+ export declare function getOptionalEnv(name: string): string | undefined;
package/dist/env.js ADDED
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.requireEnv = requireEnv;
4
+ exports.getOptionalEnv = getOptionalEnv;
5
+ function requireEnv(name) {
6
+ const value = process.env[name];
7
+ if (!value) {
8
+ throw new Error(`Variável de ambiente ausente: ${name}`);
9
+ }
10
+ return value;
11
+ }
12
+ function getOptionalEnv(name) {
13
+ return process.env[name];
14
+ }
15
+ ;
@@ -0,0 +1,10 @@
1
+ import { Filter } from "./types";
2
+ import { TableRow } from "../types";
3
+ import { CombineColumnsConfig } from "./combineTypes";
4
+ export declare class CombineFilter implements Filter<TableRow> {
5
+ private readonly combinations;
6
+ constructor(combinations: CombineColumnsConfig[]);
7
+ apply(rows: TableRow[]): TableRow[];
8
+ private rebuildRow;
9
+ private combine;
10
+ }
@@ -0,0 +1,60 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CombineFilter = void 0;
4
+ class CombineFilter {
5
+ combinations;
6
+ constructor(combinations) {
7
+ this.combinations = combinations;
8
+ }
9
+ apply(rows) {
10
+ const targets = new Set(this.combinations.map((c) => c.into));
11
+ const sourcesToRemove = new Set(this.combinations
12
+ .filter((c) => !c.keepSources)
13
+ .flatMap((c) => c.columns)
14
+ .filter((column) => !targets.has(column)));
15
+ return rows.map((row) => {
16
+ const combined = new Map();
17
+ for (const combination of this.combinations) {
18
+ combined.set(combination.into, this.combine(row, combination));
19
+ }
20
+ return this.rebuildRow(row, combined, sourcesToRemove);
21
+ });
22
+ }
23
+ rebuildRow(row, combined, sourcesToRemove) {
24
+ const anchorOf = new Map();
25
+ for (const { into, columns } of this.combinations) {
26
+ const anchor = columns.find((column) => column in row);
27
+ if (anchor !== undefined && !(into in row))
28
+ anchorOf.set(anchor, into);
29
+ }
30
+ const result = {};
31
+ for (const [column, value] of Object.entries(row)) {
32
+ const into = anchorOf.get(column);
33
+ if (into !== undefined)
34
+ result[into] = combined.get(into) ?? null;
35
+ if (combined.has(column)) {
36
+ result[column] = combined.get(column) ?? null;
37
+ }
38
+ else if (!sourcesToRemove.has(column)) {
39
+ result[column] = value;
40
+ }
41
+ }
42
+ for (const [into, value] of combined) {
43
+ if (!(into in result))
44
+ result[into] = value;
45
+ }
46
+ return result;
47
+ }
48
+ combine(row, { columns, separator = " " }) {
49
+ const values = [];
50
+ for (const column of columns) {
51
+ const raw = row[column];
52
+ const text = raw === null || raw === undefined ? "" : String(raw).trim();
53
+ if (text === "")
54
+ return null;
55
+ values.push(text);
56
+ }
57
+ return values.join(separator);
58
+ }
59
+ }
60
+ exports.CombineFilter = CombineFilter;
@@ -0,0 +1,6 @@
1
+ export interface CombineColumnsConfig {
2
+ into: string;
3
+ columns: string[];
4
+ separator?: string | undefined;
5
+ keepSources?: boolean | undefined;
6
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,12 @@
1
+ import { Filter } from "./types";
2
+ import { TableRow } from "../types";
3
+ import { KeyNormalizer } from "../normalizers/registry";
4
+ export type DedupeStrategy = "keep-first" | "keep-last";
5
+ export declare class DedupeFilter implements Filter<TableRow> {
6
+ private readonly column;
7
+ private readonly strategy;
8
+ private readonly normalizer?;
9
+ constructor(column: string, strategy?: DedupeStrategy, normalizer?: KeyNormalizer | undefined);
10
+ private keyOf;
11
+ apply(rows: TableRow[]): TableRow[];
12
+ }
@@ -0,0 +1,45 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DedupeFilter = void 0;
4
+ class DedupeFilter {
5
+ column;
6
+ strategy;
7
+ normalizer;
8
+ constructor(column, strategy = "keep-first", normalizer) {
9
+ this.column = column;
10
+ this.strategy = strategy;
11
+ this.normalizer = normalizer;
12
+ }
13
+ keyOf(row) {
14
+ const raw = row[this.column];
15
+ if (raw === null || raw === undefined)
16
+ return undefined;
17
+ if (!this.normalizer)
18
+ return raw;
19
+ const normalized = this.normalizer(raw);
20
+ if (normalized === null)
21
+ return undefined;
22
+ return normalized.group === undefined
23
+ ? normalized.key
24
+ : `${normalized.group}\u0000${normalized.key}`;
25
+ }
26
+ apply(rows) {
27
+ const seen = new Map();
28
+ for (const row of rows) {
29
+ const key = this.keyOf(row);
30
+ if (key === undefined) {
31
+ seen.set(Symbol(), row);
32
+ continue;
33
+ }
34
+ if (!seen.has(key)) {
35
+ seen.set(key, row);
36
+ }
37
+ else if (this.strategy === "keep-last") {
38
+ seen.delete(key);
39
+ seen.set(key, row);
40
+ }
41
+ }
42
+ return Array.from(seen.values());
43
+ }
44
+ }
45
+ exports.DedupeFilter = DedupeFilter;
@@ -0,0 +1,9 @@
1
+ import { Filter } from "./types";
2
+ import { TableRow } from "../types";
3
+ import { FillEmptyConfig } from "./fillEmptyTypes";
4
+ export declare class FillEmptyFilter implements Filter<TableRow> {
5
+ private readonly rules;
6
+ constructor(rules: FillEmptyConfig[]);
7
+ apply(rows: TableRow[]): TableRow[];
8
+ private isEmpty;
9
+ }
@@ -0,0 +1,32 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.FillEmptyFilter = void 0;
4
+ class FillEmptyFilter {
5
+ rules;
6
+ constructor(rules) {
7
+ this.rules = rules;
8
+ }
9
+ apply(rows) {
10
+ return rows.map((row) => {
11
+ const result = { ...row };
12
+ for (const { column, fallbackColumns = [], default: fallbackValue, } of this.rules) {
13
+ if (!this.isEmpty(row[column]))
14
+ continue;
15
+ const fromColumn = fallbackColumns
16
+ .map((fallback) => row[fallback])
17
+ .find((value) => !this.isEmpty(value));
18
+ if (fromColumn !== undefined) {
19
+ result[column] = fromColumn;
20
+ }
21
+ else if (fallbackValue !== undefined) {
22
+ result[column] = fallbackValue;
23
+ }
24
+ }
25
+ return result;
26
+ });
27
+ }
28
+ isEmpty(value) {
29
+ return (value === null || value === undefined || String(value).trim() === "");
30
+ }
31
+ }
32
+ exports.FillEmptyFilter = FillEmptyFilter;
@@ -0,0 +1,5 @@
1
+ export interface FillEmptyConfig {
2
+ column: string;
3
+ fallbackColumns?: string[] | undefined;
4
+ default?: string | undefined;
5
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,15 @@
1
+ import { Filter } from "./types";
2
+ import { TableRow } from "../types";
3
+ import { MergeColumnConfig, MergeFilterOptions } from "./mergeTypes";
4
+ export declare class MergeFilter implements Filter<TableRow> {
5
+ private readonly keyColumn;
6
+ private readonly columns;
7
+ private readonly emptyKeyLabel;
8
+ private readonly rejectedKeyLabel;
9
+ private readonly normalizeKey;
10
+ constructor(keyColumn: string, columns: MergeColumnConfig[], options?: MergeFilterOptions);
11
+ apply(rows: TableRow[]): TableRow[];
12
+ private hasGroupOverride;
13
+ private distributeOnly;
14
+ private mergeGroup;
15
+ }
@@ -0,0 +1,113 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MergeFilter = void 0;
4
+ const mergeStrategies_1 = require("./mergeStrategies");
5
+ const mergeUnkeyed_1 = require("./mergeUnkeyed");
6
+ const registry_1 = require("../normalizers/registry");
7
+ const identityNormalizer = (raw) => ({
8
+ key: typeof raw === "string" ? raw.trim() : raw,
9
+ });
10
+ class MergeFilter {
11
+ keyColumn;
12
+ columns;
13
+ emptyKeyLabel;
14
+ rejectedKeyLabel;
15
+ normalizeKey;
16
+ constructor(keyColumn, columns, options = {}) {
17
+ this.keyColumn = keyColumn;
18
+ this.columns = columns;
19
+ this.emptyKeyLabel = options.emptyKeyLabel ?? `(sem ${keyColumn})`;
20
+ this.rejectedKeyLabel =
21
+ options.rejectedKeyLabel ?? `(${keyColumn} inválido)`;
22
+ const normalizer = options.keyNormalizer;
23
+ this.normalizeKey =
24
+ typeof normalizer === "string"
25
+ ? (0, registry_1.getKeyNormalizer)(normalizer)
26
+ : (normalizer ?? identityNormalizer);
27
+ }
28
+ apply(rows) {
29
+ const groups = new Map();
30
+ const emptyKeyRows = [];
31
+ const rejectedKeyRows = [];
32
+ for (const row of rows) {
33
+ const rawKey = row[this.keyColumn];
34
+ if (rawKey === null ||
35
+ rawKey === undefined ||
36
+ (typeof rawKey === "string" && rawKey.trim() === "")) {
37
+ emptyKeyRows.push(row);
38
+ continue;
39
+ }
40
+ const normalized = this.normalizeKey(rawKey);
41
+ if (normalized === null) {
42
+ rejectedKeyRows.push(row);
43
+ continue;
44
+ }
45
+ const groupKey = normalized.group === undefined
46
+ ? normalized.key
47
+ : `${normalized.group}\u0000${normalized.key}`;
48
+ const existing = groups.get(groupKey);
49
+ if (existing) {
50
+ existing.items.push(row);
51
+ }
52
+ else {
53
+ groups.set(groupKey, {
54
+ category: normalized.group,
55
+ items: [row],
56
+ });
57
+ }
58
+ }
59
+ const merged = Array.from(groups.values()).map(({ category, items }) => items.length === 1 && !this.hasGroupOverride(category)
60
+ ? this.distributeOnly(items[0])
61
+ : this.mergeGroup(items, category));
62
+ return [
63
+ ...merged,
64
+ ...(0, mergeUnkeyed_1.buildUnkeyedRows)(emptyKeyRows, this.emptyKeyLabel, this.keyColumn, this.columns),
65
+ ...(0, mergeUnkeyed_1.buildUnkeyedRows)(rejectedKeyRows, this.rejectedKeyLabel, this.keyColumn, this.columns),
66
+ ];
67
+ }
68
+ hasGroupOverride(category) {
69
+ if (category === undefined)
70
+ return false;
71
+ return this.columns.some((c) => c.byGroup?.[category] !== undefined);
72
+ }
73
+ distributeOnly(row) {
74
+ const distributed = this.columns.filter((c) => c.strategy === "concat" && c.distribute !== undefined);
75
+ if (distributed.length === 0)
76
+ return row;
77
+ const result = { ...row };
78
+ for (const { column, separator, distribute } of distributed) {
79
+ (0, mergeStrategies_1.mergeConcatDistributed)([row], column, result, distribute, separator ?? mergeStrategies_1.DEFAULT_SEPARATOR);
80
+ }
81
+ return result;
82
+ }
83
+ mergeGroup(group, category) {
84
+ const merged = { ...group[0] };
85
+ for (const { column, strategy, separator, distribute, byGroup } of this
86
+ .columns) {
87
+ const override = category === undefined ? undefined : byGroup?.[category];
88
+ const target = override?.into ?? column;
89
+ const finalSeparator = override?.separator ?? separator ?? mergeStrategies_1.DEFAULT_SEPARATOR;
90
+ const resolvedStrategy = override?.strategy ?? strategy;
91
+ const finalDistribute = override?.distribute ?? distribute;
92
+ if (resolvedStrategy === "concat" && finalDistribute) {
93
+ (0, mergeStrategies_1.mergeConcatDistributed)(group, column, merged, finalDistribute, finalSeparator);
94
+ continue;
95
+ }
96
+ switch (resolvedStrategy) {
97
+ case "overwrite":
98
+ merged[target] = (0, mergeStrategies_1.mergeOverwrite)(group, column);
99
+ break;
100
+ case "concat":
101
+ merged[target] = (0, mergeStrategies_1.mergeConcat)(group, column, finalSeparator);
102
+ break;
103
+ case "extra-column":
104
+ (0, mergeStrategies_1.mergeExtraColumns)(group, column, merged, target);
105
+ break;
106
+ }
107
+ if (target !== column)
108
+ delete merged[column];
109
+ }
110
+ return merged;
111
+ }
112
+ }
113
+ exports.MergeFilter = MergeFilter;
@@ -0,0 +1,10 @@
1
+ import { TableRow } from "../types";
2
+ import { DistributeConfig } from "./mergeTypes";
3
+ export type CellValue = string | number | null;
4
+ export declare const DEFAULT_SEPARATOR = "; ";
5
+ export declare function presentValues(rows: TableRow[], column: string): (string | number)[];
6
+ export declare function mergeOverwrite(group: TableRow[], column: string): CellValue;
7
+ export declare function mergeConcat(group: TableRow[], column: string, separator: string): string;
8
+ export declare function mergeConcatDistributed(group: TableRow[], column: string, merged: TableRow, distribute: DistributeConfig, separator: string): void;
9
+ export declare function findFreeOverflowColumnName(column: string, merged: TableRow): string;
10
+ export declare function mergeExtraColumns(group: TableRow[], column: string, merged: TableRow, target?: string): void;
@@ -0,0 +1,69 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DEFAULT_SEPARATOR = void 0;
4
+ exports.presentValues = presentValues;
5
+ exports.mergeOverwrite = mergeOverwrite;
6
+ exports.mergeConcat = mergeConcat;
7
+ exports.mergeConcatDistributed = mergeConcatDistributed;
8
+ exports.findFreeOverflowColumnName = findFreeOverflowColumnName;
9
+ exports.mergeExtraColumns = mergeExtraColumns;
10
+ exports.DEFAULT_SEPARATOR = "; ";
11
+ function presentValues(rows, column) {
12
+ return rows
13
+ .map((row) => row[column])
14
+ .filter((v) => v !== null && v !== undefined && v !== "");
15
+ }
16
+ function mergeOverwrite(group, column) {
17
+ let result = group[0]?.[column] ?? null;
18
+ for (const row of group) {
19
+ const value = row[column];
20
+ if (value !== null && value !== undefined) {
21
+ result = value;
22
+ }
23
+ }
24
+ return result;
25
+ }
26
+ function mergeConcat(group, column, separator) {
27
+ const values = presentValues(group, column).map(String);
28
+ return Array.from(new Set(values)).join(separator);
29
+ }
30
+ function mergeConcatDistributed(group, column, merged, distribute, separator) {
31
+ const { columns, overflowInto, sources = [] } = distribute;
32
+ const sourceColumns = [column, ...sources];
33
+ const uniqueValues = Array.from(new Set(group.flatMap((row) => sourceColumns.flatMap((source) => presentValues([row], source).map(String)))));
34
+ for (const destination of columns) {
35
+ if (destination in merged)
36
+ merged[destination] = null;
37
+ }
38
+ uniqueValues.forEach((value, index) => {
39
+ if (index < columns.length) {
40
+ merged[columns[index]] = value;
41
+ }
42
+ });
43
+ const overflowValues = uniqueValues.slice(columns.length);
44
+ if (overflowValues.length > 0) {
45
+ const overflowColumn = overflowInto ?? findFreeOverflowColumnName(column, merged);
46
+ merged[overflowColumn] = overflowValues.join(separator);
47
+ }
48
+ for (const source of sourceColumns) {
49
+ if (!columns.includes(source))
50
+ delete merged[source];
51
+ }
52
+ }
53
+ function findFreeOverflowColumnName(column, merged) {
54
+ const base = `${column}_overflow`;
55
+ if (merged[base] === undefined)
56
+ return base;
57
+ let suffix = 2;
58
+ while (merged[`${base}_${suffix}`] !== undefined) {
59
+ suffix++;
60
+ }
61
+ return `${base}_${suffix}`;
62
+ }
63
+ function mergeExtraColumns(group, column, merged, target = column) {
64
+ const values = presentValues(group, column);
65
+ merged[target] = values[0] ?? null;
66
+ for (let i = 1; i < values.length; i++) {
67
+ merged[`${target}_${i + 1}`] = values[i] ?? null;
68
+ }
69
+ }
@@ -0,0 +1,32 @@
1
+ import { KeyNormalizer } from "../normalizers/registry";
2
+ export type MergeColumnStrategy = "concat" | "overwrite" | "extra-column";
3
+ export type UnkeyedColumnStrategy = "collapse-column";
4
+ export interface UnkeyedColumnConfig {
5
+ strategy: UnkeyedColumnStrategy;
6
+ into?: string | undefined;
7
+ separator?: string | undefined;
8
+ }
9
+ export interface DistributeConfig {
10
+ columns: string[];
11
+ overflowInto?: string | undefined;
12
+ sources?: string[] | undefined;
13
+ }
14
+ export interface GroupColumnOverride {
15
+ strategy: MergeColumnStrategy;
16
+ separator?: string | undefined;
17
+ into?: string | undefined;
18
+ distribute?: DistributeConfig | undefined;
19
+ }
20
+ export interface MergeColumnConfig {
21
+ column: string;
22
+ strategy: MergeColumnStrategy;
23
+ separator?: string | undefined;
24
+ distribute?: DistributeConfig | undefined;
25
+ unkeyed?: UnkeyedColumnConfig | undefined;
26
+ byGroup?: Record<string, GroupColumnOverride> | undefined;
27
+ }
28
+ export interface MergeFilterOptions {
29
+ emptyKeyLabel?: string | undefined;
30
+ rejectedKeyLabel?: string | undefined;
31
+ keyNormalizer?: string | KeyNormalizer | undefined;
32
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,3 @@
1
+ import { TableRow } from "../types";
2
+ import { MergeColumnConfig } from "./mergeTypes";
3
+ export declare function buildUnkeyedRows(rows: TableRow[], label: string, keyColumn: string, columns: MergeColumnConfig[]): TableRow[];
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.buildUnkeyedRows = buildUnkeyedRows;
4
+ const mergeStrategies_1 = require("./mergeStrategies");
5
+ function buildUnkeyedRows(rows, label, keyColumn, columns) {
6
+ if (rows.length === 0)
7
+ return [];
8
+ const collapsing = columns.filter((c) => c.unkeyed?.strategy === "collapse-column");
9
+ if (collapsing.length === 0) {
10
+ return rows.map((row) => ({ ...row, [keyColumn]: label }));
11
+ }
12
+ const collapsed = { [keyColumn]: label };
13
+ for (const { column, unkeyed } of collapsing) {
14
+ const target = unkeyed?.into ?? `${column} ${label}`;
15
+ const separator = unkeyed?.separator ?? mergeStrategies_1.DEFAULT_SEPARATOR;
16
+ collapsed[target] = (0, mergeStrategies_1.presentValues)(rows, column)
17
+ .map(String)
18
+ .join(separator);
19
+ }
20
+ return [collapsed];
21
+ }
@@ -0,0 +1,3 @@
1
+ export interface Filter<T> {
2
+ apply(rows: T[]): T[];
3
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,16 @@
1
+ import { TableRow } from "../types";
2
+ import { ValidationRule } from "./validateTypes";
3
+ export declare const DEFAULT_PENDING_SHEET = "Pend\u00EAncias";
4
+ export declare const DEFAULT_REASON_COLUMN = "Motivo";
5
+ export interface ValidationResult {
6
+ valid: TableRow[];
7
+ pending: TableRow[];
8
+ }
9
+ export declare class RowValidator {
10
+ private readonly reasonColumn;
11
+ private readonly checks;
12
+ constructor(rules: ValidationRule[], reasonColumn?: string);
13
+ split(rows: TableRow[]): ValidationResult;
14
+ private passes;
15
+ private buildCheck;
16
+ }