sqlstack 1.0.25 → 2.0.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/dist/cjs/adapters/mysql.d.ts.map +1 -1
- package/dist/cjs/adapters/mysql.js +3 -3
- package/dist/cjs/adapters/mysql.js.map +1 -1
- package/dist/cjs/adapters/pg.d.ts.map +1 -1
- package/dist/cjs/adapters/pg.js +3 -3
- package/dist/cjs/adapters/pg.js.map +1 -1
- package/dist/cjs/codegen/cli.d.ts +3 -0
- package/dist/cjs/codegen/cli.d.ts.map +1 -0
- package/dist/cjs/codegen/cli.js +50 -0
- package/dist/cjs/codegen/cli.js.map +1 -0
- package/dist/cjs/codegen/generateManifest.d.ts +60 -0
- package/dist/cjs/codegen/generateManifest.d.ts.map +1 -0
- package/dist/cjs/codegen/generateManifest.js +181 -0
- package/dist/cjs/codegen/generateManifest.js.map +1 -0
- package/dist/cjs/core/loadNodeModule.d.ts +17 -0
- package/dist/cjs/core/loadNodeModule.d.ts.map +1 -0
- package/dist/cjs/core/loadNodeModule.js +36 -0
- package/dist/cjs/core/loadNodeModule.js.map +1 -0
- package/dist/cjs/core/resolver.d.ts +81 -0
- package/dist/cjs/core/resolver.d.ts.map +1 -0
- package/dist/cjs/core/resolver.js +99 -0
- package/dist/cjs/core/resolver.js.map +1 -0
- package/dist/cjs/decorators/query.d.ts.map +1 -1
- package/dist/cjs/decorators/query.js +19 -4
- package/dist/cjs/decorators/query.js.map +1 -1
- package/dist/cjs/index.d.ts +2 -0
- package/dist/cjs/index.d.ts.map +1 -1
- package/dist/cjs/index.js +6 -1
- package/dist/cjs/index.js.map +1 -1
- package/dist/esm/adapters/mysql.js +3 -3
- package/dist/esm/adapters/mysql.js.map +1 -1
- package/dist/esm/adapters/pg.js +3 -3
- package/dist/esm/adapters/pg.js.map +1 -1
- package/dist/esm/codegen/cli.js +48 -0
- package/dist/esm/codegen/cli.js.map +1 -0
- package/dist/esm/codegen/generateManifest.js +174 -0
- package/dist/esm/codegen/generateManifest.js.map +1 -0
- package/dist/esm/core/loadNodeModule.js +33 -0
- package/dist/esm/core/loadNodeModule.js.map +1 -0
- package/dist/esm/core/resolver.js +92 -0
- package/dist/esm/core/resolver.js.map +1 -0
- package/dist/esm/decorators/query.js +19 -4
- package/dist/esm/decorators/query.js.map +1 -1
- package/dist/esm/index.js +1 -0
- package/dist/esm/index.js.map +1 -1
- package/package.json +12 -3
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mysql.d.ts","sourceRoot":"","sources":["../../../src/adapters/mysql.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"mysql.d.ts","sourceRoot":"","sources":["../../../src/adapters/mysql.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAG5C,wBAAgB,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,GAAG,GAAG,QAAQ,CAkC/D"}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.createMysqlDb = createMysqlDb;
|
|
4
|
+
const loadNodeModule_1 = require("../core/loadNodeModule");
|
|
4
5
|
function createMysqlDb(urlOrPool) {
|
|
5
6
|
let pool;
|
|
6
7
|
let own = false;
|
|
7
8
|
if (typeof urlOrPool === 'string') {
|
|
8
|
-
// Lazy
|
|
9
|
-
|
|
10
|
-
const mysql = require('mysql2/promise');
|
|
9
|
+
// Lazy load to keep driver optional (CJS require or ESM createRequire)
|
|
10
|
+
const mysql = (0, loadNodeModule_1.loadNodeModule)('mysql2/promise');
|
|
11
11
|
pool = mysql.createPool({ uri: urlOrPool, connectionLimit: 4 });
|
|
12
12
|
own = true;
|
|
13
13
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mysql.js","sourceRoot":"","sources":["../../../src/adapters/mysql.ts"],"names":[],"mappings":";;
|
|
1
|
+
{"version":3,"file":"mysql.js","sourceRoot":"","sources":["../../../src/adapters/mysql.ts"],"names":[],"mappings":";;AAGA,sCAkCC;AApCD,2DAAwD;AAExD,SAAgB,aAAa,CAAC,SAAuB;IACnD,IAAI,IAAS,CAAC;IACd,IAAI,GAAG,GAAG,KAAK,CAAC;IAEhB,IAAI,OAAO,SAAS,KAAK,QAAQ,EAAE,CAAC;QAClC,uEAAuE;QACvE,MAAM,KAAK,GAAG,IAAA,+BAAc,EAAC,gBAAgB,CAAC,CAAC;QAC/C,IAAI,GAAG,KAAK,CAAC,UAAU,CAAC,EAAE,GAAG,EAAE,SAAS,EAAE,eAAe,EAAE,CAAC,EAAE,CAAC,CAAC;QAChE,GAAG,GAAG,IAAI,CAAC;IACb,CAAC;SAAM,CAAC;QACN,IAAI,GAAG,SAAS,CAAC;IACnB,CAAC;IAED,OAAO;QACL,OAAO,EAAE,OAAO;QAChB,KAAK,CAAC,KAAK,CAAC,GAAW,EAAE,MAAiB;YACxC,MAAM,CAAC,IAAI,CAAC,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;YAC7C,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;gBACxB,oBAAoB;gBACpB,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;YAC1C,CAAC;YACD,2BAA2B;YAC3B,MAAM,GAAG,GAAQ,IAAI,IAAI,EAAE,CAAC;YAC5B,MAAM,YAAY,GAAG,MAAM,CAAC,GAAG,CAAC,YAAY,IAAI,GAAG,CAAC,WAAW,IAAI,CAAC,CAAC,CAAC;YACtE,MAAM,YAAY,GAAG,GAAG,CAAC,QAAQ,CAAC;YAClC,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,CAAC;QACxC,CAAC;QACD,IAAI,EAAE,IAAI;QACV,KAAK,CAAC,KAAK;YACT,IAAI,GAAG,IAAI,OAAO,IAAI,EAAE,GAAG,KAAK,UAAU,EAAE,CAAC;gBAC3C,MAAM,IAAI,CAAC,GAAG,EAAE,CAAC;YACnB,CAAC;QACH,CAAC;KACU,CAAC;AAChB,CAAC","sourcesContent":["import type { Database } from '../registry';\nimport { loadNodeModule } from '../core/loadNodeModule';\n\nexport function createMysqlDb(urlOrPool: string | any): Database {\n let pool: any;\n let own = false;\n\n if (typeof urlOrPool === 'string') {\n // Lazy load to keep driver optional (CJS require or ESM createRequire)\n const mysql = loadNodeModule('mysql2/promise');\n pool = mysql.createPool({ uri: urlOrPool, connectionLimit: 4 });\n own = true;\n } else {\n pool = urlOrPool;\n }\n\n return {\n dialect: 'mysql',\n async query(sql: string, params: unknown[]) {\n const [rows] = await pool.query(sql, params);\n if (Array.isArray(rows)) {\n // SELECT/CTE result\n return rows.map((r: any) => ({ ...r }));\n }\n // Non-SELECT result header\n const hdr: any = rows || {};\n const rowsAffected = Number(hdr.affectedRows ?? hdr.changedRows ?? 0);\n const lastInsertId = hdr.insertId;\n return { rowsAffected, lastInsertId };\n },\n conn: pool,\n async close() {\n if (own && typeof pool?.end === 'function') {\n await pool.end();\n }\n },\n } as Database;\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pg.d.ts","sourceRoot":"","sources":["../../../src/adapters/pg.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"pg.d.ts","sourceRoot":"","sources":["../../../src/adapters/pg.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AA8B5C,wBAAgB,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,GAAG,GAAG,QAAQ,CAmC5D"}
|
package/dist/cjs/adapters/pg.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.createPgDb = createPgDb;
|
|
4
|
+
const loadNodeModule_1 = require("../core/loadNodeModule");
|
|
4
5
|
function stripLeadingComments(sql) {
|
|
5
6
|
let i = 0;
|
|
6
7
|
const n = sql.length;
|
|
@@ -35,9 +36,8 @@ function createPgDb(urlOrPool) {
|
|
|
35
36
|
let pool;
|
|
36
37
|
let own = false;
|
|
37
38
|
if (typeof urlOrPool === 'string') {
|
|
38
|
-
// Lazy
|
|
39
|
-
|
|
40
|
-
const { Pool } = require('pg');
|
|
39
|
+
// Lazy load to keep driver optional (CJS require or ESM createRequire)
|
|
40
|
+
const { Pool } = (0, loadNodeModule_1.loadNodeModule)('pg');
|
|
41
41
|
pool = new Pool({ connectionString: urlOrPool, max: 4 });
|
|
42
42
|
own = true;
|
|
43
43
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pg.js","sourceRoot":"","sources":["../../../src/adapters/pg.ts"],"names":[],"mappings":";;
|
|
1
|
+
{"version":3,"file":"pg.js","sourceRoot":"","sources":["../../../src/adapters/pg.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Database } from '../registry';\nimport { loadNodeModule } from '../core/loadNodeModule';\n\nfunction stripLeadingComments(sql: string): string {\n let i = 0;\n const n = sql.length;\n // Strip BOM\n if (sql.charCodeAt(0) === 0xfeff) i = 1;\n while (i < n) {\n // Skip whitespace\n while (i < n && /\\s/.test(sql[i]!)) i++;\n if (i + 1 < n && sql[i] === '-' && sql[i + 1] === '-') {\n // Line comment -- ...\\n\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 let 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 createPgDb(urlOrPool: string | any): Database {\n let pool: any;\n let own = false;\n\n if (typeof urlOrPool === 'string') {\n // Lazy load to keep driver optional (CJS require or ESM createRequire)\n const { Pool } = loadNodeModule('pg');\n pool = new Pool({ connectionString: urlOrPool, max: 4 });\n own = true;\n } else {\n pool = urlOrPool;\n }\n\n return {\n dialect: 'postgres',\n async query(sql: string, params: unknown[]) {\n const res = await pool.query(sql, params);\n const text = stripLeadingComments(String(sql)).trimStart().toUpperCase();\n if (text.startsWith('SELECT') || text.startsWith('WITH')) {\n return res.rows;\n }\n // Non-SELECT: include rowCount and optional returning\n const base: any = { rowsAffected: Number(res.rowCount ?? 0) };\n if (Array.isArray(res.rows) && res.rows.length > 0) {\n base.returning = res.rows;\n }\n return base;\n },\n conn: pool,\n async close() {\n if (own && typeof pool?.end === 'function') {\n await pool.end();\n }\n },\n } as Database;\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../../../src/codegen/cli.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
const generateManifest_1 = require("./generateManifest");
|
|
5
|
+
/**
|
|
6
|
+
* sqlstack-codegen — generate the SQL manifest module (Contract A).
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
* sqlstack-codegen --root <dir> [--out <file>] [--format mjs|ts] [--quiet]
|
|
10
|
+
*/
|
|
11
|
+
async function main(argv) {
|
|
12
|
+
const args = new Map();
|
|
13
|
+
let quiet = false;
|
|
14
|
+
for (let i = 0; i < argv.length; i++) {
|
|
15
|
+
const a = argv[i];
|
|
16
|
+
if (a === '--quiet') {
|
|
17
|
+
quiet = true;
|
|
18
|
+
}
|
|
19
|
+
else if (a.startsWith('--')) {
|
|
20
|
+
args.set(a.slice(2), argv[++i] ?? '');
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
const rootDir = args.get('root');
|
|
24
|
+
if (!rootDir) {
|
|
25
|
+
console.error('usage: sqlstack-codegen --root <dir> [--out <file>] [--format mjs|ts] [--quiet]');
|
|
26
|
+
return 2;
|
|
27
|
+
}
|
|
28
|
+
const format = args.get('format');
|
|
29
|
+
if (format && format !== 'mjs' && format !== 'ts') {
|
|
30
|
+
console.error(`sqlstack-codegen: invalid --format "${format}" (expected mjs or ts)`);
|
|
31
|
+
return 2;
|
|
32
|
+
}
|
|
33
|
+
const result = await (0, generateManifest_1.generateSqlManifest)({
|
|
34
|
+
rootDir,
|
|
35
|
+
outFile: args.get('out'),
|
|
36
|
+
format: format,
|
|
37
|
+
});
|
|
38
|
+
if (!quiet) {
|
|
39
|
+
console.log(`sqlstack-codegen: ${result.entries.length} entr${result.entries.length === 1 ? 'y' : 'ies'} → ${result.outFile}`);
|
|
40
|
+
for (const m of result.missing) {
|
|
41
|
+
console.warn(`sqlstack-codegen: warning: no .sql file found for ${m.className}.${m.methodName} (declared in ${m.sourcePath})`);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return 0;
|
|
45
|
+
}
|
|
46
|
+
main(process.argv.slice(2)).then((code) => process.exit(code), (err) => {
|
|
47
|
+
console.error(err instanceof Error ? err.message : String(err));
|
|
48
|
+
process.exit(1);
|
|
49
|
+
});
|
|
50
|
+
//# sourceMappingURL=cli.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../../../src/codegen/cli.ts"],"names":[],"mappings":";;;AACA,yDAAyD;AAEzD;;;;;GAKG;AACH,KAAK,UAAU,IAAI,CAAC,IAAc;IAChC,MAAM,IAAI,GAAG,IAAI,GAAG,EAAkB,CAAC;IACvC,IAAI,KAAK,GAAG,KAAK,CAAC;IAClB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACrC,MAAM,CAAC,GAAG,IAAI,CAAC,CAAC,CAAE,CAAC;QACnB,IAAI,CAAC,KAAK,SAAS,EAAE,CAAC;YACpB,KAAK,GAAG,IAAI,CAAC;QACf,CAAC;aAAM,IAAI,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;YAC9B,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;QACxC,CAAC;IACH,CAAC;IACD,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IACjC,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,OAAO,CAAC,KAAK,CAAC,iFAAiF,CAAC,CAAC;QACjG,OAAO,CAAC,CAAC;IACX,CAAC;IACD,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IAClC,IAAI,MAAM,IAAI,MAAM,KAAK,KAAK,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;QAClD,OAAO,CAAC,KAAK,CAAC,uCAAuC,MAAM,wBAAwB,CAAC,CAAC;QACrF,OAAO,CAAC,CAAC;IACX,CAAC;IAED,MAAM,MAAM,GAAG,MAAM,IAAA,sCAAmB,EAAC;QACvC,OAAO;QACP,OAAO,EAAE,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC;QACxB,MAAM,EAAE,MAAkC;KAC3C,CAAC,CAAC;IAEH,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,OAAO,CAAC,GAAG,CACT,qBAAqB,MAAM,CAAC,OAAO,CAAC,MAAM,QAAQ,MAAM,CAAC,OAAO,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,MAAM,MAAM,CAAC,OAAO,EAAE,CAClH,CAAC;QACF,KAAK,MAAM,CAAC,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;YAC/B,OAAO,CAAC,IAAI,CACV,qDAAqD,CAAC,CAAC,SAAS,IAAI,CAAC,CAAC,UAAU,iBAAiB,CAAC,CAAC,UAAU,GAAG,CACjH,CAAC;QACJ,CAAC;IACH,CAAC;IACD,OAAO,CAAC,CAAC;AACX,CAAC;AAED,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAC9B,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAC5B,CAAC,GAAG,EAAE,EAAE;IACN,OAAO,CAAC,KAAK,CAAC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;IAChE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CACF,CAAC","sourcesContent":["#!/usr/bin/env node\nimport { generateSqlManifest } from './generateManifest';\n\n/**\n * sqlstack-codegen — generate the SQL manifest module (Contract A).\n *\n * Usage:\n * sqlstack-codegen --root <dir> [--out <file>] [--format mjs|ts] [--quiet]\n */\nasync function main(argv: string[]): Promise<number> {\n const args = new Map<string, string>();\n let quiet = false;\n for (let i = 0; i < argv.length; i++) {\n const a = argv[i]!;\n if (a === '--quiet') {\n quiet = true;\n } else if (a.startsWith('--')) {\n args.set(a.slice(2), argv[++i] ?? '');\n }\n }\n const rootDir = args.get('root');\n if (!rootDir) {\n console.error('usage: sqlstack-codegen --root <dir> [--out <file>] [--format mjs|ts] [--quiet]');\n return 2;\n }\n const format = args.get('format');\n if (format && format !== 'mjs' && format !== 'ts') {\n console.error(`sqlstack-codegen: invalid --format \"${format}\" (expected mjs or ts)`);\n return 2;\n }\n\n const result = await generateSqlManifest({\n rootDir,\n outFile: args.get('out'),\n format: format as 'mjs' | 'ts' | undefined,\n });\n\n if (!quiet) {\n console.log(\n `sqlstack-codegen: ${result.entries.length} entr${result.entries.length === 1 ? 'y' : 'ies'} → ${result.outFile}`\n );\n for (const m of result.missing) {\n console.warn(\n `sqlstack-codegen: warning: no .sql file found for ${m.className}.${m.methodName} (declared in ${m.sourcePath})`\n );\n }\n }\n return 0;\n}\n\nmain(process.argv.slice(2)).then(\n (code) => process.exit(code),\n (err) => {\n console.error(err instanceof Error ? err.message : String(err));\n process.exit(1);\n }\n);\n"]}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
export type GenerateSqlManifestOptions = {
|
|
2
|
+
/** Directory to scan recursively for @Query classes and sibling .sql files. */
|
|
3
|
+
rootDir: string;
|
|
4
|
+
/**
|
|
5
|
+
* Output path for the generated module. Default: `<rootDir>/sql-manifest.mjs`.
|
|
6
|
+
* Extension selects the format when `format` is not given (.ts → ts, else mjs).
|
|
7
|
+
*/
|
|
8
|
+
outFile?: string;
|
|
9
|
+
/** 'mjs' (default) emits an ES module; 'ts' emits a typed TS source variant. */
|
|
10
|
+
format?: 'mjs' | 'ts';
|
|
11
|
+
/** Write the generated module to outFile. Default true; false = dry run. */
|
|
12
|
+
write?: boolean;
|
|
13
|
+
/** Directory names to skip while walking. Defaults to node_modules/dist/build/coverage/.git. */
|
|
14
|
+
exclude?: string[];
|
|
15
|
+
};
|
|
16
|
+
export type SqlManifestEntry = {
|
|
17
|
+
key: string;
|
|
18
|
+
className: string;
|
|
19
|
+
methodName: string;
|
|
20
|
+
/** pg | mysql | sqlite for dialect-specific entries; undefined for generic. */
|
|
21
|
+
dialectExt?: string;
|
|
22
|
+
/** Absolute path of the .sql source file. */
|
|
23
|
+
sqlPath: string;
|
|
24
|
+
};
|
|
25
|
+
export type MissingSqlMethod = {
|
|
26
|
+
className: string;
|
|
27
|
+
methodName: string;
|
|
28
|
+
/** Source file that declares the @Query method. */
|
|
29
|
+
sourcePath: string;
|
|
30
|
+
};
|
|
31
|
+
export type GenerateSqlManifestResult = {
|
|
32
|
+
manifest: Record<string, string>;
|
|
33
|
+
entries: SqlManifestEntry[];
|
|
34
|
+
/** Non-inline @Query methods with no sibling .sql file (assertion will catch at startup). */
|
|
35
|
+
missing: MissingSqlMethod[];
|
|
36
|
+
/** The generated module source. */
|
|
37
|
+
code: string;
|
|
38
|
+
/** Absolute path written to (present when write !== false). */
|
|
39
|
+
outFile?: string;
|
|
40
|
+
};
|
|
41
|
+
type DiscoveredMethod = {
|
|
42
|
+
className: string;
|
|
43
|
+
methodName: string;
|
|
44
|
+
inline: boolean;
|
|
45
|
+
sourcePath: string;
|
|
46
|
+
};
|
|
47
|
+
/**
|
|
48
|
+
* Regex-based discovery: each @Query method is attributed to the nearest
|
|
49
|
+
* preceding `class` declaration in the same file. Inline queries are detected
|
|
50
|
+
* by a `sql:` property in the decorator arguments (see docs/codegen.md for the
|
|
51
|
+
* documented limitations of this heuristic).
|
|
52
|
+
*/
|
|
53
|
+
export declare function scanSourceForQueryMethods(source: string, sourcePath: string): DiscoveredMethod[];
|
|
54
|
+
/**
|
|
55
|
+
* Programmatic entry point — also used by the CLI (`sqlstack-codegen`) and
|
|
56
|
+
* callable from application build pipelines.
|
|
57
|
+
*/
|
|
58
|
+
export declare function generateSqlManifest(options: GenerateSqlManifestOptions): Promise<GenerateSqlManifestResult>;
|
|
59
|
+
export {};
|
|
60
|
+
//# sourceMappingURL=generateManifest.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generateManifest.d.ts","sourceRoot":"","sources":["../../../src/codegen/generateManifest.ts"],"names":[],"mappings":"AA6BA,MAAM,MAAM,0BAA0B,GAAG;IACvC,+EAA+E;IAC/E,OAAO,EAAE,MAAM,CAAC;IAChB;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,gFAAgF;IAChF,MAAM,CAAC,EAAE,KAAK,GAAG,IAAI,CAAC;IACtB,4EAA4E;IAC5E,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,gGAAgG;IAChG,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,+EAA+E;IAC/E,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,6CAA6C;IAC7C,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,mDAAmD;IACnD,UAAU,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,yBAAyB,GAAG;IACtC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,OAAO,EAAE,gBAAgB,EAAE,CAAC;IAC5B,6FAA6F;IAC7F,OAAO,EAAE,gBAAgB,EAAE,CAAC;IAC5B,mCAAmC;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,+DAA+D;IAC/D,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,KAAK,gBAAgB,GAAG;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,OAAO,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;CACpB,CAAC;AAyBF;;;;;GAKG;AACH,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,gBAAgB,EAAE,CAyBhG;AAyBD;;;GAGG;AACH,wBAAsB,mBAAmB,CACvC,OAAO,EAAE,0BAA0B,GAClC,OAAO,CAAC,yBAAyB,CAAC,CAwEpC"}
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.scanSourceForQueryMethods = scanSourceForQueryMethods;
|
|
7
|
+
exports.generateSqlManifest = generateSqlManifest;
|
|
8
|
+
const fs_1 = __importDefault(require("fs"));
|
|
9
|
+
const path_1 = __importDefault(require("path"));
|
|
10
|
+
const resolver_1 = require("../core/resolver");
|
|
11
|
+
/**
|
|
12
|
+
* Build-time SQL manifest generation (Contract A).
|
|
13
|
+
*
|
|
14
|
+
* Scans a source tree for @Query-decorated class methods, pairs each with its
|
|
15
|
+
* sibling `.sql` files (same directory as the declaring source file, filename =
|
|
16
|
+
* method name, optional dialect suffix), and emits a generated module:
|
|
17
|
+
*
|
|
18
|
+
* export const sqlManifest = {
|
|
19
|
+
* '<ClassName>.<methodName>.<dialectExt>': '<sql>',
|
|
20
|
+
* '<ClassName>.<methodName>': '<sql>',
|
|
21
|
+
* };
|
|
22
|
+
*
|
|
23
|
+
* The `.sql` files on disk remain the source of truth — the generated module
|
|
24
|
+
* inlines their text and is never hand-edited.
|
|
25
|
+
*
|
|
26
|
+
* Requirements policed here:
|
|
27
|
+
* - Class names must be unique across the scanned tree (manifest keys are
|
|
28
|
+
* class-name scoped) — duplicates are a build error.
|
|
29
|
+
* - Consumers must not minify class names away (esbuild `keepNames`,
|
|
30
|
+
* terser `keep_classnames`) — documented in docs/codegen.md.
|
|
31
|
+
*/
|
|
32
|
+
const SOURCE_EXTENSIONS = new Set(['.ts', '.tsx', '.mts', '.cts', '.js', '.mjs', '.cjs']);
|
|
33
|
+
const DEFAULT_EXCLUDES = new Set(['node_modules', 'dist', 'build', 'coverage', '.git']);
|
|
34
|
+
function walkSourceFiles(rootDir, exclude) {
|
|
35
|
+
const out = [];
|
|
36
|
+
const stack = [rootDir];
|
|
37
|
+
while (stack.length) {
|
|
38
|
+
const dir = stack.pop();
|
|
39
|
+
for (const entry of fs_1.default.readdirSync(dir, { withFileTypes: true })) {
|
|
40
|
+
if (entry.isDirectory()) {
|
|
41
|
+
if (!exclude.has(entry.name))
|
|
42
|
+
stack.push(path_1.default.join(dir, entry.name));
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
const ext = path_1.default.extname(entry.name);
|
|
46
|
+
if (!SOURCE_EXTENSIONS.has(ext))
|
|
47
|
+
continue;
|
|
48
|
+
if (entry.name.endsWith('.d.ts'))
|
|
49
|
+
continue;
|
|
50
|
+
out.push(path_1.default.join(dir, entry.name));
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return out.sort();
|
|
54
|
+
}
|
|
55
|
+
const CLASS_RE = /(?:^|[\s;{}(])class\s+([A-Za-z_$][\w$]*)/g;
|
|
56
|
+
const QUERY_RE = /@Query\s*(?:\(([^)]*)\))?\s*(?:public\s+|private\s+|protected\s+)?(?:async\s+)?([A-Za-z_$][\w$]*)\s*[(<]/g;
|
|
57
|
+
/**
|
|
58
|
+
* Regex-based discovery: each @Query method is attributed to the nearest
|
|
59
|
+
* preceding `class` declaration in the same file. Inline queries are detected
|
|
60
|
+
* by a `sql:` property in the decorator arguments (see docs/codegen.md for the
|
|
61
|
+
* documented limitations of this heuristic).
|
|
62
|
+
*/
|
|
63
|
+
function scanSourceForQueryMethods(source, sourcePath) {
|
|
64
|
+
const classes = [];
|
|
65
|
+
for (const m of source.matchAll(CLASS_RE)) {
|
|
66
|
+
classes.push({ name: m[1], index: m.index ?? 0 });
|
|
67
|
+
}
|
|
68
|
+
if (!classes.length)
|
|
69
|
+
return [];
|
|
70
|
+
const found = [];
|
|
71
|
+
for (const m of source.matchAll(QUERY_RE)) {
|
|
72
|
+
const at = m.index ?? 0;
|
|
73
|
+
let owner;
|
|
74
|
+
for (const c of classes) {
|
|
75
|
+
if (c.index < at)
|
|
76
|
+
owner = c.name;
|
|
77
|
+
else
|
|
78
|
+
break;
|
|
79
|
+
}
|
|
80
|
+
if (!owner)
|
|
81
|
+
continue;
|
|
82
|
+
const args = m[1] ?? '';
|
|
83
|
+
found.push({
|
|
84
|
+
className: owner,
|
|
85
|
+
methodName: m[2],
|
|
86
|
+
inline: /\bsql\s*:/.test(args),
|
|
87
|
+
sourcePath,
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
return found;
|
|
91
|
+
}
|
|
92
|
+
function renderModule(manifest, entries, format, rootDir) {
|
|
93
|
+
const byKey = new Map(entries.map((e) => [e.key, e]));
|
|
94
|
+
const lines = [
|
|
95
|
+
'// GENERATED by sqlstack-codegen. DO NOT EDIT.',
|
|
96
|
+
'// Source of truth: the .sql files referenced beside each entry.',
|
|
97
|
+
format === 'ts'
|
|
98
|
+
? 'export const sqlManifest: Record<string, string> = {'
|
|
99
|
+
: 'export const sqlManifest = {',
|
|
100
|
+
];
|
|
101
|
+
for (const key of Object.keys(manifest).sort()) {
|
|
102
|
+
const entry = byKey.get(key);
|
|
103
|
+
const from = entry ? path_1.default.relative(rootDir, entry.sqlPath) : '';
|
|
104
|
+
lines.push(` ${JSON.stringify(key)}: ${JSON.stringify(manifest[key])},${from ? ` // ${from}` : ''}`);
|
|
105
|
+
}
|
|
106
|
+
lines.push('};', '');
|
|
107
|
+
return lines.join('\n');
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Programmatic entry point — also used by the CLI (`sqlstack-codegen`) and
|
|
111
|
+
* callable from application build pipelines.
|
|
112
|
+
*/
|
|
113
|
+
async function generateSqlManifest(options) {
|
|
114
|
+
const rootDir = path_1.default.resolve(options.rootDir);
|
|
115
|
+
if (!fs_1.default.existsSync(rootDir) || !fs_1.default.statSync(rootDir).isDirectory()) {
|
|
116
|
+
throw new Error(`sqlstack-codegen: rootDir is not a directory: ${rootDir}`);
|
|
117
|
+
}
|
|
118
|
+
const exclude = new Set([...DEFAULT_EXCLUDES, ...(options.exclude ?? [])]);
|
|
119
|
+
// 1) discover @Query methods
|
|
120
|
+
const discovered = [];
|
|
121
|
+
for (const file of walkSourceFiles(rootDir, exclude)) {
|
|
122
|
+
discovered.push(...scanSourceForQueryMethods(fs_1.default.readFileSync(file, 'utf8'), file));
|
|
123
|
+
}
|
|
124
|
+
// 2) police duplicate class names (manifest keys are class-scoped)
|
|
125
|
+
const filesByClass = new Map();
|
|
126
|
+
for (const d of discovered) {
|
|
127
|
+
if (!filesByClass.has(d.className))
|
|
128
|
+
filesByClass.set(d.className, new Set());
|
|
129
|
+
filesByClass.get(d.className).add(d.sourcePath);
|
|
130
|
+
}
|
|
131
|
+
const duplicates = [...filesByClass.entries()].filter(([, files]) => files.size > 1);
|
|
132
|
+
if (duplicates.length) {
|
|
133
|
+
throw new Error('sqlstack-codegen: duplicate @Query class names (manifest keys are class-name scoped; rename one):\n' +
|
|
134
|
+
duplicates
|
|
135
|
+
.map(([name, files]) => ` - ${name}:\n${[...files].map((f) => ` ${f}`).join('\n')}`)
|
|
136
|
+
.join('\n'));
|
|
137
|
+
}
|
|
138
|
+
// 3) pair each non-inline method with sibling .sql files
|
|
139
|
+
const manifest = {};
|
|
140
|
+
const entries = [];
|
|
141
|
+
const missing = [];
|
|
142
|
+
const seen = new Set();
|
|
143
|
+
for (const d of discovered) {
|
|
144
|
+
if (d.inline)
|
|
145
|
+
continue;
|
|
146
|
+
const id = `${d.className}.${d.methodName}`;
|
|
147
|
+
if (seen.has(id))
|
|
148
|
+
continue;
|
|
149
|
+
seen.add(id);
|
|
150
|
+
const dir = path_1.default.dirname(d.sourcePath);
|
|
151
|
+
let matched = false;
|
|
152
|
+
const genericPath = path_1.default.join(dir, `${d.methodName}.sql`);
|
|
153
|
+
if (fs_1.default.existsSync(genericPath)) {
|
|
154
|
+
manifest[id] = fs_1.default.readFileSync(genericPath, 'utf8');
|
|
155
|
+
entries.push({ key: id, className: d.className, methodName: d.methodName, sqlPath: genericPath });
|
|
156
|
+
matched = true;
|
|
157
|
+
}
|
|
158
|
+
for (const ext of Object.values(resolver_1.MANIFEST_DIALECT_EXT)) {
|
|
159
|
+
const dialectPath = path_1.default.join(dir, `${d.methodName}.${ext}.sql`);
|
|
160
|
+
if (fs_1.default.existsSync(dialectPath)) {
|
|
161
|
+
const key = `${id}.${ext}`;
|
|
162
|
+
manifest[key] = fs_1.default.readFileSync(dialectPath, 'utf8');
|
|
163
|
+
entries.push({ key, className: d.className, methodName: d.methodName, dialectExt: ext, sqlPath: dialectPath });
|
|
164
|
+
matched = true;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
if (!matched)
|
|
168
|
+
missing.push({ className: d.className, methodName: d.methodName, sourcePath: d.sourcePath });
|
|
169
|
+
}
|
|
170
|
+
// 4) emit
|
|
171
|
+
const outFile = path_1.default.resolve(options.outFile ?? path_1.default.join(rootDir, 'sql-manifest.mjs'));
|
|
172
|
+
const format = options.format ?? (outFile.endsWith('.ts') ? 'ts' : 'mjs');
|
|
173
|
+
const code = renderModule(manifest, entries, format, rootDir);
|
|
174
|
+
if (options.write !== false) {
|
|
175
|
+
fs_1.default.mkdirSync(path_1.default.dirname(outFile), { recursive: true });
|
|
176
|
+
fs_1.default.writeFileSync(outFile, code);
|
|
177
|
+
return { manifest, entries, missing, code, outFile };
|
|
178
|
+
}
|
|
179
|
+
return { manifest, entries, missing, code };
|
|
180
|
+
}
|
|
181
|
+
//# sourceMappingURL=generateManifest.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generateManifest.js","sourceRoot":"","sources":["../../../src/codegen/generateManifest.ts"],"names":[],"mappings":"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","sourcesContent":["import fs from 'fs';\nimport path from 'path';\nimport { MANIFEST_DIALECT_EXT } from '../core/resolver';\n\n/**\n * Build-time SQL manifest generation (Contract A).\n *\n * Scans a source tree for @Query-decorated class methods, pairs each with its\n * sibling `.sql` files (same directory as the declaring source file, filename =\n * method name, optional dialect suffix), and emits a generated module:\n *\n * export const sqlManifest = {\n * '<ClassName>.<methodName>.<dialectExt>': '<sql>',\n * '<ClassName>.<methodName>': '<sql>',\n * };\n *\n * The `.sql` files on disk remain the source of truth — the generated module\n * inlines their text and is never hand-edited.\n *\n * Requirements policed here:\n * - Class names must be unique across the scanned tree (manifest keys are\n * class-name scoped) — duplicates are a build error.\n * - Consumers must not minify class names away (esbuild `keepNames`,\n * terser `keep_classnames`) — documented in docs/codegen.md.\n */\n\nconst SOURCE_EXTENSIONS = new Set(['.ts', '.tsx', '.mts', '.cts', '.js', '.mjs', '.cjs']);\nconst DEFAULT_EXCLUDES = new Set(['node_modules', 'dist', 'build', 'coverage', '.git']);\n\nexport type GenerateSqlManifestOptions = {\n /** Directory to scan recursively for @Query classes and sibling .sql files. */\n rootDir: string;\n /**\n * Output path for the generated module. Default: `<rootDir>/sql-manifest.mjs`.\n * Extension selects the format when `format` is not given (.ts → ts, else mjs).\n */\n outFile?: string;\n /** 'mjs' (default) emits an ES module; 'ts' emits a typed TS source variant. */\n format?: 'mjs' | 'ts';\n /** Write the generated module to outFile. Default true; false = dry run. */\n write?: boolean;\n /** Directory names to skip while walking. Defaults to node_modules/dist/build/coverage/.git. */\n exclude?: string[];\n};\n\nexport type SqlManifestEntry = {\n key: string;\n className: string;\n methodName: string;\n /** pg | mysql | sqlite for dialect-specific entries; undefined for generic. */\n dialectExt?: string;\n /** Absolute path of the .sql source file. */\n sqlPath: string;\n};\n\nexport type MissingSqlMethod = {\n className: string;\n methodName: string;\n /** Source file that declares the @Query method. */\n sourcePath: string;\n};\n\nexport type GenerateSqlManifestResult = {\n manifest: Record<string, string>;\n entries: SqlManifestEntry[];\n /** Non-inline @Query methods with no sibling .sql file (assertion will catch at startup). */\n missing: MissingSqlMethod[];\n /** The generated module source. */\n code: string;\n /** Absolute path written to (present when write !== false). */\n outFile?: string;\n};\n\ntype DiscoveredMethod = {\n className: string;\n methodName: string;\n inline: boolean;\n sourcePath: string;\n};\n\nfunction walkSourceFiles(rootDir: string, exclude: Set<string>): string[] {\n const out: string[] = [];\n const stack = [rootDir];\n while (stack.length) {\n const dir = stack.pop()!;\n for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {\n if (entry.isDirectory()) {\n if (!exclude.has(entry.name)) stack.push(path.join(dir, entry.name));\n continue;\n }\n const ext = path.extname(entry.name);\n if (!SOURCE_EXTENSIONS.has(ext)) continue;\n if (entry.name.endsWith('.d.ts')) continue;\n out.push(path.join(dir, entry.name));\n }\n }\n return out.sort();\n}\n\nconst CLASS_RE = /(?:^|[\\s;{}(])class\\s+([A-Za-z_$][\\w$]*)/g;\nconst QUERY_RE =\n /@Query\\s*(?:\\(([^)]*)\\))?\\s*(?:public\\s+|private\\s+|protected\\s+)?(?:async\\s+)?([A-Za-z_$][\\w$]*)\\s*[(<]/g;\n\n/**\n * Regex-based discovery: each @Query method is attributed to the nearest\n * preceding `class` declaration in the same file. Inline queries are detected\n * by a `sql:` property in the decorator arguments (see docs/codegen.md for the\n * documented limitations of this heuristic).\n */\nexport function scanSourceForQueryMethods(source: string, sourcePath: string): DiscoveredMethod[] {\n const classes: { name: string; index: number }[] = [];\n for (const m of source.matchAll(CLASS_RE)) {\n classes.push({ name: m[1]!, index: m.index ?? 0 });\n }\n if (!classes.length) return [];\n\n const found: DiscoveredMethod[] = [];\n for (const m of source.matchAll(QUERY_RE)) {\n const at = m.index ?? 0;\n let owner: string | undefined;\n for (const c of classes) {\n if (c.index < at) owner = c.name;\n else break;\n }\n if (!owner) continue;\n const args = m[1] ?? '';\n found.push({\n className: owner,\n methodName: m[2]!,\n inline: /\\bsql\\s*:/.test(args),\n sourcePath,\n });\n }\n return found;\n}\n\nfunction renderModule(\n manifest: Record<string, string>,\n entries: SqlManifestEntry[],\n format: 'mjs' | 'ts',\n rootDir: string\n): string {\n const byKey = new Map(entries.map((e) => [e.key, e]));\n const lines: string[] = [\n '// GENERATED by sqlstack-codegen. DO NOT EDIT.',\n '// Source of truth: the .sql files referenced beside each entry.',\n format === 'ts'\n ? 'export const sqlManifest: Record<string, string> = {'\n : 'export const sqlManifest = {',\n ];\n for (const key of Object.keys(manifest).sort()) {\n const entry = byKey.get(key);\n const from = entry ? path.relative(rootDir, entry.sqlPath) : '';\n lines.push(` ${JSON.stringify(key)}: ${JSON.stringify(manifest[key])},${from ? ` // ${from}` : ''}`);\n }\n lines.push('};', '');\n return lines.join('\\n');\n}\n\n/**\n * Programmatic entry point — also used by the CLI (`sqlstack-codegen`) and\n * callable from application build pipelines.\n */\nexport async function generateSqlManifest(\n options: GenerateSqlManifestOptions\n): Promise<GenerateSqlManifestResult> {\n const rootDir = path.resolve(options.rootDir);\n if (!fs.existsSync(rootDir) || !fs.statSync(rootDir).isDirectory()) {\n throw new Error(`sqlstack-codegen: rootDir is not a directory: ${rootDir}`);\n }\n const exclude = new Set([...DEFAULT_EXCLUDES, ...(options.exclude ?? [])]);\n\n // 1) discover @Query methods\n const discovered: DiscoveredMethod[] = [];\n for (const file of walkSourceFiles(rootDir, exclude)) {\n discovered.push(...scanSourceForQueryMethods(fs.readFileSync(file, 'utf8'), file));\n }\n\n // 2) police duplicate class names (manifest keys are class-scoped)\n const filesByClass = new Map<string, Set<string>>();\n for (const d of discovered) {\n if (!filesByClass.has(d.className)) filesByClass.set(d.className, new Set());\n filesByClass.get(d.className)!.add(d.sourcePath);\n }\n const duplicates = [...filesByClass.entries()].filter(([, files]) => files.size > 1);\n if (duplicates.length) {\n throw new Error(\n 'sqlstack-codegen: duplicate @Query class names (manifest keys are class-name scoped; rename one):\\n' +\n duplicates\n .map(([name, files]) => ` - ${name}:\\n${[...files].map((f) => ` ${f}`).join('\\n')}`)\n .join('\\n')\n );\n }\n\n // 3) pair each non-inline method with sibling .sql files\n const manifest: Record<string, string> = {};\n const entries: SqlManifestEntry[] = [];\n const missing: MissingSqlMethod[] = [];\n const seen = new Set<string>();\n for (const d of discovered) {\n if (d.inline) continue;\n const id = `${d.className}.${d.methodName}`;\n if (seen.has(id)) continue;\n seen.add(id);\n\n const dir = path.dirname(d.sourcePath);\n let matched = false;\n\n const genericPath = path.join(dir, `${d.methodName}.sql`);\n if (fs.existsSync(genericPath)) {\n manifest[id] = fs.readFileSync(genericPath, 'utf8');\n entries.push({ key: id, className: d.className, methodName: d.methodName, sqlPath: genericPath });\n matched = true;\n }\n for (const ext of Object.values(MANIFEST_DIALECT_EXT)) {\n const dialectPath = path.join(dir, `${d.methodName}.${ext}.sql`);\n if (fs.existsSync(dialectPath)) {\n const key = `${id}.${ext}`;\n manifest[key] = fs.readFileSync(dialectPath, 'utf8');\n entries.push({ key, className: d.className, methodName: d.methodName, dialectExt: ext, sqlPath: dialectPath });\n matched = true;\n }\n }\n if (!matched) missing.push({ className: d.className, methodName: d.methodName, sourcePath: d.sourcePath });\n }\n\n // 4) emit\n const outFile = path.resolve(options.outFile ?? path.join(rootDir, 'sql-manifest.mjs'));\n const format = options.format ?? (outFile.endsWith('.ts') ? 'ts' : 'mjs');\n const code = renderModule(manifest, entries, format, rootDir);\n\n if (options.write !== false) {\n fs.mkdirSync(path.dirname(outFile), { recursive: true });\n fs.writeFileSync(outFile, code);\n return { manifest, entries, missing, code, outFile };\n }\n return { manifest, entries, missing, code };\n}\n"]}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Synchronously load an optional Node module (database driver) from both
|
|
3
|
+
* build flavors:
|
|
4
|
+
* - CJS build: plain `require`.
|
|
5
|
+
* - ESM build: `require` does not exist, and the loading functions are
|
|
6
|
+
* synchronous so dynamic `import()` is not an option. Node >= 20.16 / 22.3
|
|
7
|
+
* exposes `process.getBuiltinModule`, which lets us reach
|
|
8
|
+
* `module.createRequire` without a static `node:` import — keeping this
|
|
9
|
+
* file portable to non-Node bundlers (Cloudflare Workers), where neither
|
|
10
|
+
* branch activates and the caller's error message stands.
|
|
11
|
+
*
|
|
12
|
+
* Resolution base under ESM is the process working directory, which is the
|
|
13
|
+
* application root in normal usage — the same node_modules that would host
|
|
14
|
+
* an optional driver dependency.
|
|
15
|
+
*/
|
|
16
|
+
export declare function loadNodeModule(specifier: string): any;
|
|
17
|
+
//# sourceMappingURL=loadNodeModule.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"loadNodeModule.d.ts","sourceRoot":"","sources":["../../../src/core/loadNodeModule.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,GAAG,CAkBrD"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.loadNodeModule = loadNodeModule;
|
|
4
|
+
/**
|
|
5
|
+
* Synchronously load an optional Node module (database driver) from both
|
|
6
|
+
* build flavors:
|
|
7
|
+
* - CJS build: plain `require`.
|
|
8
|
+
* - ESM build: `require` does not exist, and the loading functions are
|
|
9
|
+
* synchronous so dynamic `import()` is not an option. Node >= 20.16 / 22.3
|
|
10
|
+
* exposes `process.getBuiltinModule`, which lets us reach
|
|
11
|
+
* `module.createRequire` without a static `node:` import — keeping this
|
|
12
|
+
* file portable to non-Node bundlers (Cloudflare Workers), where neither
|
|
13
|
+
* branch activates and the caller's error message stands.
|
|
14
|
+
*
|
|
15
|
+
* Resolution base under ESM is the process working directory, which is the
|
|
16
|
+
* application root in normal usage — the same node_modules that would host
|
|
17
|
+
* an optional driver dependency.
|
|
18
|
+
*/
|
|
19
|
+
function loadNodeModule(specifier) {
|
|
20
|
+
if (typeof require === 'function') {
|
|
21
|
+
return require(specifier);
|
|
22
|
+
}
|
|
23
|
+
const getBuiltin = globalThis?.process?.getBuiltinModule;
|
|
24
|
+
if (typeof getBuiltin === 'function') {
|
|
25
|
+
const nodeModule = getBuiltin('module');
|
|
26
|
+
const nodePath = getBuiltin('path');
|
|
27
|
+
const nodeUrl = getBuiltin('url');
|
|
28
|
+
if (nodeModule?.createRequire && nodePath && nodeUrl) {
|
|
29
|
+
const base = nodeUrl.pathToFileURL(nodePath.join(process.cwd(), '__sqlstack_resolver__.js')).href;
|
|
30
|
+
return nodeModule.createRequire(base)(specifier);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
throw new Error(`sqlstack: cannot load "${specifier}" in this runtime (no require/createRequire available). ` +
|
|
34
|
+
'Construct the driver connection yourself and pass it in instead of a connection string.');
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=loadNodeModule.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"loadNodeModule.js","sourceRoot":"","sources":["../../../src/core/loadNodeModule.ts"],"names":[],"mappings":";;AAeA,wCAkBC;AAjCD;;;;;;;;;;;;;;GAcG;AACH,SAAgB,cAAc,CAAC,SAAiB;IAC9C,IAAI,OAAO,OAAO,KAAK,UAAU,EAAE,CAAC;QAClC,OAAO,OAAO,CAAC,SAAS,CAAC,CAAC;IAC5B,CAAC;IACD,MAAM,UAAU,GAAI,UAAkB,EAAE,OAAO,EAAE,gBAAgB,CAAC;IAClE,IAAI,OAAO,UAAU,KAAK,UAAU,EAAE,CAAC;QACrC,MAAM,UAAU,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC;QACxC,MAAM,QAAQ,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC;QACpC,MAAM,OAAO,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC;QAClC,IAAI,UAAU,EAAE,aAAa,IAAI,QAAQ,IAAI,OAAO,EAAE,CAAC;YACrD,MAAM,IAAI,GAAG,OAAO,CAAC,aAAa,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,0BAA0B,CAAC,CAAC,CAAC,IAAI,CAAC;YAClG,OAAO,UAAU,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,CAAC;QACnD,CAAC;IACH,CAAC;IACD,MAAM,IAAI,KAAK,CACb,0BAA0B,SAAS,0DAA0D;QAC3F,yFAAyF,CAC5F,CAAC;AACJ,CAAC","sourcesContent":["/**\n * Synchronously load an optional Node module (database driver) from both\n * build flavors:\n * - CJS build: plain `require`.\n * - ESM build: `require` does not exist, and the loading functions are\n * synchronous so dynamic `import()` is not an option. Node >= 20.16 / 22.3\n * exposes `process.getBuiltinModule`, which lets us reach\n * `module.createRequire` without a static `node:` import — keeping this\n * file portable to non-Node bundlers (Cloudflare Workers), where neither\n * branch activates and the caller's error message stands.\n *\n * Resolution base under ESM is the process working directory, which is the\n * application root in normal usage — the same node_modules that would host\n * an optional driver dependency.\n */\nexport function loadNodeModule(specifier: string): any {\n if (typeof require === 'function') {\n return require(specifier);\n }\n const getBuiltin = (globalThis as any)?.process?.getBuiltinModule;\n if (typeof getBuiltin === 'function') {\n const nodeModule = getBuiltin('module');\n const nodePath = getBuiltin('path');\n const nodeUrl = getBuiltin('url');\n if (nodeModule?.createRequire && nodePath && nodeUrl) {\n const base = nodeUrl.pathToFileURL(nodePath.join(process.cwd(), '__sqlstack_resolver__.js')).href;\n return nodeModule.createRequire(base)(specifier);\n }\n }\n throw new Error(\n `sqlstack: cannot load \"${specifier}\" in this runtime (no require/createRequire available). ` +\n 'Construct the driver connection yourself and pass it in instead of a connection string.'\n );\n}\n"]}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import type { Dialect } from '../registry';
|
|
2
|
+
/**
|
|
3
|
+
* Dialect → manifest key suffix. Mirrors the on-disk file convention
|
|
4
|
+
* (`findByEmail.pg.sql` → key `UsersRepo.findByEmail.pg`).
|
|
5
|
+
*/
|
|
6
|
+
export declare const MANIFEST_DIALECT_EXT: Record<Dialect, string>;
|
|
7
|
+
/**
|
|
8
|
+
* Generated SQL manifest shape (see docs/resolver_architecture.md, Contract A):
|
|
9
|
+
* { '<ClassName>.<methodName>.<dialectExt>': '<sql>', '<ClassName>.<methodName>': '<sql>' }
|
|
10
|
+
*/
|
|
11
|
+
export type SqlManifest = Record<string, string>;
|
|
12
|
+
export type SqlResolutionRequest = {
|
|
13
|
+
className: string;
|
|
14
|
+
methodName: string;
|
|
15
|
+
dialect: Dialect;
|
|
16
|
+
/** Present when the @Query decorator resolved a filesystem root (Node only). */
|
|
17
|
+
rootDir?: string;
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* Strategy for turning a decorated @Query method into a SQL string.
|
|
21
|
+
* The public @Query API is identical under every resolver.
|
|
22
|
+
*/
|
|
23
|
+
export interface SqlResolver {
|
|
24
|
+
/** Return the SQL for the request, or undefined when this resolver has none. */
|
|
25
|
+
resolve(request: SqlResolutionRequest): string | undefined;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Node filesystem resolution — the v1 behavior, unchanged: dialect-specific
|
|
29
|
+
* `<method>.<ext>.sql` first, then generic `<method>.sql`, from the rootDir
|
|
30
|
+
* the decorator captured (explicit argument or stack-based directory guess).
|
|
31
|
+
*/
|
|
32
|
+
export declare class FsResolver implements SqlResolver {
|
|
33
|
+
resolve({ rootDir, methodName, dialect }: SqlResolutionRequest): string | undefined;
|
|
34
|
+
}
|
|
35
|
+
export type ManifestResolverOptions = {
|
|
36
|
+
/**
|
|
37
|
+
* Validate at registration time that every decorated @Query method has a
|
|
38
|
+
* manifest entry (fail fast, earlier than v1's lazy fs lookup).
|
|
39
|
+
* Default true. Set false when some @Query methods build their SQL by
|
|
40
|
+
* returning a SqlFragment from the method body — those never consult the
|
|
41
|
+
* manifest and cannot be distinguished statically.
|
|
42
|
+
*/
|
|
43
|
+
assert?: boolean;
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* Build-time manifest resolution — bundling-proof (Cloudflare Workers).
|
|
47
|
+
* Lookup order per Contract A:
|
|
48
|
+
* 1. `${className}.${methodName}.${dialectExt}`
|
|
49
|
+
* 2. `${className}.${methodName}` (generic fallback)
|
|
50
|
+
*/
|
|
51
|
+
export declare class ManifestResolver implements SqlResolver {
|
|
52
|
+
private readonly manifest;
|
|
53
|
+
readonly options: ManifestResolverOptions;
|
|
54
|
+
constructor(manifest: SqlManifest, options?: ManifestResolverOptions);
|
|
55
|
+
resolve({ className, methodName, dialect }: SqlResolutionRequest): string | undefined;
|
|
56
|
+
/** True when the manifest has a generic or any dialect-specific entry. */
|
|
57
|
+
hasEntryFor(className: string, methodName: string): boolean;
|
|
58
|
+
}
|
|
59
|
+
type DecoratedMethod = {
|
|
60
|
+
className: string;
|
|
61
|
+
methodName: string;
|
|
62
|
+
inline: boolean;
|
|
63
|
+
};
|
|
64
|
+
/** @internal Called by the @Query decorator at class-definition time. */
|
|
65
|
+
export declare function registerDecoratedMethod(entry: DecoratedMethod): void;
|
|
66
|
+
/** @internal Read by the @Query call path. */
|
|
67
|
+
export declare function getActiveResolver(): SqlResolver | undefined;
|
|
68
|
+
/**
|
|
69
|
+
* Resolver registration surface (Contract A):
|
|
70
|
+
* SqlStack.useResolver(new ManifestResolver(sqlManifest))
|
|
71
|
+
* Call at app startup, after repository classes have been imported
|
|
72
|
+
* (decoration populates the coverage check).
|
|
73
|
+
* With no resolver registered, @Query resolves via the filesystem exactly
|
|
74
|
+
* as in v1.
|
|
75
|
+
*/
|
|
76
|
+
export declare const SqlStack: {
|
|
77
|
+
useResolver(resolver: SqlResolver): void;
|
|
78
|
+
clearResolver(): void;
|
|
79
|
+
};
|
|
80
|
+
export {};
|
|
81
|
+
//# sourceMappingURL=resolver.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resolver.d.ts","sourceRoot":"","sources":["../../../src/core/resolver.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAG3C;;;GAGG;AACH,eAAO,MAAM,oBAAoB,EAAE,MAAM,CAAC,OAAO,EAAE,MAAM,CAIxD,CAAC;AAEF;;;GAGG;AACH,MAAM,MAAM,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAEjD,MAAM,MAAM,oBAAoB,GAAG;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,OAAO,CAAC;IACjB,gFAAgF;IAChF,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF;;;GAGG;AACH,MAAM,WAAW,WAAW;IAC1B,gFAAgF;IAChF,OAAO,CAAC,OAAO,EAAE,oBAAoB,GAAG,MAAM,GAAG,SAAS,CAAC;CAC5D;AAED;;;;GAIG;AACH,qBAAa,UAAW,YAAW,WAAW;IAC5C,OAAO,CAAC,EAAE,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,EAAE,oBAAoB,GAAG,MAAM,GAAG,SAAS;CAIpF;AAED,MAAM,MAAM,uBAAuB,GAAG;IACpC;;;;;;OAMG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB,CAAC;AAEF;;;;;GAKG;AACH,qBAAa,gBAAiB,YAAW,WAAW;IAEhD,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,QAAQ,CAAC,OAAO,EAAE,uBAAuB;gBADxB,QAAQ,EAAE,WAAW,EAC7B,OAAO,GAAE,uBAA4B;IAGhD,OAAO,CAAC,EAAE,SAAS,EAAE,UAAU,EAAE,OAAO,EAAE,EAAE,oBAAoB,GAAG,MAAM,GAAG,SAAS;IAQrF,0EAA0E;IAC1E,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO;CAK5D;AAED,KAAK,eAAe,GAAG;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,OAAO,CAAA;CAAE,CAAC;AAclF,yEAAyE;AACzE,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,eAAe,GAAG,IAAI,CAEpE;AAED,8CAA8C;AAC9C,wBAAgB,iBAAiB,IAAI,WAAW,GAAG,SAAS,CAE3D;AAiBD;;;;;;;GAOG;AACH,eAAO,MAAM,QAAQ;0BACG,WAAW,GAAG,IAAI;qBAMvB,IAAI;CAGtB,CAAC"}
|