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,133 @@
1
+ /**
2
+ * Dataset loading for `dsh-data-quality`: workspace-confined path resolution,
3
+ * size/row guards, and deterministic parsers for CSV/TSV/JSON/JSONL. All
4
+ * parsing is hand-rolled (no external runtime dependencies) and bounded by
5
+ * the resolved config; oversized inputs reject loudly with actionable hints.
6
+ * @module dsh-data-quality/dataset
7
+ */
8
+ import type { JsonValue } from '@deepseek-ai/dsh-session';
9
+ import type { ResolvedConfig } from './config.js';
10
+ /** One cell of a tabular dataset: a JSON scalar, `null` for missing, or a nested JSON value (document rows). */
11
+ export type Cell = JsonValue;
12
+ /** One dataset row keyed by column name. */
13
+ export type Row = Record<string, Cell>;
14
+ /** A tabular dataset: ordered columns plus rows. */
15
+ export interface Table {
16
+ /** Column names in file order. */
17
+ readonly columns: string[];
18
+ /** Rows, each carrying every declared column (missing cells are `null`). */
19
+ readonly rows: Row[];
20
+ }
21
+ /** The root form a document load returns (citation checking walks this). */
22
+ export type DocumentRoot = {
23
+ readonly kind: 'table';
24
+ readonly columns: string[];
25
+ readonly rows: Row[];
26
+ } | {
27
+ readonly kind: 'json';
28
+ readonly value: unknown;
29
+ };
30
+ /** Raised for every user-facing dataset failure; `code` is stable for tests and tooling. */
31
+ export declare class DatasetError extends Error {
32
+ readonly code: 'path-escape' | 'extension-denied' | 'not-found' | 'too-large' | 'too-many-rows' | 'malformed' | 'not-tabular';
33
+ /**
34
+ * @param code - stable machine-readable reason.
35
+ * @param message - actionable human-readable detail.
36
+ */
37
+ constructor(code: 'path-escape' | 'extension-denied' | 'not-found' | 'too-large' | 'too-many-rows' | 'malformed' | 'not-tabular', message: string);
38
+ }
39
+ /**
40
+ * Resolve `requested` inside `root`, rejecting escapes and disallowed
41
+ * extensions. Both sides go through `path.resolve` before comparison so
42
+ * Windows backslash/forward-slash mixes never defeat the containment check.
43
+ * @param root - absolute workspace root.
44
+ * @param requested - the caller-supplied path (relative to root, or absolute inside root).
45
+ * @param config - resolved config (extension allowlist).
46
+ * @returns the normalized absolute path inside `root`.
47
+ */
48
+ export declare function resolveWorkspacePath(root: string, requested: string, config: ResolvedConfig): string;
49
+ /**
50
+ * Short deterministic fingerprint of a dataset path, used in storage keys.
51
+ * @param datasetPath - the workspace-relative dataset path as callers gave it.
52
+ * @returns an 8-hex-char fingerprint.
53
+ */
54
+ export declare function pathFingerprint(datasetPath: string): string;
55
+ /** Assert the abort signal has not fired; throws the signal reason otherwise. */
56
+ export declare function throwIfAborted(signal: AbortSignal | undefined): void;
57
+ /**
58
+ * Read a dataset file under the size cap.
59
+ * @param absolutePath - normalized absolute path (from {@link resolveWorkspacePath}).
60
+ * @param config - resolved config (size cap).
61
+ * @param signal - optional abort signal honored around the read.
62
+ * @returns the UTF-8 text.
63
+ */
64
+ export declare function readDatasetText(absolutePath: string, config: ResolvedConfig, signal?: AbortSignal): Promise<string>;
65
+ /**
66
+ * Parse CSV/TSV text (RFC-4180-style: quoted fields, `""` escapes, CRLF/LF).
67
+ * The first record is the header; short records pad with `null`, long records
68
+ * fail loud. An empty field parses as `null` (missing).
69
+ * @param text - file text.
70
+ * @param delimiter - field delimiter (`,` or tab).
71
+ * @param config - resolved config (row cap).
72
+ * @param signal - optional abort signal, checked per 1024-record stride.
73
+ * @returns the parsed table.
74
+ */
75
+ export declare function parseDelimited(text: string, delimiter: string, config: ResolvedConfig, signal?: AbortSignal): Table;
76
+ /**
77
+ * Parse a JSON or JSONL text into a tabular dataset. JSON must be an array of
78
+ * flat objects; JSONL is one object per line. Non-scalar cell values are kept
79
+ * as-is (engines treat them as non-scalar).
80
+ * @param text - file text.
81
+ * @param extension - `.json` or `.jsonl`.
82
+ * @param config - resolved config (row cap).
83
+ * @param signal - optional abort signal.
84
+ * @returns the parsed table.
85
+ */
86
+ export declare function parseJsonTable(text: string, extension: string, config: ResolvedConfig, signal?: AbortSignal): Table;
87
+ /**
88
+ * Load a tabular dataset from a file already resolved inside the workspace.
89
+ * @param absolutePath - normalized absolute dataset path.
90
+ * @param config - resolved config.
91
+ * @param signal - optional abort signal.
92
+ * @returns the parsed table.
93
+ */
94
+ export declare function loadTable(absolutePath: string, config: ResolvedConfig, signal?: AbortSignal): Promise<Table>;
95
+ /**
96
+ * Load a dataset as a citation-checkable document root: CSV/TSV become
97
+ * `{ columns, rows }` (so `rows[3].nav` resolves), JSON is the parsed value,
98
+ * JSONL is the array of parsed lines.
99
+ * @param absolutePath - normalized absolute dataset path.
100
+ * @param config - resolved config.
101
+ * @param signal - optional abort signal.
102
+ * @returns the document root.
103
+ */
104
+ export declare function loadDocument(absolutePath: string, config: ResolvedConfig, signal?: AbortSignal): Promise<DocumentRoot>;
105
+ /**
106
+ * Deterministic systematic sample: every `ceil(total / count)`-th row from
107
+ * index 0. No clock, no RNG — the same input always yields the same sample.
108
+ * @param rows - full row list.
109
+ * @param count - requested sample size.
110
+ * @returns the sampled rows (identity when `count >= rows.length`).
111
+ */
112
+ export declare function sampleRows(rows: Row[], count: number): Row[];
113
+ /**
114
+ * Missingness: `null`, `undefined`, or a string empty after trimming. A
115
+ * whitespace-only cell counts as missing even before any `trim` clean rule.
116
+ * @param cell - the cell to test (`undefined` when the column is absent).
117
+ * @returns whether the cell is missing.
118
+ */
119
+ export declare function isMissing(cell: Cell | undefined): boolean;
120
+ /** Strict full-string numeric parse (no thousands separators, no unit suffixes). */
121
+ export declare function parseNumeric(cell: Cell | undefined): number | undefined;
122
+ /**
123
+ * Deterministic date parse to epoch milliseconds. Accepts `YYYY-MM-DD`,
124
+ * `YYYY/MM/DD`, and ISO-like datetimes (date-only forms read as UTC midnight).
125
+ * Calendar-invalid dates (e.g. 2025-13-40) reject. Returns `undefined` when
126
+ * the cell is not a recognized date.
127
+ * @param cell - the cell to parse (`undefined` when the column is absent).
128
+ * @returns epoch milliseconds, or `undefined`.
129
+ */
130
+ export declare function parseDate(cell: Cell | undefined): number | undefined;
131
+ /** Boolean parse: true/false/yes/no/1/0, case-insensitive. */
132
+ export declare function parseBoolean(cell: Cell | undefined): boolean | undefined;
133
+ //# sourceMappingURL=dataset.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dataset.d.ts","sourceRoot":"","sources":["../../src/dataset.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,404 @@
1
+ /**
2
+ * Dataset loading for `dsh-data-quality`: workspace-confined path resolution,
3
+ * size/row guards, and deterministic parsers for CSV/TSV/JSON/JSONL. All
4
+ * parsing is hand-rolled (no external runtime dependencies) and bounded by
5
+ * the resolved config; oversized inputs reject loudly with actionable hints.
6
+ * @module dsh-data-quality/dataset
7
+ */
8
+ import { open, stat } from 'node:fs/promises';
9
+ import path from 'node:path';
10
+ import { createHash } from 'node:crypto';
11
+ /** Raised for every user-facing dataset failure; `code` is stable for tests and tooling. */
12
+ export class DatasetError extends Error {
13
+ code;
14
+ /**
15
+ * @param code - stable machine-readable reason.
16
+ * @param message - actionable human-readable detail.
17
+ */
18
+ constructor(code, message) {
19
+ super(message);
20
+ this.code = code;
21
+ this.name = 'DatasetError';
22
+ }
23
+ }
24
+ /**
25
+ * Resolve `requested` inside `root`, rejecting escapes and disallowed
26
+ * extensions. Both sides go through `path.resolve` before comparison so
27
+ * Windows backslash/forward-slash mixes never defeat the containment check.
28
+ * @param root - absolute workspace root.
29
+ * @param requested - the caller-supplied path (relative to root, or absolute inside root).
30
+ * @param config - resolved config (extension allowlist).
31
+ * @returns the normalized absolute path inside `root`.
32
+ */
33
+ export function resolveWorkspacePath(root, requested, config) {
34
+ if (requested.trim() === '') {
35
+ throw new DatasetError('path-escape', 'dataset path must not be empty');
36
+ }
37
+ const resolvedRoot = path.resolve(root);
38
+ const resolved = path.resolve(resolvedRoot, requested);
39
+ if (resolved !== resolvedRoot && !resolved.startsWith(resolvedRoot + path.sep)) {
40
+ throw new DatasetError('path-escape', `path ${JSON.stringify(requested)} resolves outside the workspace root ${JSON.stringify(resolvedRoot)}`);
41
+ }
42
+ const ext = path.extname(resolved).toLowerCase();
43
+ if (!config.allowedExtensions.includes(ext)) {
44
+ throw new DatasetError('extension-denied', `extension ${JSON.stringify(ext)} is not in allowedExtensions (${config.allowedExtensions.join(', ')})`);
45
+ }
46
+ return resolved;
47
+ }
48
+ /**
49
+ * Short deterministic fingerprint of a dataset path, used in storage keys.
50
+ * @param datasetPath - the workspace-relative dataset path as callers gave it.
51
+ * @returns an 8-hex-char fingerprint.
52
+ */
53
+ export function pathFingerprint(datasetPath) {
54
+ return createHash('sha256').update(datasetPath).digest('hex').slice(0, 8);
55
+ }
56
+ /** Assert the abort signal has not fired; throws the signal reason otherwise. */
57
+ export function throwIfAborted(signal) {
58
+ if (signal?.aborted === true) {
59
+ throw signal.reason instanceof Error ? signal.reason : new Error('operation aborted');
60
+ }
61
+ }
62
+ /**
63
+ * Read a dataset file under the size cap.
64
+ * @param absolutePath - normalized absolute path (from {@link resolveWorkspacePath}).
65
+ * @param config - resolved config (size cap).
66
+ * @param signal - optional abort signal honored around the read.
67
+ * @returns the UTF-8 text.
68
+ */
69
+ export async function readDatasetText(absolutePath, config, signal) {
70
+ throwIfAborted(signal);
71
+ let info;
72
+ try {
73
+ info = await stat(absolutePath);
74
+ }
75
+ catch {
76
+ throw new DatasetError('not-found', `dataset file not found: ${absolutePath}`);
77
+ }
78
+ if (!info.isFile()) {
79
+ throw new DatasetError('not-found', `dataset path is not a file: ${absolutePath}`);
80
+ }
81
+ const capBytes = config.maxFileSizeMB * 1024 * 1024;
82
+ if (info.size > capBytes) {
83
+ throw new DatasetError('too-large', `dataset is ${(info.size / 1024 / 1024).toFixed(1)} MiB, above the ${config.maxFileSizeMB} MiB cap; split the file or raise maxFileSizeMB`);
84
+ }
85
+ const handle = await open(absolutePath, 'r');
86
+ try {
87
+ throwIfAborted(signal);
88
+ return await handle.readFile('utf8');
89
+ }
90
+ finally {
91
+ await handle.close();
92
+ }
93
+ }
94
+ /**
95
+ * Parse CSV/TSV text (RFC-4180-style: quoted fields, `""` escapes, CRLF/LF).
96
+ * The first record is the header; short records pad with `null`, long records
97
+ * fail loud. An empty field parses as `null` (missing).
98
+ * @param text - file text.
99
+ * @param delimiter - field delimiter (`,` or tab).
100
+ * @param config - resolved config (row cap).
101
+ * @param signal - optional abort signal, checked per 1024-record stride.
102
+ * @returns the parsed table.
103
+ */
104
+ export function parseDelimited(text, delimiter, config, signal) {
105
+ const records = [];
106
+ let field = '';
107
+ let record = [];
108
+ let inQuotes = false;
109
+ const pushField = () => {
110
+ record.push(field);
111
+ field = '';
112
+ };
113
+ const pushRecord = () => {
114
+ pushField();
115
+ // Skip truly blank lines (a record of one empty field), as RFC-4180
116
+ // writers commonly emit a trailing empty line.
117
+ if (record.length === 1 && record[0] === '') {
118
+ record = [];
119
+ field = '';
120
+ return;
121
+ }
122
+ records.push(record);
123
+ record = [];
124
+ if (records.length % 1024 === 0)
125
+ throwIfAborted(signal);
126
+ if (records.length - 1 > config.maxRows) {
127
+ throw new DatasetError('too-many-rows', `dataset exceeds the ${config.maxRows} row cap; use the sample parameter or raise maxRows`);
128
+ }
129
+ };
130
+ let index = 0;
131
+ while (index < text.length) {
132
+ const char = text[index];
133
+ if (inQuotes) {
134
+ if (char === '"') {
135
+ if (text[index + 1] === '"') {
136
+ field += '"';
137
+ index += 2;
138
+ continue;
139
+ }
140
+ inQuotes = false;
141
+ index += 1;
142
+ continue;
143
+ }
144
+ field += char;
145
+ index += 1;
146
+ continue;
147
+ }
148
+ if (char === '"') {
149
+ inQuotes = true;
150
+ index += 1;
151
+ continue;
152
+ }
153
+ if (char === delimiter) {
154
+ pushField();
155
+ index += 1;
156
+ continue;
157
+ }
158
+ if (char === '\r') {
159
+ index += 1;
160
+ continue;
161
+ }
162
+ if (char === '\n') {
163
+ pushRecord();
164
+ index += 1;
165
+ continue;
166
+ }
167
+ field += char;
168
+ index += 1;
169
+ }
170
+ if (field !== '' || record.length > 0)
171
+ pushRecord();
172
+ const header = records[0];
173
+ if (header === undefined || (header.length === 1 && header[0] === '')) {
174
+ throw new DatasetError('malformed', 'dataset has no header row');
175
+ }
176
+ const columns = header.map((name) => name.trim());
177
+ if (columns.some((name) => name === '')) {
178
+ throw new DatasetError('malformed', 'header contains an empty column name');
179
+ }
180
+ if (new Set(columns).size !== columns.length) {
181
+ throw new DatasetError('malformed', 'header contains duplicate column names');
182
+ }
183
+ const rows = [];
184
+ for (let i = 1; i < records.length; i++) {
185
+ const cells = records[i];
186
+ if (cells.length > columns.length) {
187
+ throw new DatasetError('malformed', `row ${i + 1} has ${cells.length} fields but the header has ${columns.length}`);
188
+ }
189
+ const out = {};
190
+ for (let c = 0; c < columns.length; c++) {
191
+ const raw = cells[c];
192
+ out[columns[c]] = raw === undefined || raw === '' ? null : raw;
193
+ }
194
+ rows.push(out);
195
+ }
196
+ return { columns, rows };
197
+ }
198
+ /**
199
+ * Parse a JSON or JSONL text into a tabular dataset. JSON must be an array of
200
+ * flat objects; JSONL is one object per line. Non-scalar cell values are kept
201
+ * as-is (engines treat them as non-scalar).
202
+ * @param text - file text.
203
+ * @param extension - `.json` or `.jsonl`.
204
+ * @param config - resolved config (row cap).
205
+ * @param signal - optional abort signal.
206
+ * @returns the parsed table.
207
+ */
208
+ export function parseJsonTable(text, extension, config, signal) {
209
+ const values = extension === '.jsonl' ? parseJsonLines(text) : parseJsonDocument(text);
210
+ const items = Array.isArray(values) ? values : [values];
211
+ if (items.length > config.maxRows) {
212
+ throw new DatasetError('too-many-rows', `dataset exceeds the ${config.maxRows} row cap; use the sample parameter or raise maxRows`);
213
+ }
214
+ const rows = [];
215
+ const columns = [];
216
+ const seen = new Set();
217
+ for (const [index, item] of items.entries()) {
218
+ if (index % 1024 === 0)
219
+ throwIfAborted(signal);
220
+ if (typeof item !== 'object' || item === null || Array.isArray(item)) {
221
+ throw new DatasetError('not-tabular', `row ${index + 1} is not a flat object; profiling/cleaning/verification need a tabular dataset (array of objects)`);
222
+ }
223
+ const row = item;
224
+ for (const key of Object.keys(row)) {
225
+ if (!seen.has(key)) {
226
+ seen.add(key);
227
+ columns.push(key);
228
+ }
229
+ }
230
+ rows.push(row);
231
+ }
232
+ return { columns, rows: rows.map((row) => normalizeRow(row, columns)) };
233
+ }
234
+ /** Fill absent columns with `null` so every row carries every column. */
235
+ function normalizeRow(row, columns) {
236
+ const out = {};
237
+ for (const column of columns) {
238
+ const value = row[column];
239
+ out[column] = value === undefined ? null : value;
240
+ }
241
+ return out;
242
+ }
243
+ /** Parse one JSON document; failures become a loud malformed error. */
244
+ function parseJsonDocument(text) {
245
+ try {
246
+ return JSON.parse(text);
247
+ }
248
+ catch (error) {
249
+ throw new DatasetError('malformed', `invalid JSON: ${error instanceof Error ? error.message : String(error)}`);
250
+ }
251
+ }
252
+ /** Parse JSONL (one JSON value per non-empty line); failures name the line. */
253
+ function parseJsonLines(text) {
254
+ const out = [];
255
+ const lines = text.split(/\r?\n/u);
256
+ for (const [index, line] of lines.entries()) {
257
+ if (line.trim() === '')
258
+ continue;
259
+ try {
260
+ out.push(JSON.parse(line));
261
+ }
262
+ catch {
263
+ throw new DatasetError('malformed', `invalid JSON on line ${index + 1}`);
264
+ }
265
+ }
266
+ return out;
267
+ }
268
+ /**
269
+ * Load a tabular dataset from a file already resolved inside the workspace.
270
+ * @param absolutePath - normalized absolute dataset path.
271
+ * @param config - resolved config.
272
+ * @param signal - optional abort signal.
273
+ * @returns the parsed table.
274
+ */
275
+ export async function loadTable(absolutePath, config, signal) {
276
+ const text = await readDatasetText(absolutePath, config, signal);
277
+ const ext = path.extname(absolutePath).toLowerCase();
278
+ throwIfAborted(signal);
279
+ if (ext === '.csv')
280
+ return parseDelimited(text, ',', config, signal);
281
+ if (ext === '.tsv')
282
+ return parseDelimited(text, '\t', config, signal);
283
+ return parseJsonTable(text, ext, config, signal);
284
+ }
285
+ /**
286
+ * Load a dataset as a citation-checkable document root: CSV/TSV become
287
+ * `{ columns, rows }` (so `rows[3].nav` resolves), JSON is the parsed value,
288
+ * JSONL is the array of parsed lines.
289
+ * @param absolutePath - normalized absolute dataset path.
290
+ * @param config - resolved config.
291
+ * @param signal - optional abort signal.
292
+ * @returns the document root.
293
+ */
294
+ export async function loadDocument(absolutePath, config, signal) {
295
+ const ext = path.extname(absolutePath).toLowerCase();
296
+ if (ext === '.json') {
297
+ const text = await readDatasetText(absolutePath, config, signal);
298
+ return { kind: 'json', value: parseJsonDocument(text) };
299
+ }
300
+ if (ext === '.jsonl') {
301
+ const text = await readDatasetText(absolutePath, config, signal);
302
+ return { kind: 'json', value: parseJsonLines(text) };
303
+ }
304
+ const table = await loadTable(absolutePath, config, signal);
305
+ return { kind: 'table', columns: table.columns, rows: table.rows };
306
+ }
307
+ /**
308
+ * Deterministic systematic sample: every `ceil(total / count)`-th row from
309
+ * index 0. No clock, no RNG — the same input always yields the same sample.
310
+ * @param rows - full row list.
311
+ * @param count - requested sample size.
312
+ * @returns the sampled rows (identity when `count >= rows.length`).
313
+ */
314
+ export function sampleRows(rows, count) {
315
+ if (!Number.isSafeInteger(count) || count <= 0) {
316
+ throw new DatasetError('malformed', `sample must be a positive integer, got ${String(count)}`);
317
+ }
318
+ if (count >= rows.length)
319
+ return rows;
320
+ const stride = Math.ceil(rows.length / count);
321
+ const out = [];
322
+ for (let i = 0; i < rows.length; i += stride) {
323
+ out.push(rows[i]);
324
+ }
325
+ return out;
326
+ }
327
+ /**
328
+ * Missingness: `null`, `undefined`, or a string empty after trimming. A
329
+ * whitespace-only cell counts as missing even before any `trim` clean rule.
330
+ * @param cell - the cell to test (`undefined` when the column is absent).
331
+ * @returns whether the cell is missing.
332
+ */
333
+ export function isMissing(cell) {
334
+ if (cell === null || cell === undefined)
335
+ return true;
336
+ if (typeof cell === 'string')
337
+ return cell.trim() === '';
338
+ return false;
339
+ }
340
+ /** Strict full-string numeric parse (no thousands separators, no unit suffixes). */
341
+ export function parseNumeric(cell) {
342
+ if (typeof cell === 'number')
343
+ return Number.isFinite(cell) ? cell : undefined;
344
+ if (typeof cell !== 'string')
345
+ return undefined;
346
+ const text = cell.trim();
347
+ if (text === '')
348
+ return undefined;
349
+ if (!/^[+-]?(?:\d+\.?\d*|\.\d+)(?:[eE][+-]?\d+)?$/u.test(text))
350
+ return undefined;
351
+ const value = Number(text);
352
+ return Number.isFinite(value) ? value : undefined;
353
+ }
354
+ const DATE_PATTERNS = [
355
+ /^(\d{4})-(\d{1,2})-(\d{1,2})$/u,
356
+ /^(\d{4})\/(\d{1,2})\/(\d{1,2})$/u,
357
+ /^(\d{4})-(\d{1,2})-(\d{1,2})[ T](\d{1,2}):(\d{2})(?::(\d{2}))?(?:\.\d+)?(?:Z|[+-]\d{2}:?\d{2})?$/u,
358
+ ];
359
+ /**
360
+ * Deterministic date parse to epoch milliseconds. Accepts `YYYY-MM-DD`,
361
+ * `YYYY/MM/DD`, and ISO-like datetimes (date-only forms read as UTC midnight).
362
+ * Calendar-invalid dates (e.g. 2025-13-40) reject. Returns `undefined` when
363
+ * the cell is not a recognized date.
364
+ * @param cell - the cell to parse (`undefined` when the column is absent).
365
+ * @returns epoch milliseconds, or `undefined`.
366
+ */
367
+ export function parseDate(cell) {
368
+ if (typeof cell !== 'string')
369
+ return undefined;
370
+ const text = cell.trim();
371
+ for (const pattern of DATE_PATTERNS) {
372
+ const match = pattern.exec(text);
373
+ if (match === null)
374
+ continue;
375
+ const year = Number(match[1]);
376
+ const month = Number(match[2]);
377
+ const day = Number(match[3]);
378
+ const hour = Number(match[4] ?? 0);
379
+ const minute = Number(match[5] ?? 0);
380
+ const second = Number(match[6] ?? 0);
381
+ if (month < 1 || month > 12 || day < 1 || day > 31 || hour > 23 || minute > 59 || second > 59)
382
+ return undefined;
383
+ const epoch = Date.UTC(year, month - 1, day, hour, minute, second);
384
+ const check = new Date(epoch);
385
+ if (check.getUTCMonth() !== month - 1 || check.getUTCDate() !== day)
386
+ return undefined;
387
+ return epoch;
388
+ }
389
+ return undefined;
390
+ }
391
+ /** Boolean parse: true/false/yes/no/1/0, case-insensitive. */
392
+ export function parseBoolean(cell) {
393
+ if (typeof cell === 'boolean')
394
+ return cell;
395
+ if (typeof cell !== 'string')
396
+ return undefined;
397
+ const text = cell.trim().toLowerCase();
398
+ if (text === 'true' || text === 'yes' || text === '1')
399
+ return true;
400
+ if (text === 'false' || text === 'no' || text === '0')
401
+ return false;
402
+ return undefined;
403
+ }
404
+ //# sourceMappingURL=dataset.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dataset.js","sourceRoot":"","sources":["../../src/dataset.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,73 @@
1
+ /**
2
+ * The `data-quality/*` session-event vocabulary and its adaptive append gate.
3
+ * The vocabulary is declared on `SessionEventMap` for type-checked payloads;
4
+ * the runtime gate decides per host whether appending is safe:
5
+ *
6
+ * - Hosts whose known-type set already covers the vocabulary (a future harness
7
+ * that adopts these events) append plainly.
8
+ * - Hosts with an `ignorable` append option (the master-build
9
+ * `Session.append(type, data, { ignorable: true })` contract) append with the
10
+ * marker, so builds that do not know the type skip the event on restore.
11
+ * - 0.1.0-rc.6 hosts have neither a registration surface nor the `ignorable`
12
+ * append flag; appending an unknown type there would make the persistence
13
+ * coordinator refuse the session log on restore, so the append is skipped
14
+ * and the storage-domain report remains the durable copy.
15
+ * @module dsh-data-quality/events
16
+ */
17
+ import { type Session } from '@deepseek-ai/dsh-session';
18
+ /** Payload shared by every `data-quality/*` session event. */
19
+ export interface DataQualityEventData {
20
+ /** Which run produced the event. */
21
+ readonly kind: 'profile' | 'clean' | 'verify';
22
+ /** Workspace-relative dataset path as the caller gave it. */
23
+ readonly dataset: string;
24
+ /** Storage-domain key of the full persisted report, when persistence is on. */
25
+ readonly reportKey?: string;
26
+ /** Compact run summary (small integers/booleans only). */
27
+ readonly summary: {
28
+ readonly rows: number;
29
+ readonly columns?: number;
30
+ readonly rules?: number;
31
+ readonly failedRules?: number;
32
+ readonly passed?: boolean;
33
+ };
34
+ }
35
+ declare module '@deepseek-ai/dsh-session/types' {
36
+ interface SessionEventMap {
37
+ /**
38
+ * A `data_profile` run completed.
39
+ * @mode emit
40
+ * @param data - run kind, dataset, optional report key, and compact summary.
41
+ */
42
+ 'data-quality/profile': DataQualityEventData;
43
+ /**
44
+ * A `data_clean` run completed.
45
+ * @mode emit
46
+ * @param data - run kind, dataset, optional report key, and compact summary.
47
+ */
48
+ 'data-quality/clean': DataQualityEventData;
49
+ /**
50
+ * A `data_verify` run completed.
51
+ * @mode emit
52
+ * @param data - run kind, dataset, optional report key, and compact summary.
53
+ */
54
+ 'data-quality/verify': DataQualityEventData;
55
+ }
56
+ }
57
+ /** The event type vocabulary this plugin appends. */
58
+ export declare const DATA_QUALITY_EVENT_TYPES: readonly ["data-quality/profile", "data-quality/clean", "data-quality/verify"];
59
+ /** Union of the event types this plugin appends. */
60
+ export type DataQualityEventType = (typeof DATA_QUALITY_EVENT_TYPES)[number];
61
+ /**
62
+ * Append one `data-quality/*` event when the host can carry it safely; skip
63
+ * silently otherwise (the storage-domain report is always the durable copy).
64
+ * The `ignorable` probe reads the UNBOUND method's source (a `.bind()` result
65
+ * reports `[native code]`): the rc.6 build contains no `ignorable` handling
66
+ * while the master build references the flag by name; property names survive
67
+ * minification, so the probe fails safe (skips) rather than corrupting a log.
68
+ * @param session - the calling session.
69
+ * @param type - the event type.
70
+ * @param data - the payload.
71
+ */
72
+ export declare function appendDataQualityEvent(session: Session, type: DataQualityEventType, data: DataQualityEventData): void;
73
+ //# sourceMappingURL=events.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"events.d.ts","sourceRoot":"","sources":["../../src/events.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAA6B,KAAK,OAAO,EAAE,MAAM,0BAA0B,CAAA;AAElF,8DAA8D;AAC9D,MAAM,WAAW,oBAAoB;IACnC,oCAAoC;IACpC,QAAQ,CAAC,IAAI,EAAE,SAAS,GAAG,OAAO,GAAG,QAAQ,CAAA;IAC7C,6DAA6D;IAC7D,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,+EAA+E;IAC/E,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAA;IAC3B,0DAA0D;IAC1D,QAAQ,CAAC,OAAO,EAAE;QAChB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;QACrB,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAA;QACzB,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;QACvB,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;QAC7B,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAA;KAC1B,CAAA;CACF;AAED,OAAO,QAAQ,gCAAgC,CAAC;IAC9C,UAAU,eAAe;QACvB;;;;WAIG;QACH,sBAAsB,EAAE,oBAAoB,CAAA;QAC5C;;;;WAIG;QACH,oBAAoB,EAAE,oBAAoB,CAAA;QAC1C;;;;WAIG;QACH,qBAAqB,EAAE,oBAAoB,CAAA;KAC5C;CACF;AAED,qDAAqD;AACrD,eAAO,MAAM,wBAAwB,gFAAiF,CAAA;AAEtH,oDAAoD;AACpD,MAAM,MAAM,oBAAoB,GAAG,CAAC,OAAO,wBAAwB,CAAC,CAAC,MAAM,CAAC,CAAA;AAK5E;;;;;;;;;;GAUG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,oBAAoB,EAAE,IAAI,EAAE,oBAAoB,GAAG,IAAI,CASrH"}