dsh-data-quality 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 (91) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/LICENSE +201 -0
  3. package/README.es.md +181 -0
  4. package/README.hi.md +181 -0
  5. package/README.md +181 -0
  6. package/README.pt.md +181 -0
  7. package/README.zh.md +181 -0
  8. package/THIRD_PARTY_NOTICES.md +20 -0
  9. package/cordis.patch.yml +46 -0
  10. package/lib/index.js +2458 -0
  11. package/lib/types/clean.d.ts +82 -0
  12. package/lib/types/clean.d.ts.map +1 -0
  13. package/lib/types/clean.js +351 -0
  14. package/lib/types/clean.js.map +1 -0
  15. package/lib/types/config.d.ts +47 -0
  16. package/lib/types/config.d.ts.map +1 -0
  17. package/lib/types/config.js +66 -0
  18. package/lib/types/config.js.map +1 -0
  19. package/lib/types/dataset.d.ts +133 -0
  20. package/lib/types/dataset.d.ts.map +1 -0
  21. package/lib/types/dataset.js +404 -0
  22. package/lib/types/dataset.js.map +1 -0
  23. package/lib/types/events.d.ts +73 -0
  24. package/lib/types/events.d.ts.map +1 -0
  25. package/lib/types/events.js +41 -0
  26. package/lib/types/events.js.map +1 -0
  27. package/lib/types/index.d.ts +45 -0
  28. package/lib/types/index.d.ts.map +1 -0
  29. package/lib/types/index.js +78 -0
  30. package/lib/types/index.js.map +1 -0
  31. package/lib/types/present.d.ts +24 -0
  32. package/lib/types/present.d.ts.map +1 -0
  33. package/lib/types/present.js +34 -0
  34. package/lib/types/present.js.map +1 -0
  35. package/lib/types/profile.d.ts +79 -0
  36. package/lib/types/profile.d.ts.map +1 -0
  37. package/lib/types/profile.js +196 -0
  38. package/lib/types/profile.js.map +1 -0
  39. package/lib/types/provider-local.d.ts +56 -0
  40. package/lib/types/provider-local.d.ts.map +1 -0
  41. package/lib/types/provider-local.js +163 -0
  42. package/lib/types/provider-local.js.map +1 -0
  43. package/lib/types/service.d.ts +160 -0
  44. package/lib/types/service.d.ts.map +1 -0
  45. package/lib/types/service.js +25 -0
  46. package/lib/types/service.js.map +1 -0
  47. package/lib/types/store.d.ts +61 -0
  48. package/lib/types/store.d.ts.map +1 -0
  49. package/lib/types/store.js +42 -0
  50. package/lib/types/store.js.map +1 -0
  51. package/lib/types/tools/clean.d.ts +14 -0
  52. package/lib/types/tools/clean.d.ts.map +1 -0
  53. package/lib/types/tools/clean.js +146 -0
  54. package/lib/types/tools/clean.js.map +1 -0
  55. package/lib/types/tools/profile.d.ts +13 -0
  56. package/lib/types/tools/profile.d.ts.map +1 -0
  57. package/lib/types/tools/profile.js +91 -0
  58. package/lib/types/tools/profile.js.map +1 -0
  59. package/lib/types/tools/shared.d.ts +19 -0
  60. package/lib/types/tools/shared.d.ts.map +1 -0
  61. package/lib/types/tools/shared.js +44 -0
  62. package/lib/types/tools/shared.js.map +1 -0
  63. package/lib/types/tools/verify.d.ts +14 -0
  64. package/lib/types/tools/verify.d.ts.map +1 -0
  65. package/lib/types/tools/verify.js +160 -0
  66. package/lib/types/tools/verify.js.map +1 -0
  67. package/lib/types/verify.d.ts +124 -0
  68. package/lib/types/verify.d.ts.map +1 -0
  69. package/lib/types/verify.js +391 -0
  70. package/lib/types/verify.js.map +1 -0
  71. package/lib/types/version.d.ts +8 -0
  72. package/lib/types/version.d.ts.map +1 -0
  73. package/lib/types/version.js +8 -0
  74. package/lib/types/version.js.map +1 -0
  75. package/package.json +137 -0
  76. package/src/clean.ts +382 -0
  77. package/src/config.ts +104 -0
  78. package/src/dataset.ts +445 -0
  79. package/src/events.ts +90 -0
  80. package/src/index.ts +115 -0
  81. package/src/present.ts +38 -0
  82. package/src/profile.ts +250 -0
  83. package/src/provider-local.ts +194 -0
  84. package/src/service.ts +172 -0
  85. package/src/store.ts +74 -0
  86. package/src/tools/clean.ts +150 -0
  87. package/src/tools/profile.ts +94 -0
  88. package/src/tools/shared.ts +47 -0
  89. package/src/tools/verify.ts +163 -0
  90. package/src/verify.ts +496 -0
  91. package/src/version.ts +8 -0
@@ -0,0 +1,82 @@
1
+ /**
2
+ * Declarative cleaning rules over a parsed {@link Table}: dedupe, fill-missing,
3
+ * coerce-type, normalize-unit, trim, and map-values. Pure and deterministic —
4
+ * rules apply in array order, each seeing the previous rule's output; every
5
+ * rule logs how many rows it affected. Unknown columns and invalid rule
6
+ * parameters fail loud.
7
+ * @module dsh-data-quality/clean
8
+ */
9
+ import { type Row, type Table } from './dataset.js';
10
+ /** One declarative cleaning rule (discriminated on `rule`). */
11
+ export type CleanRule = {
12
+ readonly rule: 'dedupe';
13
+ readonly columns?: readonly string[];
14
+ } | {
15
+ readonly rule: 'fill-missing';
16
+ readonly column: string;
17
+ readonly strategy: 'constant' | 'mean' | 'median' | 'forward';
18
+ readonly value?: string | number;
19
+ } | {
20
+ readonly rule: 'coerce-type';
21
+ readonly column: string;
22
+ readonly to: 'number' | 'date' | 'boolean';
23
+ } | {
24
+ readonly rule: 'normalize-unit';
25
+ readonly column: string;
26
+ readonly factors: Readonly<Record<string, number>>;
27
+ } | {
28
+ readonly rule: 'trim';
29
+ readonly columns?: readonly string[];
30
+ } | {
31
+ readonly rule: 'map-values';
32
+ readonly column: string;
33
+ readonly map: Readonly<Record<string, string>>;
34
+ readonly else?: 'keep' | 'missing';
35
+ };
36
+ /** Raised for invalid rules or unknown columns; `message` names the rule index and reason. */
37
+ export declare class CleanRuleError extends Error {
38
+ readonly ruleIndex: number;
39
+ /**
40
+ * @param ruleIndex - index of the offending rule in the request array.
41
+ * @param message - actionable human-readable detail.
42
+ */
43
+ constructor(ruleIndex: number, message: string);
44
+ }
45
+ /** Per-rule audit entry. */
46
+ export interface CleanRuleLog {
47
+ readonly ruleIndex: number;
48
+ readonly rule: CleanRule['rule'];
49
+ /** Rows the rule changed (removed, filled, coerced, rewritten, or trimmed). */
50
+ readonly affectedRows: number;
51
+ /** Extra detail, e.g. coerce failure counts. */
52
+ readonly detail: string;
53
+ }
54
+ /** The cleaning outcome: cleaned table plus the per-rule audit log. */
55
+ export interface CleanResult {
56
+ readonly columns: string[];
57
+ readonly rows: Row[];
58
+ readonly logs: CleanRuleLog[];
59
+ readonly inputRows: number;
60
+ readonly outputRows: number;
61
+ }
62
+ /**
63
+ * Apply cleaning rules in array order; each rule sees the previous output.
64
+ * @param table - the parsed dataset.
65
+ * @param rules - non-empty rule list.
66
+ * @param options - abort signal.
67
+ * @returns the cleaned table plus the per-rule audit log.
68
+ */
69
+ export declare function applyCleanRules(table: Table, rules: readonly CleanRule[], options?: {
70
+ signal?: AbortSignal | undefined;
71
+ }): CleanResult;
72
+ /**
73
+ * Serialize a table back to CSV/TSV text with RFC-4180 quoting (used when a
74
+ * clean run writes an output file). Numbers and booleans render via `String`;
75
+ * missing cells render empty.
76
+ * @param columns - column order.
77
+ * @param rows - rows to serialize.
78
+ * @param delimiter - field delimiter.
79
+ * @returns the file text (LF newlines, trailing newline).
80
+ */
81
+ export declare function serializeDelimited(columns: readonly string[], rows: readonly Row[], delimiter: string): string;
82
+ //# sourceMappingURL=clean.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"clean.d.ts","sourceRoot":"","sources":["../../src/clean.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAA+E,KAAK,GAAG,EAAE,KAAK,KAAK,EAAE,MAAM,cAAc,CAAA;AAEhI,+DAA+D;AAC/D,MAAM,MAAM,SAAS,GACjB;IAAE,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;CAAE,GACjE;IACE,QAAQ,CAAC,IAAI,EAAE,cAAc,CAAA;IAC7B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,QAAQ,EAAE,UAAU,GAAG,MAAM,GAAG,QAAQ,GAAG,SAAS,CAAA;IAC7D,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;CACjC,GACD;IAAE,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,EAAE,QAAQ,GAAG,MAAM,GAAG,SAAS,CAAA;CAAE,GACrG;IAAE,QAAQ,CAAC,IAAI,EAAE,gBAAgB,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAA;CAAE,GAChH;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;CAAE,GAC/D;IACE,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAA;IAC3B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,GAAG,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAA;IAC9C,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;CACnC,CAAA;AAEL,8FAA8F;AAC9F,qBAAa,cAAe,SAAQ,KAAK;IAMrC,QAAQ,CAAC,SAAS,EAAE,MAAM;IAL5B;;;OAGG;gBAEQ,SAAS,EAAE,MAAM,EAC1B,OAAO,EAAE,MAAM;CAKlB;AAED,4BAA4B;AAC5B,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC,MAAM,CAAC,CAAA;IAChC,+EAA+E;IAC/E,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAA;IAC7B,gDAAgD;IAChD,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CACxB;AAED,uEAAuE;AACvE,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,CAAA;IAC1B,QAAQ,CAAC,IAAI,EAAE,GAAG,EAAE,CAAA;IACpB,QAAQ,CAAC,IAAI,EAAE,YAAY,EAAE,CAAA;IAC7B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;CAC5B;AAsPD;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,SAAS,SAAS,EAAE,EAAE,OAAO,GAAE;IAAE,MAAM,CAAC,EAAE,WAAW,GAAG,SAAS,CAAA;CAAO,GAAG,WAAW,CAyC1I;AAED;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,EAAE,IAAI,EAAE,SAAS,GAAG,EAAE,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAc9G"}
@@ -0,0 +1,351 @@
1
+ /**
2
+ * Declarative cleaning rules over a parsed {@link Table}: dedupe, fill-missing,
3
+ * coerce-type, normalize-unit, trim, and map-values. Pure and deterministic —
4
+ * rules apply in array order, each seeing the previous rule's output; every
5
+ * rule logs how many rows it affected. Unknown columns and invalid rule
6
+ * parameters fail loud.
7
+ * @module dsh-data-quality/clean
8
+ */
9
+ import { isMissing, parseBoolean, parseDate, parseNumeric, throwIfAborted } from "./dataset.js";
10
+ /** Raised for invalid rules or unknown columns; `message` names the rule index and reason. */
11
+ export class CleanRuleError extends Error {
12
+ ruleIndex;
13
+ /**
14
+ * @param ruleIndex - index of the offending rule in the request array.
15
+ * @param message - actionable human-readable detail.
16
+ */
17
+ constructor(ruleIndex, message) {
18
+ super(message);
19
+ this.ruleIndex = ruleIndex;
20
+ this.name = 'CleanRuleError';
21
+ }
22
+ }
23
+ /** Assert `column` exists; every rule validates its columns before touching data. */
24
+ function requireColumn(columns, column, ruleIndex) {
25
+ if (!columns.includes(column)) {
26
+ throw new CleanRuleError(ruleIndex, `rule ${ruleIndex}: unknown column ${JSON.stringify(column)} (columns: ${columns.join(', ')})`);
27
+ }
28
+ }
29
+ /** Assert every selected column exists. */
30
+ function requireColumns(columns, selected, ruleIndex) {
31
+ for (const column of selected)
32
+ requireColumn(columns, column, ruleIndex);
33
+ }
34
+ /** dedupe: keep the first row of each key group; later duplicates are removed. */
35
+ function applyDedupe(table, rule, ruleIndex, signal) {
36
+ const selected = rule.columns ?? table.columns;
37
+ requireColumns(table.columns, selected, ruleIndex);
38
+ const seen = new Set();
39
+ const rows = [];
40
+ let removed = 0;
41
+ for (const [index, row] of table.rows.entries()) {
42
+ if (index % 1024 === 0)
43
+ throwIfAborted(signal);
44
+ const key = JSON.stringify(selected.map((column) => row[column] ?? null));
45
+ if (seen.has(key)) {
46
+ removed += 1;
47
+ continue;
48
+ }
49
+ seen.add(key);
50
+ rows.push(row);
51
+ }
52
+ return {
53
+ table: { columns: table.columns, rows },
54
+ log: { ruleIndex, rule: 'dedupe', affectedRows: removed, detail: `removed ${removed} duplicate row(s) by [${selected.join(', ')}]` },
55
+ };
56
+ }
57
+ /** fill-missing: constant / mean / median / forward fill of missing cells in one column. */
58
+ function applyFillMissing(table, rule, ruleIndex, signal) {
59
+ requireColumn(table.columns, rule.column, ruleIndex);
60
+ let fill;
61
+ let detail;
62
+ switch (rule.strategy) {
63
+ case 'constant': {
64
+ if (rule.value === undefined) {
65
+ throw new CleanRuleError(ruleIndex, `rule ${ruleIndex}: fill-missing strategy "constant" requires a value`);
66
+ }
67
+ fill = rule.value;
68
+ detail = `filled with constant ${JSON.stringify(rule.value)}`;
69
+ break;
70
+ }
71
+ case 'mean':
72
+ case 'median': {
73
+ const values = table.rows.map((row) => parseNumeric(row[rule.column])).filter((value) => value !== undefined).sort((a, b) => a - b);
74
+ if (values.length === 0) {
75
+ throw new CleanRuleError(ruleIndex, `rule ${ruleIndex}: fill-missing strategy "${rule.strategy}" needs at least one numeric value in column ${JSON.stringify(rule.column)}`);
76
+ }
77
+ const computed = rule.strategy === 'mean'
78
+ ? values.reduce((acc, value) => acc + value, 0) / values.length
79
+ : values.length % 2 === 1
80
+ ? values[(values.length - 1) / 2]
81
+ : (values[values.length / 2 - 1] + values[values.length / 2]) / 2;
82
+ fill = computed;
83
+ detail = `filled with ${rule.strategy} ${computed}`;
84
+ break;
85
+ }
86
+ case 'forward': {
87
+ fill = undefined;
88
+ detail = 'filled with the previous non-missing value';
89
+ break;
90
+ }
91
+ }
92
+ let filled = 0;
93
+ let previous;
94
+ const rows = table.rows.map((row, index) => {
95
+ if (index % 1024 === 0)
96
+ throwIfAborted(signal);
97
+ const cell = row[rule.column];
98
+ if (!isMissing(cell)) {
99
+ previous = cell ?? null;
100
+ return row;
101
+ }
102
+ const replacement = rule.strategy === 'forward' ? previous : fill;
103
+ if (replacement === undefined)
104
+ return row;
105
+ filled += 1;
106
+ return { ...row, [rule.column]: replacement };
107
+ });
108
+ return {
109
+ table: { columns: table.columns, rows },
110
+ log: { ruleIndex, rule: 'fill-missing', affectedRows: filled, detail: `${detail}; ${filled} row(s) filled` },
111
+ };
112
+ }
113
+ /** coerce-type: rewrite one column to number / ISO date / boolean; failures become missing and are counted. */
114
+ function applyCoerceType(table, rule, ruleIndex, signal) {
115
+ requireColumn(table.columns, rule.column, ruleIndex);
116
+ let coerced = 0;
117
+ let failed = 0;
118
+ const failureExamples = [];
119
+ const rows = table.rows.map((row, index) => {
120
+ if (index % 1024 === 0)
121
+ throwIfAborted(signal);
122
+ const cell = row[rule.column];
123
+ if (isMissing(cell))
124
+ return row;
125
+ let value;
126
+ switch (rule.to) {
127
+ case 'number': {
128
+ const numeric = parseNumeric(cell);
129
+ value = numeric === undefined ? undefined : numeric;
130
+ break;
131
+ }
132
+ case 'date': {
133
+ const epoch = parseDate(cell);
134
+ if (epoch === undefined) {
135
+ value = undefined;
136
+ }
137
+ else {
138
+ const date = new Date(epoch);
139
+ value = date.getUTCHours() === 0 && date.getUTCMinutes() === 0 && date.getUTCSeconds() === 0
140
+ ? date.toISOString().slice(0, 10)
141
+ : date.toISOString();
142
+ }
143
+ break;
144
+ }
145
+ case 'boolean': {
146
+ const bool = parseBoolean(cell);
147
+ value = bool === undefined ? undefined : String(bool);
148
+ break;
149
+ }
150
+ }
151
+ if (value === undefined) {
152
+ failed += 1;
153
+ if (failureExamples.length < 3)
154
+ failureExamples.push(JSON.stringify(cell));
155
+ return { ...row, [rule.column]: null };
156
+ }
157
+ coerced += 1;
158
+ return { ...row, [rule.column]: value };
159
+ });
160
+ const detail = `${coerced} value(s) coerced to ${rule.to}` +
161
+ (failed > 0 ? `; ${failed} failure(s) set to missing (e.g. ${failureExamples.join(', ')})` : '');
162
+ return {
163
+ table: { columns: table.columns, rows },
164
+ log: { ruleIndex, rule: 'coerce-type', affectedRows: coerced, detail },
165
+ };
166
+ }
167
+ /** normalize-unit: strip a unit suffix and multiply by its factor (e.g. 万 -> 1e4). */
168
+ function applyNormalizeUnit(table, rule, ruleIndex, signal) {
169
+ requireColumn(table.columns, rule.column, ruleIndex);
170
+ const entries = Object.entries(rule.factors);
171
+ if (entries.length === 0) {
172
+ throw new CleanRuleError(ruleIndex, `rule ${ruleIndex}: normalize-unit requires a non-empty factors map`);
173
+ }
174
+ for (const [suffix, factor] of entries) {
175
+ if (suffix === '')
176
+ throw new CleanRuleError(ruleIndex, `rule ${ruleIndex}: normalize-unit suffixes must not be empty`);
177
+ if (!Number.isFinite(factor) || factor <= 0) {
178
+ throw new CleanRuleError(ruleIndex, `rule ${ruleIndex}: factor for ${JSON.stringify(suffix)} must be a positive finite number, got ${String(factor)}`);
179
+ }
180
+ }
181
+ // Longest suffix first so "亿万" style prefixes cannot shadow longer units.
182
+ const ordered = [...entries].sort((a, b) => b[0].length - a[0].length);
183
+ let normalized = 0;
184
+ let skipped = 0;
185
+ const rows = table.rows.map((row, index) => {
186
+ if (index % 1024 === 0)
187
+ throwIfAborted(signal);
188
+ const cell = row[rule.column];
189
+ if (isMissing(cell))
190
+ return row;
191
+ const plain = parseNumeric(cell);
192
+ if (plain !== undefined) {
193
+ normalized += 1;
194
+ return { ...row, [rule.column]: plain };
195
+ }
196
+ if (typeof cell === 'string') {
197
+ const text = cell.trim();
198
+ for (const [suffix, factor] of ordered) {
199
+ if (!text.endsWith(suffix))
200
+ continue;
201
+ const numeric = parseNumeric(text.slice(0, text.length - suffix.length));
202
+ if (numeric === undefined)
203
+ continue;
204
+ normalized += 1;
205
+ return { ...row, [rule.column]: numeric * factor };
206
+ }
207
+ }
208
+ skipped += 1;
209
+ return row;
210
+ });
211
+ const detail = `${normalized} value(s) normalized to base units` + (skipped > 0 ? `; ${skipped} unparseable value(s) left unchanged` : '');
212
+ return {
213
+ table: { columns: table.columns, rows },
214
+ log: { ruleIndex, rule: 'normalize-unit', affectedRows: normalized, detail },
215
+ };
216
+ }
217
+ /** trim: strip leading/trailing whitespace of string cells in the selected columns. */
218
+ function applyTrim(table, rule, ruleIndex, signal) {
219
+ const selected = rule.columns ?? table.columns;
220
+ requireColumns(table.columns, selected, ruleIndex);
221
+ let trimmed = 0;
222
+ const rows = table.rows.map((row, index) => {
223
+ if (index % 1024 === 0)
224
+ throwIfAborted(signal);
225
+ let changed = false;
226
+ const next = { ...row };
227
+ for (const column of selected) {
228
+ const cell = next[column];
229
+ if (typeof cell !== 'string')
230
+ continue;
231
+ const value = cell.trim();
232
+ if (value !== cell) {
233
+ next[column] = value;
234
+ changed = true;
235
+ }
236
+ }
237
+ if (changed)
238
+ trimmed += 1;
239
+ return changed ? next : row;
240
+ });
241
+ return {
242
+ table: { columns: table.columns, rows },
243
+ log: { ruleIndex, rule: 'trim', affectedRows: trimmed, detail: `trimmed whitespace in [${selected.join(', ')}] for ${trimmed} row(s)` },
244
+ };
245
+ }
246
+ /** map-values: exact-match enum mapping; unmapped values follow the `else` policy. */
247
+ function applyMapValues(table, rule, ruleIndex, signal) {
248
+ requireColumn(table.columns, rule.column, ruleIndex);
249
+ const elsePolicy = rule.else ?? 'keep';
250
+ let mapped = 0;
251
+ let unmapped = 0;
252
+ const rows = table.rows.map((row, index) => {
253
+ if (index % 1024 === 0)
254
+ throwIfAborted(signal);
255
+ const cell = row[rule.column];
256
+ if (isMissing(cell))
257
+ return row;
258
+ const key = typeof cell === 'string' ? cell : typeof cell === 'object' && cell !== null ? JSON.stringify(cell) : String(cell);
259
+ const replacement = rule.map[key];
260
+ if (replacement !== undefined) {
261
+ mapped += 1;
262
+ return { ...row, [rule.column]: replacement };
263
+ }
264
+ if (elsePolicy === 'missing') {
265
+ unmapped += 1;
266
+ return { ...row, [rule.column]: null };
267
+ }
268
+ unmapped += 1;
269
+ return row;
270
+ });
271
+ const detail = `${mapped} value(s) mapped` + (unmapped > 0 ? `; ${unmapped} unmapped value(s) ${elsePolicy === 'missing' ? 'set to missing' : 'kept'}` : '');
272
+ return {
273
+ table: { columns: table.columns, rows },
274
+ log: { ruleIndex, rule: 'map-values', affectedRows: mapped, detail },
275
+ };
276
+ }
277
+ /**
278
+ * Apply cleaning rules in array order; each rule sees the previous output.
279
+ * @param table - the parsed dataset.
280
+ * @param rules - non-empty rule list.
281
+ * @param options - abort signal.
282
+ * @returns the cleaned table plus the per-rule audit log.
283
+ */
284
+ export function applyCleanRules(table, rules, options = {}) {
285
+ if (rules.length === 0) {
286
+ throw new CleanRuleError(0, 'rules must be a non-empty array');
287
+ }
288
+ let current = { columns: [...table.columns], rows: table.rows.map((row) => ({ ...row })) };
289
+ const logs = [];
290
+ for (const [ruleIndex, rule] of rules.entries()) {
291
+ throwIfAborted(options.signal);
292
+ let step;
293
+ switch (rule.rule) {
294
+ case 'dedupe':
295
+ step = applyDedupe(current, rule, ruleIndex, options.signal);
296
+ break;
297
+ case 'fill-missing':
298
+ step = applyFillMissing(current, rule, ruleIndex, options.signal);
299
+ break;
300
+ case 'coerce-type':
301
+ step = applyCoerceType(current, rule, ruleIndex, options.signal);
302
+ break;
303
+ case 'normalize-unit':
304
+ step = applyNormalizeUnit(current, rule, ruleIndex, options.signal);
305
+ break;
306
+ case 'trim':
307
+ step = applyTrim(current, rule, ruleIndex, options.signal);
308
+ break;
309
+ case 'map-values':
310
+ step = applyMapValues(current, rule, ruleIndex, options.signal);
311
+ break;
312
+ default:
313
+ throw new CleanRuleError(ruleIndex, `rule ${ruleIndex}: unknown rule type ${JSON.stringify(rule.rule)}`);
314
+ }
315
+ current = step.table;
316
+ logs.push(step.log);
317
+ }
318
+ return {
319
+ columns: current.columns,
320
+ rows: current.rows,
321
+ logs,
322
+ inputRows: table.rows.length,
323
+ outputRows: current.rows.length,
324
+ };
325
+ }
326
+ /**
327
+ * Serialize a table back to CSV/TSV text with RFC-4180 quoting (used when a
328
+ * clean run writes an output file). Numbers and booleans render via `String`;
329
+ * missing cells render empty.
330
+ * @param columns - column order.
331
+ * @param rows - rows to serialize.
332
+ * @param delimiter - field delimiter.
333
+ * @returns the file text (LF newlines, trailing newline).
334
+ */
335
+ export function serializeDelimited(columns, rows, delimiter) {
336
+ const encode = (cell) => {
337
+ if (isMissing(cell))
338
+ return '';
339
+ const text = typeof cell === 'string' ? cell : typeof cell === 'object' && cell !== null ? JSON.stringify(cell) : String(cell);
340
+ if (text.includes(delimiter) || text.includes('"') || text.includes('\n') || text.includes('\r')) {
341
+ return `"${text.replace(/"/gu, '""')}"`;
342
+ }
343
+ return text;
344
+ };
345
+ const lines = [columns.map((column) => encode(column)).join(delimiter)];
346
+ for (const row of rows) {
347
+ lines.push(columns.map((column) => encode(row[column])).join(delimiter));
348
+ }
349
+ return `${lines.join('\n')}\n`;
350
+ }
351
+ //# sourceMappingURL=clean.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"clean.js","sourceRoot":"","sources":["../../src/clean.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Config schema and resolution for `dsh-data-quality`. Every tunable is a
3
+ * validated {@link Config} field changeable from cordis.yml; the resolution
4
+ * step validates bounds so misconfiguration fails loud at mount.
5
+ * @module dsh-data-quality/config
6
+ */
7
+ import z from '@deepseek-ai/schemastery';
8
+ /** Raw plugin config — every field optional; {@link resolveConfig} supplies the defaults. */
9
+ export interface Config {
10
+ /** Master switch; `false` mounts nothing. */
11
+ enabled?: boolean;
12
+ /** Hard row cap per dataset load. */
13
+ maxRows?: number;
14
+ /** Hard file-size cap in MiB per dataset load. */
15
+ maxFileSizeMB?: number;
16
+ /** Default relative tolerance for numeric citation comparison. */
17
+ defaultTolerance?: number;
18
+ /** Cap on evidence rows (verify) and preview rows (clean) in one result. */
19
+ evidenceRowLimit?: number;
20
+ /** Extensions accepted as datasets (dot-prefixed, lowercase). */
21
+ allowedExtensions?: string[];
22
+ /** Absolute root for service-level calls without a session workspace; empty = process launch directory. */
23
+ workspaceRoot?: string;
24
+ /** Persist reports to the `data_quality` storage domain. */
25
+ storeReports?: boolean;
26
+ }
27
+ /** Fully resolved config handed to the runtime. */
28
+ export interface ResolvedConfig {
29
+ readonly enabled: boolean;
30
+ readonly maxRows: number;
31
+ readonly maxFileSizeMB: number;
32
+ readonly defaultTolerance: number;
33
+ readonly evidenceRowLimit: number;
34
+ readonly allowedExtensions: readonly string[];
35
+ readonly workspaceRoot: string;
36
+ readonly storeReports: boolean;
37
+ }
38
+ /** Schemastery schema: the loader validates and fills defaults before `apply`. */
39
+ export declare const Config: z<Config>;
40
+ /**
41
+ * Validate raw values and fill explicit defaults. Invalid bounds throw here —
42
+ * misconfiguration fails loud at mount even without the Schemastery loader.
43
+ * @param config - raw (possibly partial) plugin config.
44
+ * @returns the fully resolved config.
45
+ */
46
+ export declare function resolveConfig(config?: Config): ResolvedConfig;
47
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/config.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,CAAC,MAAM,0BAA0B,CAAA;AAExC,6FAA6F;AAC7F,MAAM,WAAW,MAAM;IACrB,6CAA6C;IAC7C,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,qCAAqC;IACrC,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,kDAAkD;IAClD,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,kEAAkE;IAClE,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,4EAA4E;IAC5E,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,iEAAiE;IACjE,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAA;IAC5B,2GAA2G;IAC3G,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,4DAA4D;IAC5D,YAAY,CAAC,EAAE,OAAO,CAAA;CACvB;AAED,mDAAmD;AACnD,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;IACzB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAA;IAC9B,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAA;IACjC,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAA;IACjC,QAAQ,CAAC,iBAAiB,EAAE,SAAS,MAAM,EAAE,CAAA;IAC7C,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAA;IAC9B,QAAQ,CAAC,YAAY,EAAE,OAAO,CAAA;CAC/B;AAED,kFAAkF;AAClF,eAAO,MAAM,MAAM,EAAE,CAAC,CAAC,MAAM,CAS3B,CAAA;AAgBF;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,MAAM,GAAE,MAAW,GAAG,cAAc,CA8BjE"}
@@ -0,0 +1,66 @@
1
+ /**
2
+ * Config schema and resolution for `dsh-data-quality`. Every tunable is a
3
+ * validated {@link Config} field changeable from cordis.yml; the resolution
4
+ * step validates bounds so misconfiguration fails loud at mount.
5
+ * @module dsh-data-quality/config
6
+ */
7
+ import z from '@deepseek-ai/schemastery';
8
+ /** Schemastery schema: the loader validates and fills defaults before `apply`. */
9
+ export const Config = z.object({
10
+ enabled: z.boolean().default(true),
11
+ maxRows: z.number().default(200_000),
12
+ maxFileSizeMB: z.number().default(64),
13
+ defaultTolerance: z.number().default(1e-9),
14
+ evidenceRowLimit: z.number().default(20),
15
+ allowedExtensions: z.array(z.string()).default(['.csv', '.tsv', '.json', '.jsonl']),
16
+ workspaceRoot: z.string().default(''),
17
+ storeReports: z.boolean().default(true),
18
+ });
19
+ /** Throw unless `value` is a positive safe integer. */
20
+ function assertPositiveInt(name, value) {
21
+ if (!Number.isSafeInteger(value) || value <= 0) {
22
+ throw new TypeError(`${name} must be a positive safe integer, got ${String(value)}`);
23
+ }
24
+ }
25
+ /** Throw unless `value` is a finite number in `[min, max]`. */
26
+ function assertFiniteRange(name, value, min, max) {
27
+ if (typeof value !== 'number' || !Number.isFinite(value) || value < min || value > max) {
28
+ throw new TypeError(`${name} must be a finite number in [${min}, ${max}], got ${String(value)}`);
29
+ }
30
+ }
31
+ /**
32
+ * Validate raw values and fill explicit defaults. Invalid bounds throw here —
33
+ * misconfiguration fails loud at mount even without the Schemastery loader.
34
+ * @param config - raw (possibly partial) plugin config.
35
+ * @returns the fully resolved config.
36
+ */
37
+ export function resolveConfig(config = {}) {
38
+ const maxRows = config.maxRows ?? 200_000;
39
+ assertPositiveInt('maxRows', maxRows);
40
+ const maxFileSizeMB = config.maxFileSizeMB ?? 64;
41
+ assertFiniteRange('maxFileSizeMB', maxFileSizeMB, Number.MIN_VALUE, 1024);
42
+ const defaultTolerance = config.defaultTolerance ?? 1e-9;
43
+ assertFiniteRange('defaultTolerance', defaultTolerance, 0, 1);
44
+ const evidenceRowLimit = config.evidenceRowLimit ?? 20;
45
+ assertPositiveInt('evidenceRowLimit', evidenceRowLimit);
46
+ const allowedExtensions = (config.allowedExtensions ?? ['.csv', '.tsv', '.json', '.jsonl']).map((ext) => {
47
+ if (!/^\.[a-z0-9]+$/u.test(ext)) {
48
+ throw new TypeError(`allowedExtensions entries must be dot-prefixed lowercase extensions, got ${JSON.stringify(ext)}`);
49
+ }
50
+ return ext;
51
+ });
52
+ if (allowedExtensions.length === 0) {
53
+ throw new TypeError('allowedExtensions must not be empty');
54
+ }
55
+ return {
56
+ enabled: config.enabled ?? true,
57
+ maxRows,
58
+ maxFileSizeMB,
59
+ defaultTolerance,
60
+ evidenceRowLimit,
61
+ allowedExtensions,
62
+ workspaceRoot: config.workspaceRoot ?? '',
63
+ storeReports: config.storeReports ?? true,
64
+ };
65
+ }
66
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/config.ts"],"names":[],"mappings":"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"}