sqlstack 1.0.7 → 1.0.8
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/dist/cjs/adapters/index.d.ts.map +1 -0
- package/dist/cjs/adapters/index.js.map +1 -0
- package/dist/cjs/adapters/mysql.d.ts.map +1 -0
- package/dist/cjs/adapters/mysql.js.map +1 -0
- package/dist/cjs/adapters/pg.d.ts.map +1 -0
- package/dist/cjs/adapters/pg.js.map +1 -0
- package/dist/cjs/adapters/sqlite.d.ts.map +1 -0
- package/dist/cjs/adapters/sqlite.js.map +1 -0
- package/dist/cjs/adapters/test.d.ts.map +1 -0
- package/dist/cjs/adapters/test.js.map +1 -0
- package/dist/cjs/binders/sqlBinder.d.ts.map +1 -0
- package/dist/cjs/binders/sqlBinder.js.map +1 -0
- package/dist/cjs/core/errors.d.ts.map +1 -0
- package/dist/cjs/core/errors.js.map +1 -0
- package/dist/cjs/core/fileResolution.d.ts.map +1 -0
- package/dist/cjs/core/fileResolution.js.map +1 -0
- package/dist/cjs/core/metadata.d.ts.map +1 -0
- package/dist/cjs/core/metadata.js.map +1 -0
- package/dist/cjs/core/validator.d.ts.map +1 -0
- package/dist/cjs/core/validator.js.map +1 -0
- package/dist/cjs/decorators/defaults.d.ts.map +1 -0
- package/dist/cjs/decorators/defaults.js.map +1 -0
- package/dist/cjs/decorators/page.d.ts.map +1 -0
- package/dist/cjs/decorators/page.js.map +1 -0
- package/dist/cjs/decorators/query.d.ts.map +1 -0
- package/dist/cjs/decorators/query.js.map +1 -0
- package/dist/cjs/decorators/queryBinder.d.ts.map +1 -0
- package/dist/cjs/decorators/queryBinder.js.map +1 -0
- package/dist/cjs/decorators/resultShape.d.ts.map +1 -0
- package/dist/cjs/decorators/resultShape.js.map +1 -0
- package/dist/cjs/decorators/transform.d.ts.map +1 -0
- package/dist/cjs/decorators/transform.js.map +1 -0
- package/dist/cjs/decorators/validateResult.d.ts.map +1 -0
- package/dist/cjs/decorators/validateResult.js.map +1 -0
- package/dist/cjs/index.d.ts.map +1 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/cjs/registry.d.ts.map +1 -0
- package/dist/cjs/registry.js.map +1 -0
- package/dist/cjs/utils/dto.d.ts.map +1 -0
- package/dist/cjs/utils/dto.js.map +1 -0
- package/dist/esm/adapters/index.js +4 -0
- package/dist/esm/adapters/index.js.map +1 -0
- package/dist/esm/adapters/mysql.js +36 -0
- package/dist/esm/adapters/mysql.js.map +1 -0
- package/dist/esm/adapters/pg.js +67 -0
- package/dist/esm/adapters/pg.js.map +1 -0
- package/dist/esm/adapters/sqlite.js +141 -0
- package/dist/esm/adapters/sqlite.js.map +1 -0
- package/dist/esm/adapters/test.js +47 -0
- package/dist/esm/adapters/test.js.map +1 -0
- package/dist/esm/binders/sqlBinder.js +182 -0
- package/dist/esm/binders/sqlBinder.js.map +1 -0
- package/dist/esm/core/errors.js +25 -0
- package/dist/esm/core/errors.js.map +1 -0
- package/dist/esm/core/fileResolution.js +74 -0
- package/dist/esm/core/fileResolution.js.map +1 -0
- package/dist/esm/core/metadata.js +24 -0
- package/dist/esm/core/metadata.js.map +1 -0
- package/dist/esm/core/validator.js +57 -0
- package/dist/esm/core/validator.js.map +1 -0
- package/dist/esm/decorators/defaults.js +7 -0
- package/dist/esm/decorators/defaults.js.map +1 -0
- package/dist/esm/decorators/page.js +8 -0
- package/dist/esm/decorators/page.js.map +1 -0
- package/dist/esm/decorators/query.js +278 -0
- package/dist/esm/decorators/query.js.map +1 -0
- package/dist/esm/decorators/queryBinder.js +7 -0
- package/dist/esm/decorators/queryBinder.js.map +1 -0
- package/dist/esm/decorators/resultShape.js +14 -0
- package/dist/esm/decorators/resultShape.js.map +1 -0
- package/dist/esm/decorators/transform.js +8 -0
- package/dist/esm/decorators/transform.js.map +1 -0
- package/dist/esm/decorators/validateResult.js +8 -0
- package/dist/esm/decorators/validateResult.js.map +1 -0
- package/dist/esm/index.js +14 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/package.json +3 -0
- package/dist/esm/registry.js +26 -0
- package/dist/esm/registry.js.map +1 -0
- package/dist/esm/utils/dto.js +129 -0
- package/dist/esm/utils/dto.js.map +1 -0
- package/package.json +18 -16
- package/dist/adapters/index.d.ts.map +0 -1
- package/dist/adapters/index.js.map +0 -1
- package/dist/adapters/mysql.d.ts.map +0 -1
- package/dist/adapters/mysql.js.map +0 -1
- package/dist/adapters/pg.d.ts.map +0 -1
- package/dist/adapters/pg.js.map +0 -1
- package/dist/adapters/sqlite.d.ts.map +0 -1
- package/dist/adapters/sqlite.js.map +0 -1
- package/dist/adapters/test.d.ts.map +0 -1
- package/dist/adapters/test.js.map +0 -1
- package/dist/binders/sqlBinder.d.ts.map +0 -1
- package/dist/binders/sqlBinder.js.map +0 -1
- package/dist/core/errors.d.ts.map +0 -1
- package/dist/core/errors.js.map +0 -1
- package/dist/core/fileResolution.d.ts.map +0 -1
- package/dist/core/fileResolution.js.map +0 -1
- package/dist/core/metadata.d.ts.map +0 -1
- package/dist/core/metadata.js.map +0 -1
- package/dist/core/validator.d.ts.map +0 -1
- package/dist/core/validator.js.map +0 -1
- package/dist/decorators/defaults.d.ts.map +0 -1
- package/dist/decorators/defaults.js.map +0 -1
- package/dist/decorators/page.d.ts.map +0 -1
- package/dist/decorators/page.js.map +0 -1
- package/dist/decorators/query.d.ts.map +0 -1
- package/dist/decorators/query.js.map +0 -1
- package/dist/decorators/queryBinder.d.ts.map +0 -1
- package/dist/decorators/queryBinder.js.map +0 -1
- package/dist/decorators/resultShape.d.ts.map +0 -1
- package/dist/decorators/resultShape.js.map +0 -1
- package/dist/decorators/transform.d.ts.map +0 -1
- package/dist/decorators/transform.js.map +0 -1
- package/dist/decorators/validateResult.d.ts.map +0 -1
- package/dist/decorators/validateResult.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/registry.d.ts.map +0 -1
- package/dist/registry.js.map +0 -1
- package/dist/utils/dto.d.ts.map +0 -1
- package/dist/utils/dto.js.map +0 -1
- /package/dist/{adapters → cjs/adapters}/index.d.ts +0 -0
- /package/dist/{adapters → cjs/adapters}/index.js +0 -0
- /package/dist/{adapters → cjs/adapters}/mysql.d.ts +0 -0
- /package/dist/{adapters → cjs/adapters}/mysql.js +0 -0
- /package/dist/{adapters → cjs/adapters}/pg.d.ts +0 -0
- /package/dist/{adapters → cjs/adapters}/pg.js +0 -0
- /package/dist/{adapters → cjs/adapters}/sqlite.d.ts +0 -0
- /package/dist/{adapters → cjs/adapters}/sqlite.js +0 -0
- /package/dist/{adapters → cjs/adapters}/test.d.ts +0 -0
- /package/dist/{adapters → cjs/adapters}/test.js +0 -0
- /package/dist/{binders → cjs/binders}/sqlBinder.d.ts +0 -0
- /package/dist/{binders → cjs/binders}/sqlBinder.js +0 -0
- /package/dist/{core → cjs/core}/errors.d.ts +0 -0
- /package/dist/{core → cjs/core}/errors.js +0 -0
- /package/dist/{core → cjs/core}/fileResolution.d.ts +0 -0
- /package/dist/{core → cjs/core}/fileResolution.js +0 -0
- /package/dist/{core → cjs/core}/metadata.d.ts +0 -0
- /package/dist/{core → cjs/core}/metadata.js +0 -0
- /package/dist/{core → cjs/core}/validator.d.ts +0 -0
- /package/dist/{core → cjs/core}/validator.js +0 -0
- /package/dist/{decorators → cjs/decorators}/defaults.d.ts +0 -0
- /package/dist/{decorators → cjs/decorators}/defaults.js +0 -0
- /package/dist/{decorators → cjs/decorators}/page.d.ts +0 -0
- /package/dist/{decorators → cjs/decorators}/page.js +0 -0
- /package/dist/{decorators → cjs/decorators}/query.d.ts +0 -0
- /package/dist/{decorators → cjs/decorators}/query.js +0 -0
- /package/dist/{decorators → cjs/decorators}/queryBinder.d.ts +0 -0
- /package/dist/{decorators → cjs/decorators}/queryBinder.js +0 -0
- /package/dist/{decorators → cjs/decorators}/resultShape.d.ts +0 -0
- /package/dist/{decorators → cjs/decorators}/resultShape.js +0 -0
- /package/dist/{decorators → cjs/decorators}/transform.d.ts +0 -0
- /package/dist/{decorators → cjs/decorators}/transform.js +0 -0
- /package/dist/{decorators → cjs/decorators}/validateResult.d.ts +0 -0
- /package/dist/{decorators → cjs/decorators}/validateResult.js +0 -0
- /package/dist/{index.d.ts → cjs/index.d.ts} +0 -0
- /package/dist/{index.js → cjs/index.js} +0 -0
- /package/dist/{registry.d.ts → cjs/registry.d.ts} +0 -0
- /package/dist/{registry.js → cjs/registry.js} +0 -0
- /package/dist/{utils → cjs/utils}/dto.d.ts +0 -0
- /package/dist/{utils → cjs/utils}/dto.js +0 -0
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
function stripLeadingComments(sql) {
|
|
2
|
+
let i = 0;
|
|
3
|
+
const n = sql.length;
|
|
4
|
+
if (sql.charCodeAt(0) === 0xfeff)
|
|
5
|
+
i = 1; // BOM
|
|
6
|
+
while (i < n) {
|
|
7
|
+
while (i < n && /\s/.test(sql[i]))
|
|
8
|
+
i++;
|
|
9
|
+
if (i + 1 < n && sql[i] === '-' && sql[i + 1] === '-') {
|
|
10
|
+
// -- line comment
|
|
11
|
+
i += 2;
|
|
12
|
+
while (i < n && sql[i] !== '\n')
|
|
13
|
+
i++;
|
|
14
|
+
continue;
|
|
15
|
+
}
|
|
16
|
+
if (i + 1 < n && sql[i] === '/' && sql[i + 1] === '*') {
|
|
17
|
+
// /* block comment */
|
|
18
|
+
i += 2;
|
|
19
|
+
const end = sql.indexOf('*/', i);
|
|
20
|
+
if (end === -1)
|
|
21
|
+
return '';
|
|
22
|
+
i = end + 2;
|
|
23
|
+
continue;
|
|
24
|
+
}
|
|
25
|
+
break;
|
|
26
|
+
}
|
|
27
|
+
return sql.slice(i);
|
|
28
|
+
}
|
|
29
|
+
export function createSqliteDb(filenameOrDb) {
|
|
30
|
+
// If a pre-created connection is provided
|
|
31
|
+
if (typeof filenameOrDb !== 'string' && filenameOrDb) {
|
|
32
|
+
const db = filenameOrDb;
|
|
33
|
+
// better-sqlite3 shape
|
|
34
|
+
if (typeof db.prepare === 'function') {
|
|
35
|
+
return {
|
|
36
|
+
dialect: 'sqlite',
|
|
37
|
+
async query(sql, params) {
|
|
38
|
+
const stmt = db.prepare(sql);
|
|
39
|
+
const text = stripLeadingComments(String(sql)).trimStart().toUpperCase();
|
|
40
|
+
if (text.startsWith('SELECT') || text.startsWith('WITH')) {
|
|
41
|
+
return stmt.all(...params);
|
|
42
|
+
}
|
|
43
|
+
const info = stmt.run(...params);
|
|
44
|
+
return { rowsAffected: Number(info?.changes ?? 0), lastInsertId: info?.lastInsertRowid };
|
|
45
|
+
},
|
|
46
|
+
conn: db,
|
|
47
|
+
// Do not close externally provided connection
|
|
48
|
+
close() { },
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
// sqlite3 shape
|
|
52
|
+
return {
|
|
53
|
+
dialect: 'sqlite',
|
|
54
|
+
async query(sql, params) {
|
|
55
|
+
const text = stripLeadingComments(String(sql)).trimStart().toUpperCase();
|
|
56
|
+
if (text.startsWith('SELECT') || text.startsWith('WITH')) {
|
|
57
|
+
return new Promise((resolve, reject) => {
|
|
58
|
+
db.all(sql, params, (err, rows) => {
|
|
59
|
+
if (err)
|
|
60
|
+
return reject(err);
|
|
61
|
+
resolve(rows || []);
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
return new Promise((resolve, reject) => {
|
|
66
|
+
// Use function callback to access `this` for metadata
|
|
67
|
+
db.run(sql, params, function (err) {
|
|
68
|
+
if (err)
|
|
69
|
+
return reject(err);
|
|
70
|
+
resolve({ rowsAffected: Number(this?.changes ?? 0), lastInsertId: this?.lastID });
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
},
|
|
74
|
+
conn: db,
|
|
75
|
+
// Do not close externally provided connection
|
|
76
|
+
close() { },
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
const filename = filenameOrDb;
|
|
80
|
+
// Prefer better-sqlite3
|
|
81
|
+
try {
|
|
82
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
83
|
+
const BetterSqlite3 = require('better-sqlite3');
|
|
84
|
+
const db = new BetterSqlite3(filename);
|
|
85
|
+
return {
|
|
86
|
+
dialect: 'sqlite',
|
|
87
|
+
async query(sql, params) {
|
|
88
|
+
const stmt = db.prepare(sql);
|
|
89
|
+
const text = stripLeadingComments(String(sql)).trimStart().toUpperCase();
|
|
90
|
+
if (text.startsWith('SELECT') || text.startsWith('WITH')) {
|
|
91
|
+
return stmt.all(...params);
|
|
92
|
+
}
|
|
93
|
+
const info = stmt.run(...params);
|
|
94
|
+
return { rowsAffected: Number(info?.changes ?? 0), lastInsertId: info?.lastInsertRowid };
|
|
95
|
+
},
|
|
96
|
+
conn: db,
|
|
97
|
+
close() {
|
|
98
|
+
db.close();
|
|
99
|
+
},
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
catch { }
|
|
103
|
+
// Fallback to sqlite3
|
|
104
|
+
try {
|
|
105
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
106
|
+
const sqlite3 = require('sqlite3');
|
|
107
|
+
const db = new sqlite3.Database(filename);
|
|
108
|
+
return {
|
|
109
|
+
dialect: 'sqlite',
|
|
110
|
+
async query(sql, params) {
|
|
111
|
+
const text = stripLeadingComments(String(sql)).trimStart().toUpperCase();
|
|
112
|
+
if (text.startsWith('SELECT') || text.startsWith('WITH')) {
|
|
113
|
+
return new Promise((resolve, reject) => {
|
|
114
|
+
db.all(sql, params, (err, rows) => {
|
|
115
|
+
if (err)
|
|
116
|
+
return reject(err);
|
|
117
|
+
resolve(rows || []);
|
|
118
|
+
});
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
// Non-SELECT statements
|
|
122
|
+
return new Promise((resolve, reject) => {
|
|
123
|
+
db.run(sql, params, function (err) {
|
|
124
|
+
if (err)
|
|
125
|
+
return reject(err);
|
|
126
|
+
resolve({ rowsAffected: Number(this?.changes ?? 0), lastInsertId: this?.lastID });
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
},
|
|
130
|
+
conn: db,
|
|
131
|
+
close() {
|
|
132
|
+
return new Promise((resolve, reject) => {
|
|
133
|
+
db.close((err) => (err ? reject(err) : resolve()));
|
|
134
|
+
});
|
|
135
|
+
},
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
catch { }
|
|
139
|
+
throw new Error('No SQLite driver available. Install either better-sqlite3 or sqlite3.');
|
|
140
|
+
}
|
|
141
|
+
//# sourceMappingURL=sqlite.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sqlite.js","sourceRoot":"","sources":["../../../src/adapters/sqlite.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Database } from '../registry';\n\nfunction stripLeadingComments(sql: string): string {\n let i = 0;\n const n = sql.length;\n if (sql.charCodeAt(0) === 0xfeff) i = 1; // BOM\n while (i < n) {\n while (i < n && /\\s/.test(sql[i]!)) i++;\n if (i + 1 < n && sql[i] === '-' && sql[i + 1] === '-') {\n // -- line comment\n i += 2;\n while (i < n && sql[i] !== '\\n') i++;\n continue;\n }\n if (i + 1 < n && sql[i] === '/' && sql[i + 1] === '*') {\n // /* block comment */\n i += 2;\n const end = sql.indexOf('*/', i);\n if (end === -1) return '';\n i = end + 2;\n continue;\n }\n break;\n }\n return sql.slice(i);\n}\n\nexport function createSqliteDb(filenameOrDb: string | any): Database {\n // If a pre-created connection is provided\n if (typeof filenameOrDb !== 'string' && filenameOrDb) {\n const db = filenameOrDb;\n\n // better-sqlite3 shape\n if (typeof db.prepare === 'function') {\n return {\n dialect: 'sqlite',\n async query(sql: string, params: unknown[]) {\n const stmt = db.prepare(sql);\n const text = stripLeadingComments(String(sql)).trimStart().toUpperCase();\n if (text.startsWith('SELECT') || text.startsWith('WITH')) {\n return stmt.all(...params);\n }\n const info = stmt.run(...params);\n return { rowsAffected: Number(info?.changes ?? 0), lastInsertId: info?.lastInsertRowid };\n },\n conn: db,\n // Do not close externally provided connection\n close() {},\n } as Database;\n }\n\n // sqlite3 shape\n return {\n dialect: 'sqlite',\n async query(sql: string, params: unknown[]) {\n const text = stripLeadingComments(String(sql)).trimStart().toUpperCase();\n if (text.startsWith('SELECT') || text.startsWith('WITH')) {\n return new Promise<any[]>((resolve, reject) => {\n db.all(sql, params, (err: Error | null, rows: any[]) => {\n if (err) return reject(err);\n resolve(rows || []);\n });\n });\n }\n return new Promise((resolve, reject) => {\n // Use function callback to access `this` for metadata\n db.run(sql, params, function (this: any, err: Error | null) {\n if (err) return reject(err);\n resolve({ rowsAffected: Number(this?.changes ?? 0), lastInsertId: this?.lastID });\n });\n });\n },\n conn: db,\n // Do not close externally provided connection\n close() {},\n } as Database;\n }\n\n const filename = filenameOrDb as string;\n\n // Prefer better-sqlite3\n try {\n // eslint-disable-next-line @typescript-eslint/no-var-requires\n const BetterSqlite3 = require('better-sqlite3');\n const db = new BetterSqlite3(filename);\n return {\n dialect: 'sqlite',\n async query(sql: string, params: unknown[]) {\n const stmt = db.prepare(sql);\n const text = stripLeadingComments(String(sql)).trimStart().toUpperCase();\n if (text.startsWith('SELECT') || text.startsWith('WITH')) {\n return stmt.all(...params);\n }\n const info = stmt.run(...params);\n return { rowsAffected: Number(info?.changes ?? 0), lastInsertId: info?.lastInsertRowid };\n },\n conn: db,\n close() {\n db.close();\n },\n } as Database;\n } catch {}\n\n // Fallback to sqlite3\n try {\n // eslint-disable-next-line @typescript-eslint/no-var-requires\n const sqlite3 = require('sqlite3');\n const db = new sqlite3.Database(filename);\n return {\n dialect: 'sqlite',\n async query(sql: string, params: unknown[]) {\n const text = stripLeadingComments(String(sql)).trimStart().toUpperCase();\n if (text.startsWith('SELECT') || text.startsWith('WITH')) {\n return new Promise<any[]>((resolve, reject) => {\n db.all(sql, params, (err: Error | null, rows: any[]) => {\n if (err) return reject(err);\n resolve(rows || []);\n });\n });\n }\n // Non-SELECT statements\n return new Promise((resolve, reject) => {\n db.run(sql, params, function (this: any, err: Error | null) {\n if (err) return reject(err);\n resolve({ rowsAffected: Number(this?.changes ?? 0), lastInsertId: this?.lastID });\n });\n });\n },\n conn: db,\n close() {\n return new Promise<void>((resolve, reject) => {\n db.close((err: Error | null) => (err ? reject(err) : resolve()));\n });\n },\n } as Database;\n } catch {}\n\n throw new Error('No SQLite driver available. Install either better-sqlite3 or sqlite3.');\n}\n"]}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
export function createTestDb() {
|
|
2
|
+
const tables = Object.create(null);
|
|
3
|
+
const ensure = (name) => (tables[name] || (tables[name] = []));
|
|
4
|
+
return {
|
|
5
|
+
dialect: 'sqlite',
|
|
6
|
+
async query(sql, params) {
|
|
7
|
+
const s = sql.trim().toUpperCase();
|
|
8
|
+
// Very small subset for tests: CREATE TABLE users
|
|
9
|
+
if (s.startsWith('CREATE TABLE USERS')) {
|
|
10
|
+
ensure('users');
|
|
11
|
+
return { rowsAffected: 0 };
|
|
12
|
+
}
|
|
13
|
+
// INSERT INTO users (id, email, status, ...)
|
|
14
|
+
if (s.startsWith('INSERT INTO USERS')) {
|
|
15
|
+
const users = ensure('users');
|
|
16
|
+
// naive: params order must match tests
|
|
17
|
+
const [id, email, status, created_at] = params;
|
|
18
|
+
users.push({ id, email, status, created_at });
|
|
19
|
+
return { rowsAffected: 1, lastInsertId: id };
|
|
20
|
+
}
|
|
21
|
+
// SELECT id, email, status FROM users WHERE email = ? AND status = ?
|
|
22
|
+
if (/FROM\s+users/i.test(sql) && /WHERE\s+email\s*=/.test(sql) && /status\s*=/.test(sql)) {
|
|
23
|
+
const [email, status] = params;
|
|
24
|
+
return ensure('users').filter((r) => r.email === email && r.status === status);
|
|
25
|
+
}
|
|
26
|
+
// SELECT id, email, status FROM users WHERE id = ?
|
|
27
|
+
if (/FROM\s+users/i.test(sql) && /WHERE\s+id\s*=/.test(sql)) {
|
|
28
|
+
const [id] = params;
|
|
29
|
+
return ensure('users').filter((r) => r.id === id).map((r) => ({ id: r.id, email: r.email, status: r.status }));
|
|
30
|
+
}
|
|
31
|
+
// SELECT id FROM users WHERE status = ? ORDER BY id
|
|
32
|
+
if (/FROM\s+users/i.test(sql) && /WHERE\s+status\s*=/.test(sql)) {
|
|
33
|
+
const [status] = params;
|
|
34
|
+
return ensure('users')
|
|
35
|
+
.filter((r) => r.status === status)
|
|
36
|
+
.sort((a, b) => String(a.id).localeCompare(String(b.id)))
|
|
37
|
+
.map((r) => ({ id: r.id }));
|
|
38
|
+
}
|
|
39
|
+
// Fallback: return empty
|
|
40
|
+
return [];
|
|
41
|
+
},
|
|
42
|
+
conn: null,
|
|
43
|
+
// Ensure close exists; no-op for in-memory test adapter
|
|
44
|
+
close() { },
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"test.js","sourceRoot":"","sources":["../../../src/adapters/test.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Database } from '../registry';\n\ntype Row = Record<string, any>;\n\nexport function createTestDb(): Database {\n const tables: Record<string, Row[]> = Object.create(null);\n const ensure = (name: string) => (tables[name] ||= []);\n\n return {\n dialect: 'sqlite',\n async query(sql: string, params: unknown[]) {\n const s = sql.trim().toUpperCase();\n\n // Very small subset for tests: CREATE TABLE users\n if (s.startsWith('CREATE TABLE USERS')) {\n ensure('users');\n return { rowsAffected: 0 };\n }\n\n // INSERT INTO users (id, email, status, ...)\n if (s.startsWith('INSERT INTO USERS')) {\n const users = ensure('users');\n // naive: params order must match tests\n const [id, email, status, created_at] = params as any[];\n users.push({ id, email, status, created_at });\n return { rowsAffected: 1, lastInsertId: id };\n }\n\n // SELECT id, email, status FROM users WHERE email = ? AND status = ?\n if (/FROM\\s+users/i.test(sql) && /WHERE\\s+email\\s*=/.test(sql) && /status\\s*=/.test(sql)) {\n const [email, status] = params as any[];\n return ensure('users').filter((r) => r.email === email && r.status === status);\n }\n\n // SELECT id, email, status FROM users WHERE id = ?\n if (/FROM\\s+users/i.test(sql) && /WHERE\\s+id\\s*=/.test(sql)) {\n const [id] = params as any[];\n return ensure('users').filter((r) => r.id === id).map((r) => ({ id: r.id, email: r.email, status: r.status }));\n }\n\n // SELECT id FROM users WHERE status = ? ORDER BY id\n if (/FROM\\s+users/i.test(sql) && /WHERE\\s+status\\s*=/.test(sql)) {\n const [status] = params as any[];\n return ensure('users')\n .filter((r) => r.status === status)\n .sort((a, b) => String(a.id).localeCompare(String(b.id)))\n .map((r) => ({ id: r.id }));\n }\n\n // Fallback: return empty\n return [];\n },\n conn: null,\n // Ensure close exists; no-op for in-memory test adapter\n close() {},\n };\n}\n"]}
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
export function createBinder(dialect) {
|
|
2
|
+
const placeholder = (indexRef) => {
|
|
3
|
+
if (dialect === 'postgres')
|
|
4
|
+
return `$${indexRef.i++}`;
|
|
5
|
+
return '?'; // mysql + sqlite
|
|
6
|
+
};
|
|
7
|
+
return {
|
|
8
|
+
bind(sql, args) {
|
|
9
|
+
const isArrayArgs = Array.isArray(args);
|
|
10
|
+
const params = [];
|
|
11
|
+
const out = [];
|
|
12
|
+
let i = 0;
|
|
13
|
+
let inSQ = false; // single quote string
|
|
14
|
+
let inDQ = false; // double quote identifier
|
|
15
|
+
let inLC = false; // line comment -- ...\n
|
|
16
|
+
let inBC = false; // block comment /* ... */
|
|
17
|
+
const pgIndex = { i: 1 };
|
|
18
|
+
let sawNamed = false;
|
|
19
|
+
let sawPositional = false;
|
|
20
|
+
const len = sql.length;
|
|
21
|
+
while (i < len) {
|
|
22
|
+
const ch = sql[i];
|
|
23
|
+
const next = i + 1 < len ? sql[i + 1] : '';
|
|
24
|
+
// Handle exiting comments/strings first
|
|
25
|
+
if (inLC) {
|
|
26
|
+
out.push(ch);
|
|
27
|
+
i++;
|
|
28
|
+
if (ch === '\n')
|
|
29
|
+
inLC = false;
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
32
|
+
if (inBC) {
|
|
33
|
+
out.push(ch);
|
|
34
|
+
i++;
|
|
35
|
+
if (ch === '*' && next === '/') {
|
|
36
|
+
out.push(next);
|
|
37
|
+
i++;
|
|
38
|
+
inBC = false;
|
|
39
|
+
}
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
if (inSQ) {
|
|
43
|
+
out.push(ch);
|
|
44
|
+
i++;
|
|
45
|
+
// escape '' inside string
|
|
46
|
+
if (ch === "'" && next === "'") {
|
|
47
|
+
out.push(next);
|
|
48
|
+
i++;
|
|
49
|
+
}
|
|
50
|
+
else if (ch === "'") {
|
|
51
|
+
inSQ = false;
|
|
52
|
+
}
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
if (inDQ) {
|
|
56
|
+
out.push(ch);
|
|
57
|
+
i++;
|
|
58
|
+
// escape "" inside identifier
|
|
59
|
+
if (ch === '"' && next === '"') {
|
|
60
|
+
out.push(next);
|
|
61
|
+
i++;
|
|
62
|
+
}
|
|
63
|
+
else if (ch === '"') {
|
|
64
|
+
inDQ = false;
|
|
65
|
+
}
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
// Not inside string/comment
|
|
69
|
+
if (ch === '-' && next === '-') {
|
|
70
|
+
out.push(ch, next);
|
|
71
|
+
i += 2;
|
|
72
|
+
inLC = true;
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
if (ch === '/' && next === '*') {
|
|
76
|
+
out.push(ch, next);
|
|
77
|
+
i += 2;
|
|
78
|
+
inBC = true;
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
if (ch === "'") {
|
|
82
|
+
out.push(ch);
|
|
83
|
+
i++;
|
|
84
|
+
inSQ = true;
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
if (ch === '"') {
|
|
88
|
+
out.push(ch);
|
|
89
|
+
i++;
|
|
90
|
+
inDQ = true;
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
// Postgres cast :: — skip as literal
|
|
94
|
+
if (ch === ':' && next === ':') {
|
|
95
|
+
out.push('::');
|
|
96
|
+
i += 2;
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
if (ch === ':') {
|
|
100
|
+
// Attempt to parse a parameter token
|
|
101
|
+
let j = i + 1;
|
|
102
|
+
const start = j;
|
|
103
|
+
while (j < len) {
|
|
104
|
+
const c = sql[j];
|
|
105
|
+
if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c === '_') {
|
|
106
|
+
j++;
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
break;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
const name = sql.slice(start, j);
|
|
113
|
+
if (!name) {
|
|
114
|
+
// not a parameter — keep literal ':'
|
|
115
|
+
out.push(':');
|
|
116
|
+
i++;
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
// Decide positional vs named
|
|
120
|
+
const m = /^arg([1-9][0-9]*)$/.exec(name);
|
|
121
|
+
const isPos = !!m;
|
|
122
|
+
if (isPos)
|
|
123
|
+
sawPositional = true;
|
|
124
|
+
else
|
|
125
|
+
sawNamed = true;
|
|
126
|
+
if (sawNamed && sawPositional) {
|
|
127
|
+
throw new Error('Cannot mix named and positional parameters in a single query');
|
|
128
|
+
}
|
|
129
|
+
let value;
|
|
130
|
+
if (isPos) {
|
|
131
|
+
if (!isArrayArgs)
|
|
132
|
+
throw new Error('Positional placeholders require array arguments');
|
|
133
|
+
const index = parseInt(m[1], 10) - 1; // 1-based in SQL
|
|
134
|
+
const arr = args;
|
|
135
|
+
if (index < 0 || index >= arr.length)
|
|
136
|
+
throw new Error(`Missing positional argument :${name}`);
|
|
137
|
+
value = arr[index];
|
|
138
|
+
}
|
|
139
|
+
else {
|
|
140
|
+
if (isArrayArgs)
|
|
141
|
+
throw new Error('Named placeholders require a parameter object');
|
|
142
|
+
const obj = args;
|
|
143
|
+
if (!Object.prototype.hasOwnProperty.call(obj, name)) {
|
|
144
|
+
throw new Error(`Missing named parameter :${name}`);
|
|
145
|
+
}
|
|
146
|
+
value = obj[name];
|
|
147
|
+
}
|
|
148
|
+
if (value === undefined) {
|
|
149
|
+
throw new Error(`Parameter :${name} is undefined`);
|
|
150
|
+
}
|
|
151
|
+
// Expand placeholder(s)
|
|
152
|
+
if (Array.isArray(value)) {
|
|
153
|
+
if (value.length === 0)
|
|
154
|
+
throw new Error(`Array parameter :${name} must not be empty`);
|
|
155
|
+
const placeholders = [];
|
|
156
|
+
for (const v of value) {
|
|
157
|
+
if (v === undefined)
|
|
158
|
+
throw new Error(`Array parameter :${name} contains undefined`);
|
|
159
|
+
placeholders.push(placeholder(pgIndex));
|
|
160
|
+
params.push(v === undefined ? null : v);
|
|
161
|
+
}
|
|
162
|
+
out.push(placeholders.join(', '));
|
|
163
|
+
}
|
|
164
|
+
else {
|
|
165
|
+
out.push(placeholder(pgIndex));
|
|
166
|
+
params.push(value === undefined ? null : value);
|
|
167
|
+
}
|
|
168
|
+
i = j;
|
|
169
|
+
continue;
|
|
170
|
+
}
|
|
171
|
+
// default: copy char
|
|
172
|
+
out.push(ch);
|
|
173
|
+
i++;
|
|
174
|
+
}
|
|
175
|
+
return { sql: out.join(''), params };
|
|
176
|
+
},
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
export const createMysqlBinder = () => createBinder('mysql');
|
|
180
|
+
export const createSqliteBinder = () => createBinder('sqlite');
|
|
181
|
+
export const createPostgresBinder = () => createBinder('postgres');
|
|
182
|
+
//# sourceMappingURL=sqlBinder.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sqlBinder.js","sourceRoot":"","sources":["../../../src/binders/sqlBinder.ts"],"names":[],"mappings":"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","sourcesContent":["export type BindResult = {\n sql: string;\n params: unknown[];\n};\n\nexport interface SqlBinder {\n bind(sql: string, args: Record<string, unknown> | unknown[]): BindResult;\n}\n\ntype Dialect = 'mysql' | 'sqlite' | 'postgres';\n\nexport function createBinder(dialect: Dialect): SqlBinder {\n const placeholder = (indexRef: { i: number }) => {\n if (dialect === 'postgres') return `$${indexRef.i++}`;\n return '?'; // mysql + sqlite\n };\n\n return {\n bind(sql: string, args: Record<string, unknown> | unknown[]): BindResult {\n const isArrayArgs = Array.isArray(args);\n const params: unknown[] = [];\n const out: string[] = [];\n\n let i = 0;\n let inSQ = false; // single quote string\n let inDQ = false; // double quote identifier\n let inLC = false; // line comment -- ...\\n\n let inBC = false; // block comment /* ... */\n const pgIndex = { i: 1 };\n\n let sawNamed = false;\n let sawPositional = false;\n\n const len = sql.length;\n while (i < len) {\n const ch = sql[i];\n const next = i + 1 < len ? sql[i + 1] : '';\n\n // Handle exiting comments/strings first\n if (inLC) {\n out.push(ch);\n i++;\n if (ch === '\\n') inLC = false;\n continue;\n }\n if (inBC) {\n out.push(ch);\n i++;\n if (ch === '*' && next === '/') {\n out.push(next);\n i++;\n inBC = false;\n }\n continue;\n }\n if (inSQ) {\n out.push(ch);\n i++;\n // escape '' inside string\n if (ch === \"'\" && next === \"'\") {\n out.push(next);\n i++;\n } else if (ch === \"'\") {\n inSQ = false;\n }\n continue;\n }\n if (inDQ) {\n out.push(ch);\n i++;\n // escape \"\" inside identifier\n if (ch === '\"' && next === '\"') {\n out.push(next);\n i++;\n } else if (ch === '\"') {\n inDQ = false;\n }\n continue;\n }\n\n // Not inside string/comment\n if (ch === '-' && next === '-') {\n out.push(ch, next);\n i += 2;\n inLC = true;\n continue;\n }\n if (ch === '/' && next === '*') {\n out.push(ch, next);\n i += 2;\n inBC = true;\n continue;\n }\n if (ch === \"'\") {\n out.push(ch);\n i++;\n inSQ = true;\n continue;\n }\n if (ch === '\"') {\n out.push(ch);\n i++;\n inDQ = true;\n continue;\n }\n\n // Postgres cast :: — skip as literal\n if (ch === ':' && next === ':') {\n out.push('::');\n i += 2;\n continue;\n }\n\n if (ch === ':') {\n // Attempt to parse a parameter token\n let j = i + 1;\n const start = j;\n while (j < len) {\n const c = sql[j];\n if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c === '_') {\n j++;\n } else {\n break;\n }\n }\n const name = sql.slice(start, j);\n if (!name) {\n // not a parameter — keep literal ':'\n out.push(':');\n i++;\n continue;\n }\n\n // Decide positional vs named\n const m = /^arg([1-9][0-9]*)$/.exec(name);\n const isPos = !!m;\n if (isPos) sawPositional = true; else sawNamed = true;\n if (sawNamed && sawPositional) {\n throw new Error('Cannot mix named and positional parameters in a single query');\n }\n\n let value: unknown;\n if (isPos) {\n if (!isArrayArgs) throw new Error('Positional placeholders require array arguments');\n const index = parseInt(m![1], 10) - 1; // 1-based in SQL\n const arr = args as unknown[];\n if (index < 0 || index >= arr.length) throw new Error(`Missing positional argument :${name}`);\n value = arr[index];\n } else {\n if (isArrayArgs) throw new Error('Named placeholders require a parameter object');\n const obj = args as Record<string, unknown>;\n if (!Object.prototype.hasOwnProperty.call(obj, name)) {\n throw new Error(`Missing named parameter :${name}`);\n }\n value = obj[name];\n }\n\n if (value === undefined) {\n throw new Error(`Parameter :${name} is undefined`);\n }\n\n // Expand placeholder(s)\n if (Array.isArray(value)) {\n if (value.length === 0) throw new Error(`Array parameter :${name} must not be empty`);\n const placeholders: string[] = [];\n for (const v of value) {\n if (v === undefined) throw new Error(`Array parameter :${name} contains undefined`);\n placeholders.push(placeholder(pgIndex));\n params.push(v === undefined ? null : v);\n }\n out.push(placeholders.join(', '));\n } else {\n out.push(placeholder(pgIndex));\n params.push(value === undefined ? null : value);\n }\n\n i = j;\n continue;\n }\n\n // default: copy char\n out.push(ch);\n i++;\n }\n\n return { sql: out.join(''), params };\n },\n };\n}\n\nexport const createMysqlBinder = () => createBinder('mysql');\nexport const createSqliteBinder = () => createBinder('sqlite');\nexport const createPostgresBinder = () => createBinder('postgres');\n"]}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export class SqlStackError extends Error {
|
|
2
|
+
constructor(message) {
|
|
3
|
+
super(message);
|
|
4
|
+
this.name = 'SqlStackError';
|
|
5
|
+
}
|
|
6
|
+
}
|
|
7
|
+
export class ExactlyOneRowError extends SqlStackError {
|
|
8
|
+
constructor(actualCount) {
|
|
9
|
+
super(`Expected exactly one row, got ${actualCount}`);
|
|
10
|
+
this.name = 'ExactlyOneRowError';
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
export class NoRowsError extends SqlStackError {
|
|
14
|
+
constructor() {
|
|
15
|
+
super('Expected at least one row, got 0');
|
|
16
|
+
this.name = 'NoRowsError';
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
export class RowsExistError extends SqlStackError {
|
|
20
|
+
constructor(actualCount) {
|
|
21
|
+
super(`Expected no rows, got ${actualCount}`);
|
|
22
|
+
this.name = 'RowsExistError';
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../../../src/core/errors.ts"],"names":[],"mappings":"AAAA,MAAM,OAAO,aAAc,SAAQ,KAAK;IACtC,YAAY,OAAgB;QAC1B,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,eAAe,CAAC;IAC9B,CAAC;CACF;AAED,MAAM,OAAO,kBAAmB,SAAQ,aAAa;IACnD,YAAY,WAAmB;QAC7B,KAAK,CAAC,iCAAiC,WAAW,EAAE,CAAC,CAAC;QACtD,IAAI,CAAC,IAAI,GAAG,oBAAoB,CAAC;IACnC,CAAC;CACF;AAED,MAAM,OAAO,WAAY,SAAQ,aAAa;IAC5C;QACE,KAAK,CAAC,kCAAkC,CAAC,CAAC;QAC1C,IAAI,CAAC,IAAI,GAAG,aAAa,CAAC;IAC5B,CAAC;CACF;AAED,MAAM,OAAO,cAAe,SAAQ,aAAa;IAC/C,YAAY,WAAmB;QAC7B,KAAK,CAAC,yBAAyB,WAAW,EAAE,CAAC,CAAC;QAC9C,IAAI,CAAC,IAAI,GAAG,gBAAgB,CAAC;IAC/B,CAAC;CACF","sourcesContent":["export class SqlStackError extends Error {\n constructor(message?: string) {\n super(message);\n this.name = 'SqlStackError';\n }\n}\n\nexport class ExactlyOneRowError extends SqlStackError {\n constructor(actualCount: number) {\n super(`Expected exactly one row, got ${actualCount}`);\n this.name = 'ExactlyOneRowError';\n }\n}\n\nexport class NoRowsError extends SqlStackError {\n constructor() {\n super('Expected at least one row, got 0');\n this.name = 'NoRowsError';\n }\n}\n\nexport class RowsExistError extends SqlStackError {\n constructor(actualCount: number) {\n super(`Expected no rows, got ${actualCount}`);\n this.name = 'RowsExistError';\n }\n}\n"]}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
const DIALECT_EXT = {
|
|
4
|
+
postgres: 'pg',
|
|
5
|
+
mysql: 'mysql',
|
|
6
|
+
sqlite: 'sqlite',
|
|
7
|
+
};
|
|
8
|
+
const contentCache = new Map();
|
|
9
|
+
const pathCache = new Map();
|
|
10
|
+
function key(root, method, dialect) {
|
|
11
|
+
return `${root}::${method}::${dialect}`;
|
|
12
|
+
}
|
|
13
|
+
export function resolveSqlFromFs(rootDir, methodName, dialect) {
|
|
14
|
+
const k = key(rootDir, methodName, dialect);
|
|
15
|
+
const tried = [];
|
|
16
|
+
const ext = DIALECT_EXT[dialect];
|
|
17
|
+
const dialectPath = path.join(rootDir, `${methodName}.${ext}.sql`);
|
|
18
|
+
const genericPath = path.join(rootDir, `${methodName}.sql`);
|
|
19
|
+
// Cached path
|
|
20
|
+
let chosen = pathCache.get(k);
|
|
21
|
+
if (!chosen) {
|
|
22
|
+
if (fs.existsSync(dialectPath)) {
|
|
23
|
+
chosen = dialectPath;
|
|
24
|
+
}
|
|
25
|
+
else if (fs.existsSync(genericPath)) {
|
|
26
|
+
chosen = genericPath;
|
|
27
|
+
}
|
|
28
|
+
if (chosen)
|
|
29
|
+
pathCache.set(k, chosen);
|
|
30
|
+
}
|
|
31
|
+
if (!chosen) {
|
|
32
|
+
tried.push(dialectPath, genericPath);
|
|
33
|
+
throw new Error(`sqlstack: SQL file not found for ${methodName}\ntried: ${tried.join(', ')}`);
|
|
34
|
+
}
|
|
35
|
+
// Cached content
|
|
36
|
+
let sql = contentCache.get(chosen);
|
|
37
|
+
if (!sql) {
|
|
38
|
+
sql = fs.readFileSync(chosen, 'utf8');
|
|
39
|
+
contentCache.set(chosen, sql);
|
|
40
|
+
}
|
|
41
|
+
return { sql, tried: [chosen] };
|
|
42
|
+
}
|
|
43
|
+
export function coerceRootPath(input) {
|
|
44
|
+
if (!input)
|
|
45
|
+
return {};
|
|
46
|
+
const stat = fs.existsSync(input) ? fs.statSync(input) : undefined;
|
|
47
|
+
if (stat?.isDirectory())
|
|
48
|
+
return { rootDir: input, from: 'folder' };
|
|
49
|
+
if (stat?.isFile())
|
|
50
|
+
return { rootDir: path.dirname(input), from: 'file' };
|
|
51
|
+
// If not found, treat as folder hint anyway
|
|
52
|
+
return { rootDir: input, from: 'folder' };
|
|
53
|
+
}
|
|
54
|
+
export function guessDecoratorDir() {
|
|
55
|
+
const err = new Error();
|
|
56
|
+
const stack = (err.stack || '').split('\n').slice(2);
|
|
57
|
+
for (const line of stack) {
|
|
58
|
+
const m = line.match(/\((.*?):\d+:\d+\)/) || line.match(/at (.*?):\d+:\d+/);
|
|
59
|
+
const file = m?.[1];
|
|
60
|
+
if (!file)
|
|
61
|
+
continue;
|
|
62
|
+
if (file.includes('node_modules'))
|
|
63
|
+
continue;
|
|
64
|
+
if (file.endsWith('.ts') || file.endsWith('.js')) {
|
|
65
|
+
try {
|
|
66
|
+
const dir = path.dirname(file);
|
|
67
|
+
return dir;
|
|
68
|
+
}
|
|
69
|
+
catch { }
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return undefined;
|
|
73
|
+
}
|
|
74
|
+
//# sourceMappingURL=fileResolution.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fileResolution.js","sourceRoot":"","sources":["../../../src/core/fileResolution.ts"],"names":[],"mappings":"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","sourcesContent":["import fs from 'fs';\nimport path from 'path';\nimport type { Dialect } from '../registry';\n\nconst DIALECT_EXT: Record<Dialect, string> = {\n postgres: 'pg',\n mysql: 'mysql',\n sqlite: 'sqlite',\n};\n\nconst contentCache = new Map<string, string>();\nconst pathCache = new Map<string, string>();\n\nfunction key(root: string, method: string, dialect: Dialect): string {\n return `${root}::${method}::${dialect}`;\n}\n\nexport function resolveSqlFromFs(rootDir: string, methodName: string, dialect: Dialect): { sql: string; tried: string[] } {\n const k = key(rootDir, methodName, dialect);\n const tried: string[] = [];\n const ext = DIALECT_EXT[dialect];\n const dialectPath = path.join(rootDir, `${methodName}.${ext}.sql`);\n const genericPath = path.join(rootDir, `${methodName}.sql`);\n\n // Cached path\n let chosen = pathCache.get(k);\n if (!chosen) {\n if (fs.existsSync(dialectPath)) {\n chosen = dialectPath;\n } else if (fs.existsSync(genericPath)) {\n chosen = genericPath;\n }\n if (chosen) pathCache.set(k, chosen);\n }\n\n if (!chosen) {\n tried.push(dialectPath, genericPath);\n throw new Error(\n `sqlstack: SQL file not found for ${methodName}\\ntried: ${tried.join(', ')}`\n );\n }\n\n // Cached content\n let sql = contentCache.get(chosen);\n if (!sql) {\n sql = fs.readFileSync(chosen, 'utf8');\n contentCache.set(chosen, sql);\n }\n return { sql, tried: [chosen] };\n}\n\nexport function coerceRootPath(input?: string): { rootDir?: string; from?: 'file' | 'folder' } {\n if (!input) return {};\n const stat = fs.existsSync(input) ? fs.statSync(input) : undefined;\n if (stat?.isDirectory()) return { rootDir: input, from: 'folder' };\n if (stat?.isFile()) return { rootDir: path.dirname(input), from: 'file' };\n // If not found, treat as folder hint anyway\n return { rootDir: input, from: 'folder' };\n}\n\nexport function guessDecoratorDir(): string | undefined {\n const err = new Error();\n const stack = (err.stack || '').split('\\n').slice(2);\n for (const line of stack) {\n const m = line.match(/\\((.*?):\\d+:\\d+\\)/) || line.match(/at (.*?):\\d+:\\d+/);\n const file = m?.[1];\n if (!file) continue;\n if (file.includes('node_modules')) continue;\n if (file.endsWith('.ts') || file.endsWith('.js')) {\n try {\n const dir = path.dirname(file);\n return dir;\n } catch {}\n }\n }\n return undefined;\n}\n\n"]}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
const META = Symbol.for('sqlstack:meta');
|
|
2
|
+
export function getMeta(target) {
|
|
3
|
+
if (!target[META]) {
|
|
4
|
+
target[META] = { methods: {} };
|
|
5
|
+
}
|
|
6
|
+
return target[META];
|
|
7
|
+
}
|
|
8
|
+
export function setMethodLabels(target, prop, patch) {
|
|
9
|
+
const meta = getMeta(target);
|
|
10
|
+
meta.methods[prop] = { ...(meta.methods[prop] || {}), ...patch };
|
|
11
|
+
}
|
|
12
|
+
export function getMethodLabels(instance, prop) {
|
|
13
|
+
const meta = getMeta(Object.getPrototypeOf(instance));
|
|
14
|
+
return meta.methods[prop] || {};
|
|
15
|
+
}
|
|
16
|
+
export function setClassLabels(ctor, patch) {
|
|
17
|
+
const meta = getMeta(ctor.prototype);
|
|
18
|
+
meta.class = { ...(meta.class || {}), ...patch };
|
|
19
|
+
}
|
|
20
|
+
export function getClassLabels(instance) {
|
|
21
|
+
const meta = getMeta(Object.getPrototypeOf(instance));
|
|
22
|
+
return meta.class;
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=metadata.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"metadata.js","sourceRoot":"","sources":["../../../src/core/metadata.ts"],"names":[],"mappings":"AAAA,MAAM,IAAI,GAAG,MAAM,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC;AAqBzC,MAAM,UAAU,OAAO,CAAC,MAAW;IACjC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC;QAClB,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,OAAO,EAAE,EAAE,EAAe,CAAC;IAC9C,CAAC;IACD,OAAO,MAAM,CAAC,IAAI,CAAC,CAAC;AACtB,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,MAAW,EAAE,IAAqB,EAAE,KAA4B;IAC9F,MAAM,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAC7B,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,EAAE,GAAG,KAAK,EAAE,CAAC;AACnE,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,QAAa,EAAE,IAAqB;IAClE,MAAM,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,cAAc,CAAC,QAAQ,CAAC,CAAC,CAAC;IACtD,OAAO,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;AAClC,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,IAAS,EAAE,KAA2B;IACnE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IACrC,IAAI,CAAC,KAAK,GAAG,EAAE,GAAG,CAAC,IAAI,CAAC,KAAK,IAAI,EAAE,CAAC,EAAE,GAAG,KAAK,EAAE,CAAC;AACnD,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,QAAa;IAC1C,MAAM,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,cAAc,CAAC,QAAQ,CAAC,CAAC,CAAC;IACtD,OAAO,IAAI,CAAC,KAAK,CAAC;AACpB,CAAC","sourcesContent":["const META = Symbol.for('sqlstack:meta');\n\nexport type MethodLabels = {\n defaults?: Record<string, unknown>;\n shape?: 'single' | 'only' | 'exist' | 'none';\n validateSchema?: unknown;\n source?: { kind: 'inline'; sql: string } | { kind: 'fs'; rootDir: string };\n db?: string;\n transform?: (row: any) => any;\n page?: { size: number; zeroBase?: boolean };\n};\n\nexport type ClassLabels = {\n db?: string;\n};\n\nexport type MetaShape = {\n class?: ClassLabels;\n methods: Record<string | symbol, MethodLabels>;\n};\n\nexport function getMeta(target: any): MetaShape {\n if (!target[META]) {\n target[META] = { methods: {} } as MetaShape;\n }\n return target[META];\n}\n\nexport function setMethodLabels(target: any, prop: string | symbol, patch: Partial<MethodLabels>) {\n const meta = getMeta(target);\n meta.methods[prop] = { ...(meta.methods[prop] || {}), ...patch };\n}\n\nexport function getMethodLabels(instance: any, prop: string | symbol): MethodLabels {\n const meta = getMeta(Object.getPrototypeOf(instance));\n return meta.methods[prop] || {};\n}\n\nexport function setClassLabels(ctor: any, patch: Partial<ClassLabels>) {\n const meta = getMeta(ctor.prototype);\n meta.class = { ...(meta.class || {}), ...patch };\n}\n\nexport function getClassLabels(instance: any): ClassLabels | undefined {\n const meta = getMeta(Object.getPrototypeOf(instance));\n return meta.class;\n}\n"]}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import Ajv from 'ajv';
|
|
2
|
+
import addFormats from 'ajv-formats';
|
|
3
|
+
import { SqlStackError } from './errors';
|
|
4
|
+
export class ValidationError extends SqlStackError {
|
|
5
|
+
constructor(details) {
|
|
6
|
+
super(`Validation failed: ${details}`);
|
|
7
|
+
this.details = details;
|
|
8
|
+
this.name = 'ValidationError';
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
const ajv = new Ajv({ allErrors: true, strict: false });
|
|
12
|
+
addFormats(ajv);
|
|
13
|
+
const cache = new WeakMap();
|
|
14
|
+
function getValidator(schema) {
|
|
15
|
+
if (schema && typeof schema === 'object') {
|
|
16
|
+
const existing = cache.get(schema);
|
|
17
|
+
if (existing)
|
|
18
|
+
return existing;
|
|
19
|
+
const compiled = ajv.compile(schema);
|
|
20
|
+
cache.set(schema, compiled);
|
|
21
|
+
return compiled;
|
|
22
|
+
}
|
|
23
|
+
throw new ValidationError('Invalid schema');
|
|
24
|
+
}
|
|
25
|
+
export function validateOutput(schema, data) {
|
|
26
|
+
const validate = getValidator(schema);
|
|
27
|
+
const ok = validate(data);
|
|
28
|
+
if (!ok) {
|
|
29
|
+
const msg = ajv.errorsText(validate.errors, { dataVar: 'result' });
|
|
30
|
+
throw new ValidationError(msg);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
function isTruthyEnv(value) {
|
|
34
|
+
if (!value)
|
|
35
|
+
return false;
|
|
36
|
+
switch (value.toLowerCase()) {
|
|
37
|
+
case '1':
|
|
38
|
+
case 'true':
|
|
39
|
+
case 'yes':
|
|
40
|
+
case 'on':
|
|
41
|
+
return true;
|
|
42
|
+
default:
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
// SQLSTACK_DISABLE_VALIDATION: when truthy, validation is skipped.
|
|
47
|
+
export function isValidationDisabled() {
|
|
48
|
+
// Highest priority: explicit enable wins (even in production)
|
|
49
|
+
if (isTruthyEnv(process.env.SQLSTACK_ENABLE_VALIDATION))
|
|
50
|
+
return false;
|
|
51
|
+
// Next: explicit disable
|
|
52
|
+
if (isTruthyEnv(process.env.SQLSTACK_DISABLE_VALIDATION))
|
|
53
|
+
return true;
|
|
54
|
+
// Default: enabled in all environments
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=validator.js.map
|