@toiroakr/lines-db 0.1.0 → 0.2.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.
@@ -1,5 +1,5 @@
1
1
  /**
2
- * SQLite adapter that works across Node.js, Bun, and Deno
2
+ * SQLite adapter for Node.js
3
3
  */
4
4
 
5
5
  import { RUNTIME } from './runtime.js';
@@ -20,51 +20,16 @@ export interface SQLiteStatement {
20
20
  }
21
21
 
22
22
  /**
23
- * Create a SQLite database instance based on the runtime environment
23
+ * Create a SQLite database instance for Node.js
24
24
  */
25
25
  export function createDatabase(path: string = ':memory:'): SQLiteDatabase {
26
- if (RUNTIME === 'bun') {
27
- return createBunDatabase(path);
28
- } else if (RUNTIME === 'node' || RUNTIME === 'deno') {
26
+ if (RUNTIME === 'node') {
29
27
  return createNodeDatabase(path);
30
28
  } else {
31
29
  throw new Error(`Unsupported runtime: ${RUNTIME}`);
32
30
  }
33
31
  }
34
32
 
35
- /**
36
- * Create a Bun SQLite database
37
- */
38
- function createBunDatabase(path: string): SQLiteDatabase {
39
- // Dynamic import for Bun
40
- // eslint-disable-next-line @typescript-eslint/no-require-imports
41
- const { Database } = require('bun:sqlite');
42
- const db = new Database(path);
43
-
44
- return {
45
- prepare(sql: string): SQLiteStatement {
46
- const stmt = db.prepare(sql);
47
- return {
48
- run(...params: any[]) {
49
- return stmt.run(...params);
50
- },
51
- get(...params: any[]) {
52
- return stmt.get(...params);
53
- },
54
- all(...params: any[]) {
55
- return stmt.all(...params);
56
- },
57
- };
58
- },
59
- exec(sql: string): void {
60
- db.exec(sql);
61
- },
62
- close(): void {
63
- db.close();
64
- },
65
- };
66
- }
67
-
68
33
  /**
69
34
  * Create a Node.js SQLite database
70
35
  */
@@ -71,9 +71,7 @@ export class TypeGenerator {
71
71
  const schemaFilePath = join(this.dataDirPath, schemaFileName);
72
72
 
73
73
  // Check if schema file exists
74
- const hasSchema = entries.some(
75
- e => e.isFile() && e.name === schemaFileName
76
- );
74
+ const hasSchema = entries.some((e) => e.isFile() && e.name === schemaFileName);
77
75
 
78
76
  tables.push({
79
77
  tableName,
@@ -137,7 +135,7 @@ export class TypeGenerator {
137
135
  return `// Auto-generated by lines-db
138
136
  // Do not edit this file manually
139
137
 
140
- ${importSection}import type { DatabaseConfig${inferOutputImport} } from 'lines-db';
138
+ ${importSection}import type { DatabaseConfig${inferOutputImport} } from '@toiroakr/lines-db';
141
139
  import { fileURLToPath } from 'node:url';
142
140
  import { dirname } from 'node:path';
143
141
 
@@ -46,6 +46,7 @@ describe('Validator', () => {
46
46
 
47
47
  expect(result.valid).toBe(true);
48
48
  expect(result.errors).toHaveLength(0);
49
+ expect(result.warnings).toHaveLength(0);
49
50
  });
50
51
 
51
52
  it('should detect validation errors', async () => {
@@ -77,7 +78,7 @@ describe('Validator', () => {
77
78
 
78
79
  expect(result.valid).toBe(false);
79
80
  expect(result.errors).toHaveLength(1);
80
- expect(result.errors[0].rowIndex).toBe(2);
81
+ expect(result.errors[0].rowIndex).toBe(1); // 0-indexed: line 2 is index 1
81
82
  expect(result.errors[0].tableName).toBe('users');
82
83
  expect(result.errors[0].issues).toHaveLength(1);
83
84
  });
@@ -119,8 +120,8 @@ describe('Validator', () => {
119
120
 
120
121
  expect(result.valid).toBe(false);
121
122
  expect(result.errors).toHaveLength(2);
122
- expect(result.errors[0].rowIndex).toBe(1);
123
- expect(result.errors[1].rowIndex).toBe(2);
123
+ expect(result.errors[0].rowIndex).toBe(0); // 0-indexed: line 1 is index 0
124
+ expect(result.errors[1].rowIndex).toBe(1); // 0-indexed: line 2 is index 1
124
125
  });
125
126
 
126
127
  it('should return 0-based rowIndex (first line = index 0)', async () => {
@@ -128,10 +129,7 @@ describe('Validator', () => {
128
129
  const schemaPath = join(testDir, 'users.schema.ts');
129
130
 
130
131
  // Line 1: valid, Line 2: invalid (no name), Line 3: invalid (no name)
131
- await writeFile(
132
- jsonlPath,
133
- '{"id":1,"name":"Alice"}\n{"id":2}\n{"id":3}\n',
134
- );
132
+ await writeFile(jsonlPath, '{"id":1,"name":"Alice"}\n{"id":2}\n{"id":3}\n');
135
133
  await writeFile(
136
134
  schemaPath,
137
135
  `
@@ -239,6 +237,7 @@ describe('Validator', () => {
239
237
 
240
238
  expect(result.valid).toBe(true);
241
239
  expect(result.errors).toHaveLength(0);
240
+ expect(result.warnings).toHaveLength(0);
242
241
  });
243
242
 
244
243
  it('should collect errors from multiple files', async () => {
@@ -295,6 +294,37 @@ describe('Validator', () => {
295
294
 
296
295
  await expect(validator.validate()).rejects.toThrow('No JSONL files found');
297
296
  });
297
+
298
+ it('should skip validation and warn for JSONL files without schema', async () => {
299
+ const usersPath = join(testDir, 'users.jsonl');
300
+ const productsPath = join(testDir, 'products.jsonl');
301
+ const usersSchemaPath = join(testDir, 'users.schema.ts');
302
+
303
+ // Only users.jsonl has a schema
304
+ await writeFile(usersPath, '{"id":1,"name":"Alice"}\n');
305
+ await writeFile(productsPath, '{"id":1,"name":"Product"}\n');
306
+ await writeFile(
307
+ usersSchemaPath,
308
+ `
309
+ export const schema = {
310
+ '~standard': {
311
+ version: 1,
312
+ vendor: 'test',
313
+ validate: (data) => ({ value: data, issues: [] })
314
+ }
315
+ };
316
+ `,
317
+ );
318
+
319
+ const validator = new Validator({ path: testDir });
320
+ const result = await validator.validate();
321
+
322
+ expect(result.valid).toBe(true);
323
+ expect(result.errors).toHaveLength(0);
324
+ expect(result.warnings).toHaveLength(1);
325
+ expect(result.warnings[0]).toContain('products');
326
+ expect(result.warnings[0]).toContain('schema file not found');
327
+ });
298
328
  });
299
329
 
300
330
  describe('error handling', () => {
package/src/validator.ts CHANGED
@@ -8,6 +8,7 @@ import type { BiDirectionalSchema } from './schema.js';
8
8
  export interface ValidationResult {
9
9
  valid: boolean;
10
10
  errors: ValidationErrorDetail[];
11
+ warnings: string[];
11
12
  }
12
13
 
13
14
  export interface ValidationErrorDetail {
@@ -69,20 +70,37 @@ export class Validator {
69
70
  }
70
71
 
71
72
  const allErrors: ValidationErrorDetail[] = [];
73
+ const allWarnings: string[] = [];
74
+ const filesWithSchema: string[] = [];
72
75
 
73
- // First, validate schema for each file
76
+ // Filter files with schema and collect warnings for files without schema
74
77
  for (const file of jsonlFiles) {
78
+ const hasSchema = await SchemaLoader.hasSchema(file);
79
+ if (hasSchema) {
80
+ filesWithSchema.push(file);
81
+ } else {
82
+ const tableName = basename(file, '.jsonl');
83
+ allWarnings.push(`Skipping validation for '${tableName}': schema file not found`);
84
+ }
85
+ }
86
+
87
+ // Validate schema for each file with schema
88
+ for (const file of filesWithSchema) {
75
89
  const result = await this.validateFile(file);
76
90
  allErrors.push(...result.errors);
91
+ allWarnings.push(...result.warnings);
77
92
  }
78
93
 
79
- // Then, validate foreign keys across all tables
80
- const fkErrors = await this.validateForeignKeys(dirPath, jsonlFiles);
81
- allErrors.push(...fkErrors);
94
+ // Then, validate foreign keys across all tables (only for files with schema)
95
+ if (filesWithSchema.length > 0) {
96
+ const fkErrors = await this.validateForeignKeys(dirPath, filesWithSchema);
97
+ allErrors.push(...fkErrors);
98
+ }
82
99
 
83
100
  return {
84
101
  valid: allErrors.length === 0,
85
102
  errors: allErrors,
103
+ warnings: allWarnings,
86
104
  };
87
105
  }
88
106
 
@@ -202,6 +220,7 @@ export class Validator {
202
220
  return {
203
221
  valid: errors.length === 0,
204
222
  errors,
223
+ warnings: [],
205
224
  };
206
225
  }
207
226
  }
package/LICENSE DELETED
@@ -1,21 +0,0 @@
1
- MIT License
2
-
3
- Copyright (c) 2025 Akira HIGUCHI
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.