@toiroakr/lines-db 0.3.0 → 0.4.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.
- package/CHANGELOG.md +8 -0
- package/bin/cli.js +144 -134
- package/dist/index.cjs +66 -56
- package/dist/index.d.cts +11 -10
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.ts +11 -10
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +66 -56
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/database.ts +32 -15
- package/src/schema.ts +6 -6
- package/src/types.ts +2 -2
- package/src/validator.ts +70 -72
package/src/schema.ts
CHANGED
|
@@ -5,9 +5,9 @@ import type { StandardSchema, Table, ForeignKeyDefinition, IndexDefinition } fro
|
|
|
5
5
|
*/
|
|
6
6
|
export interface SchemaOptions {
|
|
7
7
|
/**
|
|
8
|
-
* Primary key
|
|
8
|
+
* Primary key column
|
|
9
9
|
*/
|
|
10
|
-
primaryKey?: string
|
|
10
|
+
primaryKey?: string;
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
13
|
* Foreign key constraints
|
|
@@ -39,9 +39,9 @@ export interface BiDirectionalSchema<Input extends Table = Table, Output extends
|
|
|
39
39
|
backward?: (output: Output) => Input;
|
|
40
40
|
|
|
41
41
|
/**
|
|
42
|
-
* Primary key
|
|
42
|
+
* Primary key column
|
|
43
43
|
*/
|
|
44
|
-
primaryKey?: string
|
|
44
|
+
primaryKey?: string;
|
|
45
45
|
|
|
46
46
|
/**
|
|
47
47
|
* Foreign key constraints
|
|
@@ -79,9 +79,9 @@ export interface BiDirectionalSchema<Input extends Table = Table, Output extends
|
|
|
79
79
|
* const schema = defineSchema(
|
|
80
80
|
* v.object({ id: v.number(), customerId: v.number() }),
|
|
81
81
|
* {
|
|
82
|
-
* primaryKey:
|
|
82
|
+
* primaryKey: 'id',
|
|
83
83
|
* foreignKeys: [
|
|
84
|
-
* {
|
|
84
|
+
* { column: 'customerId', references: { table: 'users', column: 'id' } }
|
|
85
85
|
* ]
|
|
86
86
|
* }
|
|
87
87
|
* );
|
package/src/types.ts
CHANGED
|
@@ -18,10 +18,10 @@ export type InferInput<T> = T extends StandardSchemaV1<infer I, unknown> ? I : n
|
|
|
18
18
|
export type InferOutput<T> = T extends StandardSchemaV1<unknown, infer O> ? O : never;
|
|
19
19
|
|
|
20
20
|
export interface ForeignKeyDefinition {
|
|
21
|
-
|
|
21
|
+
column: string;
|
|
22
22
|
references: {
|
|
23
23
|
table: string;
|
|
24
|
-
|
|
24
|
+
column: string;
|
|
25
25
|
};
|
|
26
26
|
onDelete?: 'CASCADE' | 'SET NULL' | 'RESTRICT' | 'NO ACTION';
|
|
27
27
|
onUpdate?: 'CASCADE' | 'SET NULL' | 'RESTRICT' | 'NO ACTION';
|
package/src/validator.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { readdir, stat } from 'node:fs/promises';
|
|
2
|
-
import { join, basename } from 'node:path';
|
|
2
|
+
import { join, basename, dirname } from 'node:path';
|
|
3
3
|
import { JsonlReader } from './jsonl-reader.js';
|
|
4
4
|
import { SchemaLoader } from './schema-loader.js';
|
|
5
|
-
import
|
|
6
|
-
import type {
|
|
5
|
+
import { LinesDB } from './database.js';
|
|
6
|
+
import type { StandardSchemaIssue } from './types.js';
|
|
7
7
|
|
|
8
8
|
export interface ValidationResult {
|
|
9
9
|
valid: boolean;
|
|
@@ -91,10 +91,10 @@ export class Validator {
|
|
|
91
91
|
allWarnings.push(...result.warnings);
|
|
92
92
|
}
|
|
93
93
|
|
|
94
|
-
// Then, validate
|
|
95
|
-
if (filesWithSchema.length > 0) {
|
|
96
|
-
const
|
|
97
|
-
allErrors.push(...
|
|
94
|
+
// Then, validate by actually loading into database
|
|
95
|
+
if (filesWithSchema.length > 0 && allErrors.length === 0) {
|
|
96
|
+
const dbErrors = await this.validateWithDatabase(dirPath, filesWithSchema);
|
|
97
|
+
allErrors.push(...dbErrors);
|
|
98
98
|
}
|
|
99
99
|
|
|
100
100
|
return {
|
|
@@ -105,80 +105,71 @@ export class Validator {
|
|
|
105
105
|
}
|
|
106
106
|
|
|
107
107
|
/**
|
|
108
|
-
* Validate
|
|
108
|
+
* Validate by loading data into an actual database
|
|
109
|
+
* This catches constraint violations (unique, primary key, foreign key, etc.)
|
|
109
110
|
*/
|
|
110
|
-
private async
|
|
111
|
+
private async validateWithDatabase(
|
|
111
112
|
dirPath: string,
|
|
112
113
|
jsonlFiles: string[],
|
|
113
114
|
): Promise<ValidationErrorDetail[]> {
|
|
114
115
|
const errors: ValidationErrorDetail[] = [];
|
|
115
116
|
|
|
116
|
-
//
|
|
117
|
-
const
|
|
118
|
-
const
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
tableData.set(tableName, data);
|
|
126
|
-
tableSchemas.set(tableName, schema as BiDirectionalSchema);
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
// Check foreign keys for each table
|
|
130
|
-
for (const file of jsonlFiles) {
|
|
131
|
-
const tableName = basename(file, '.jsonl');
|
|
132
|
-
const schema = tableSchemas.get(tableName);
|
|
133
|
-
const data = tableData.get(tableName);
|
|
134
|
-
|
|
135
|
-
if (!schema || !data || !schema.foreignKeys) {
|
|
136
|
-
continue;
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
// Check each foreign key constraint
|
|
140
|
-
for (const fk of schema.foreignKeys) {
|
|
141
|
-
const referencedTable = fk.references.table;
|
|
142
|
-
const referencedData = tableData.get(referencedTable);
|
|
143
|
-
|
|
144
|
-
if (!referencedData) {
|
|
145
|
-
// Referenced table not found - skip validation
|
|
146
|
-
continue;
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
// Build index of referenced values for fast lookup
|
|
150
|
-
const referencedValues = new Set<string>();
|
|
151
|
-
for (const refRow of referencedData) {
|
|
152
|
-
// Build composite key from referenced columns
|
|
153
|
-
const keyValues = fk.references.columns.map((col) => refRow[col]);
|
|
154
|
-
const compositeKey = JSON.stringify(keyValues);
|
|
155
|
-
referencedValues.add(compositeKey);
|
|
156
|
-
}
|
|
117
|
+
// Capture console.warn messages
|
|
118
|
+
const warnMessages: string[] = [];
|
|
119
|
+
const originalWarn = console.warn;
|
|
120
|
+
console.warn = (...args: unknown[]) => {
|
|
121
|
+
const message = args.map((arg) => String(arg)).join(' ');
|
|
122
|
+
warnMessages.push(message);
|
|
123
|
+
// Still output to console for debugging
|
|
124
|
+
originalWarn(...args);
|
|
125
|
+
};
|
|
157
126
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
127
|
+
try {
|
|
128
|
+
// Try to initialize database with the data directory
|
|
129
|
+
const db = LinesDB.create({ dataDir: dirPath });
|
|
130
|
+
await db.initialize();
|
|
131
|
+
await db.close();
|
|
132
|
+
|
|
133
|
+
// Check if there were any loading errors
|
|
134
|
+
for (const message of warnMessages) {
|
|
135
|
+
if (message.includes('Failed to load table')) {
|
|
136
|
+
// Extract table name from message
|
|
137
|
+
const tableNameMatch = message.match(/Failed to load table '([^']+)'/);
|
|
138
|
+
const tableName = tableNameMatch ? tableNameMatch[1] : 'unknown';
|
|
139
|
+
|
|
140
|
+
const file = jsonlFiles.find((f) => basename(f, '.jsonl') === tableName);
|
|
141
|
+
|
|
142
|
+
errors.push({
|
|
143
|
+
file: file || `${dirPath}/${tableName}.jsonl`,
|
|
144
|
+
tableName,
|
|
145
|
+
rowIndex: 0,
|
|
146
|
+
issues: [
|
|
147
|
+
{
|
|
148
|
+
message: message.replace(/^Warning:\s*/, ''),
|
|
149
|
+
path: [],
|
|
177
150
|
},
|
|
178
|
-
|
|
179
|
-
|
|
151
|
+
],
|
|
152
|
+
type: 'schema',
|
|
153
|
+
});
|
|
180
154
|
}
|
|
181
155
|
}
|
|
156
|
+
} catch (error) {
|
|
157
|
+
// If initialization itself fails, report it
|
|
158
|
+
errors.push({
|
|
159
|
+
file: dirPath,
|
|
160
|
+
tableName: 'database',
|
|
161
|
+
rowIndex: 0,
|
|
162
|
+
issues: [
|
|
163
|
+
{
|
|
164
|
+
message: `Database initialization failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
165
|
+
path: [],
|
|
166
|
+
},
|
|
167
|
+
],
|
|
168
|
+
type: 'schema',
|
|
169
|
+
});
|
|
170
|
+
} finally {
|
|
171
|
+
// Restore console.warn
|
|
172
|
+
console.warn = originalWarn;
|
|
182
173
|
}
|
|
183
174
|
|
|
184
175
|
return errors;
|
|
@@ -196,7 +187,7 @@ export class Validator {
|
|
|
196
187
|
|
|
197
188
|
const errors: ValidationErrorDetail[] = [];
|
|
198
189
|
|
|
199
|
-
// Validate each row
|
|
190
|
+
// Validate each row with schema
|
|
200
191
|
for (let i = 0; i < data.length; i++) {
|
|
201
192
|
const row = data[i];
|
|
202
193
|
const result = schema['~standard'].validate(row);
|
|
@@ -217,6 +208,13 @@ export class Validator {
|
|
|
217
208
|
}
|
|
218
209
|
}
|
|
219
210
|
|
|
211
|
+
// If schema validation passed, validate with database
|
|
212
|
+
if (errors.length === 0) {
|
|
213
|
+
const dirPath = dirname(filePath);
|
|
214
|
+
const dbErrors = await this.validateWithDatabase(dirPath, [filePath]);
|
|
215
|
+
errors.push(...dbErrors);
|
|
216
|
+
}
|
|
217
|
+
|
|
220
218
|
return {
|
|
221
219
|
valid: errors.length === 0,
|
|
222
220
|
errors,
|