sqlstack 3.0.0 → 3.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/adapters/pg.d.ts.map +1 -1
- package/dist/cjs/adapters/pg.js +13 -9
- package/dist/cjs/adapters/pg.js.map +1 -1
- package/dist/cjs/adapters/sqlite.d.ts.map +1 -1
- package/dist/cjs/adapters/sqlite.js +15 -8
- package/dist/cjs/adapters/sqlite.js.map +1 -1
- package/dist/cjs/core/errors.d.ts +5 -0
- package/dist/cjs/core/errors.d.ts.map +1 -1
- package/dist/cjs/core/errors.js +15 -1
- package/dist/cjs/core/errors.js.map +1 -1
- package/dist/cjs/decorators/query.d.ts.map +1 -1
- package/dist/cjs/decorators/query.js +255 -241
- package/dist/cjs/decorators/query.js.map +1 -1
- package/dist/cjs/index.d.ts +1 -0
- package/dist/cjs/index.d.ts.map +1 -1
- package/dist/cjs/index.js +4 -1
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/registry.d.ts +3 -4
- package/dist/cjs/registry.d.ts.map +1 -1
- package/dist/cjs/registry.js.map +1 -1
- package/dist/cjs/run.d.ts +29 -0
- package/dist/cjs/run.d.ts.map +1 -0
- package/dist/cjs/run.js +38 -0
- package/dist/cjs/run.js.map +1 -0
- package/dist/cjs/testing/index.d.ts +4 -0
- package/dist/cjs/testing/index.d.ts.map +1 -1
- package/dist/cjs/testing/index.js +5 -1
- package/dist/cjs/testing/index.js.map +1 -1
- package/dist/cjs/testing/schema/ddl.d.ts +1 -10
- package/dist/cjs/testing/schema/ddl.d.ts.map +1 -1
- package/dist/cjs/testing/schema/ddl.js +29 -13
- package/dist/cjs/testing/schema/ddl.js.map +1 -1
- package/dist/cjs/testing/schema/normalize.js +3 -3
- package/dist/cjs/testing/schema/normalize.js.map +1 -1
- package/dist/cjs/testing/schema/types.d.ts +1 -20
- package/dist/cjs/testing/schema/types.d.ts.map +1 -1
- package/dist/cjs/testing/schema/types.js +0 -4
- package/dist/cjs/testing/schema/types.js.map +1 -1
- package/dist/cjs/testing/sqlite/builder.d.ts +24 -0
- package/dist/cjs/testing/sqlite/builder.d.ts.map +1 -0
- package/dist/cjs/testing/sqlite/builder.js +134 -0
- package/dist/cjs/testing/sqlite/builder.js.map +1 -0
- package/dist/cjs/testing/sqlite/database.d.ts +18 -0
- package/dist/cjs/testing/sqlite/database.d.ts.map +1 -0
- package/dist/cjs/testing/sqlite/database.js +123 -0
- package/dist/cjs/testing/sqlite/database.js.map +1 -0
- package/dist/cjs/testing/sqlite/loadData.d.ts +7 -0
- package/dist/cjs/testing/sqlite/loadData.d.ts.map +1 -0
- package/dist/cjs/testing/sqlite/loadData.js +55 -0
- package/dist/cjs/testing/sqlite/loadData.js.map +1 -0
- package/dist/esm/adapters/pg.js +13 -9
- package/dist/esm/adapters/pg.js.map +1 -1
- package/dist/esm/adapters/sqlite.js +15 -8
- package/dist/esm/adapters/sqlite.js.map +1 -1
- package/dist/esm/core/errors.js +13 -0
- package/dist/esm/core/errors.js.map +1 -1
- package/dist/esm/decorators/query.js +256 -242
- package/dist/esm/decorators/query.js.map +1 -1
- package/dist/esm/index.js +2 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/registry.js.map +1 -1
- package/dist/esm/run.js +34 -0
- package/dist/esm/run.js.map +1 -0
- package/dist/esm/testing/index.js +2 -0
- package/dist/esm/testing/index.js.map +1 -1
- package/dist/esm/testing/schema/ddl.js +29 -13
- package/dist/esm/testing/schema/ddl.js.map +1 -1
- package/dist/esm/testing/schema/normalize.js +3 -3
- package/dist/esm/testing/schema/normalize.js.map +1 -1
- package/dist/esm/testing/schema/types.js +0 -4
- package/dist/esm/testing/schema/types.js.map +1 -1
- package/dist/esm/testing/sqlite/builder.js +98 -0
- package/dist/esm/testing/sqlite/builder.js.map +1 -0
- package/dist/esm/testing/sqlite/database.js +85 -0
- package/dist/esm/testing/sqlite/database.js.map +1 -0
- package/dist/esm/testing/sqlite/loadData.js +48 -0
- package/dist/esm/testing/sqlite/loadData.js.map +1 -0
- package/package.json +4 -3
- package/readme.md +6 -8
|
@@ -1,11 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Types for the sqlstack/testing versioned YAML test-schema.
|
|
3
|
-
*
|
|
4
|
-
* The structured vocabulary is intentionally small (type / nullable /
|
|
5
|
-
* primary / unique / default / references). PostgreSQL type strings pass
|
|
6
|
-
* through; raw SQL covers everything the vocabulary cannot express.
|
|
7
3
|
*/
|
|
8
|
-
/** A column declaration after shorthand expansion (scalar string => { type }). */
|
|
9
4
|
export interface TestColumnInput {
|
|
10
5
|
type: string;
|
|
11
6
|
nullable?: boolean;
|
|
@@ -19,14 +14,12 @@ export interface TestColumnInput {
|
|
|
19
14
|
export interface TestTableInput {
|
|
20
15
|
columns: Record<string, string | TestColumnInput>;
|
|
21
16
|
}
|
|
22
|
-
/** Root YAML document shape. */
|
|
23
17
|
export interface TestSchemaInput {
|
|
24
18
|
version: number;
|
|
25
19
|
dialect: string;
|
|
26
20
|
tables?: Record<string, TestTableInput>;
|
|
27
21
|
sql?: string[];
|
|
28
22
|
}
|
|
29
|
-
/** Normalized (immutable) representations ------------------------------- */
|
|
30
23
|
export interface NormalizedReference {
|
|
31
24
|
readonly table: string;
|
|
32
25
|
readonly column: string;
|
|
@@ -43,9 +36,7 @@ export interface NormalizedColumn {
|
|
|
43
36
|
}
|
|
44
37
|
export interface NormalizedTable {
|
|
45
38
|
readonly name: string;
|
|
46
|
-
/** Authored column order preserved. */
|
|
47
39
|
readonly columns: readonly NormalizedColumn[];
|
|
48
|
-
/** Ordered declared primary-key column names. */
|
|
49
40
|
readonly primaryKey: readonly string[];
|
|
50
41
|
}
|
|
51
42
|
export type RawSqlEntry = {
|
|
@@ -55,28 +46,18 @@ export type RawSqlEntry = {
|
|
|
55
46
|
} | {
|
|
56
47
|
readonly kind: 'file';
|
|
57
48
|
readonly index: number;
|
|
58
|
-
/** Path as authored in the YAML. */
|
|
59
49
|
readonly sourcePath: string;
|
|
60
|
-
/** Absolute resolved path. */
|
|
61
50
|
readonly resolvedPath: string;
|
|
62
51
|
};
|
|
63
52
|
export interface NormalizedTestSchema {
|
|
64
53
|
readonly version: 1;
|
|
65
|
-
readonly dialect: 'postgres';
|
|
66
|
-
/** Authored table order preserved. */
|
|
54
|
+
readonly dialect: 'postgres' | 'sqlite';
|
|
67
55
|
readonly tables: readonly NormalizedTable[];
|
|
68
56
|
readonly sql: readonly RawSqlEntry[];
|
|
69
|
-
/** Where the schema came from, for diagnostics. */
|
|
70
57
|
readonly source: string;
|
|
71
58
|
}
|
|
72
59
|
export interface ParseTestSchemaOptions {
|
|
73
|
-
/**
|
|
74
|
-
* Directory used to resolve relative .sql file entries. Required when the
|
|
75
|
-
* schema contains file entries and no source file path is known. Never
|
|
76
|
-
* defaults to process.cwd() implicitly.
|
|
77
|
-
*/
|
|
78
60
|
sourceDir?: string;
|
|
79
|
-
/** Label used in diagnostics (e.g. the YAML file path). */
|
|
80
61
|
sourceLabel?: string;
|
|
81
62
|
}
|
|
82
63
|
//# sourceMappingURL=types.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/testing/schema/types.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/testing/schema/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,8DAA8D;IAC9D,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;IACpC,2DAA2D;IAC3D,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,eAAe,CAAC,CAAC;CACnD;AAED,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;IACxC,GAAG,CAAC,EAAE,MAAM,EAAE,CAAC;CAChB;AAED,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IACzB,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC;IAC7B,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;IAC7C,QAAQ,CAAC,UAAU,CAAC,EAAE,mBAAmB,CAAC;CAC3C;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,SAAS,gBAAgB,EAAE,CAAC;IAC9C,QAAQ,CAAC,UAAU,EAAE,SAAS,MAAM,EAAE,CAAC;CACxC;AAED,MAAM,MAAM,WAAW,GACnB;IAAE,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GACzE;IACE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;CAC/B,CAAC;AAEN,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAC;IACpB,QAAQ,CAAC,OAAO,EAAE,UAAU,GAAG,QAAQ,CAAC;IACxC,QAAQ,CAAC,MAAM,EAAE,SAAS,eAAe,EAAE,CAAC;IAC5C,QAAQ,CAAC,GAAG,EAAE,SAAS,WAAW,EAAE,CAAC;IACrC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,sBAAsB;IACrC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB"}
|
|
@@ -1,10 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
/**
|
|
3
3
|
* Types for the sqlstack/testing versioned YAML test-schema.
|
|
4
|
-
*
|
|
5
|
-
* The structured vocabulary is intentionally small (type / nullable /
|
|
6
|
-
* primary / unique / default / references). PostgreSQL type strings pass
|
|
7
|
-
* through; raw SQL covers everything the vocabulary cannot express.
|
|
8
4
|
*/
|
|
9
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
6
|
//# sourceMappingURL=types.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../../src/testing/schema/types.ts"],"names":[],"mappings":";AAAA
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../../src/testing/schema/types.ts"],"names":[],"mappings":";AAAA;;GAEG","sourcesContent":["/**\n * Types for the sqlstack/testing versioned YAML test-schema.\n */\n\nexport interface TestColumnInput {\n type: string;\n nullable?: boolean;\n primary?: boolean;\n unique?: boolean;\n /** Raw SQL default expression (emitted verbatim into DDL). */\n default?: string | number | boolean;\n /** Reference target: \"table.column\" or \"table(column)\". */\n references?: string;\n}\n\nexport interface TestTableInput {\n columns: Record<string, string | TestColumnInput>;\n}\n\nexport interface TestSchemaInput {\n version: number;\n dialect: string;\n tables?: Record<string, TestTableInput>;\n sql?: string[];\n}\n\nexport interface NormalizedReference {\n readonly table: string;\n readonly column: string;\n}\n\nexport interface NormalizedColumn {\n readonly name: string;\n readonly type: string;\n readonly nullable: boolean;\n readonly primary: boolean;\n readonly unique: boolean;\n readonly hasDefault: boolean;\n readonly default?: string | number | boolean;\n readonly references?: NormalizedReference;\n}\n\nexport interface NormalizedTable {\n readonly name: string;\n readonly columns: readonly NormalizedColumn[];\n readonly primaryKey: readonly string[];\n}\n\nexport type RawSqlEntry =\n | { readonly kind: 'inline'; readonly index: number; readonly sql: string }\n | {\n readonly kind: 'file';\n readonly index: number;\n readonly sourcePath: string;\n readonly resolvedPath: string;\n };\n\nexport interface NormalizedTestSchema {\n readonly version: 1;\n readonly dialect: 'postgres' | 'sqlite';\n readonly tables: readonly NormalizedTable[];\n readonly sql: readonly RawSqlEntry[];\n readonly source: string;\n}\n\nexport interface ParseTestSchemaOptions {\n sourceDir?: string;\n sourceLabel?: string;\n}\n"]}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { NormalizedTestSchema, ParseTestSchemaOptions } from '../schema/types';
|
|
2
|
+
import { TestSqliteDatabase } from './database';
|
|
3
|
+
import { DataOperation, Row } from './loadData';
|
|
4
|
+
export interface TestSqliteOptions extends ParseTestSchemaOptions {
|
|
5
|
+
/**
|
|
6
|
+
* Storage for the test database. Defaults to `'file'`: an owned temporary
|
|
7
|
+
* file, removed on `dispose()`. Pass `'memory'` to opt back into an isolated
|
|
8
|
+
* `:memory:` database — only worth it for a test that opens many databases
|
|
9
|
+
* and never touches restart behavior.
|
|
10
|
+
*/
|
|
11
|
+
mode?: 'memory' | 'file';
|
|
12
|
+
/** Legacy alias for `mode`. `false` selects `:memory:`. */
|
|
13
|
+
fileBacked?: boolean;
|
|
14
|
+
/** Legacy alias for `mode`. `false` selects `:memory:`. */
|
|
15
|
+
tempFile?: boolean;
|
|
16
|
+
}
|
|
17
|
+
export interface TestSqliteBuilder {
|
|
18
|
+
readonly schema: NormalizedTestSchema;
|
|
19
|
+
readonly ops: readonly DataOperation[];
|
|
20
|
+
data(rows: Record<string, Row[]>): TestSqliteBuilder;
|
|
21
|
+
build(): Promise<TestSqliteDatabase>;
|
|
22
|
+
}
|
|
23
|
+
export declare function testSqlite(schema: string | object, options?: TestSqliteOptions): TestSqliteBuilder;
|
|
24
|
+
//# sourceMappingURL=builder.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"builder.d.ts","sourceRoot":"","sources":["../../../../src/testing/sqlite/builder.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,oBAAoB,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAC;AACpF,OAAO,EAAwB,kBAAkB,EAAmB,MAAM,YAAY,CAAC;AACvF,OAAO,EAAe,aAAa,EAAc,GAAG,EAAgB,MAAM,YAAY,CAAC;AAEvF,MAAM,WAAW,iBAAkB,SAAQ,sBAAsB;IAC/D;;;;;OAKG;IACH,IAAI,CAAC,EAAE,QAAQ,GAAG,MAAM,CAAC;IACzB,2DAA2D;IAC3D,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,2DAA2D;IAC3D,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,MAAM,EAAE,oBAAoB,CAAC;IACtC,QAAQ,CAAC,GAAG,EAAE,SAAS,aAAa,EAAE,CAAC;IACvC,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,GAAG,iBAAiB,CAAC;IACrD,KAAK,IAAI,OAAO,CAAC,kBAAkB,CAAC,CAAC;CACtC;AAoFD,wBAAgB,UAAU,CACxB,MAAM,EAAE,MAAM,GAAG,MAAM,EACvB,OAAO,GAAE,iBAAsB,GAC9B,iBAAiB,CAcnB"}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.testSqlite = testSqlite;
|
|
37
|
+
const fs = __importStar(require("node:fs"));
|
|
38
|
+
const ddl_1 = require("../schema/ddl");
|
|
39
|
+
const parser_1 = require("../schema/parser");
|
|
40
|
+
const database_1 = require("./database");
|
|
41
|
+
const loadData_1 = require("./loadData");
|
|
42
|
+
function readRawSql(schema) {
|
|
43
|
+
return schema.sql.map((entry) => {
|
|
44
|
+
if (entry.kind === 'inline') {
|
|
45
|
+
return { label: `sql entry #${entry.index} (inline)`, sql: entry.sql };
|
|
46
|
+
}
|
|
47
|
+
try {
|
|
48
|
+
return {
|
|
49
|
+
label: `sql entry #${entry.index} (${entry.sourcePath})`,
|
|
50
|
+
sql: fs.readFileSync(entry.resolvedPath, 'utf8'),
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
catch (err) {
|
|
54
|
+
throw new database_1.TestSqliteError(`Cannot read raw SQL file for sql entry #${entry.index}: ${entry.resolvedPath} ` +
|
|
55
|
+
`(authored as "${entry.sourcePath}" in ${schema.source}): ${err.message}`);
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
async function executeRaw(handle, sql) {
|
|
60
|
+
const conn = handle.conn;
|
|
61
|
+
if (typeof conn?.exec === 'function') {
|
|
62
|
+
await conn.exec(sql);
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
await handle.db.query(sql, []);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
function makeBuilder(state) {
|
|
69
|
+
return {
|
|
70
|
+
schema: state.schema,
|
|
71
|
+
ops: state.ops,
|
|
72
|
+
data(rows) {
|
|
73
|
+
return makeBuilder({
|
|
74
|
+
...state,
|
|
75
|
+
ops: [...state.ops, ...(0, loadData_1.toOperations)(state.schema, rows)],
|
|
76
|
+
});
|
|
77
|
+
},
|
|
78
|
+
async build() {
|
|
79
|
+
const rawSql = readRawSql(state.schema);
|
|
80
|
+
const ddl = (0, ddl_1.generateDdl)(state.schema);
|
|
81
|
+
const effective = (0, loadData_1.composeRows)(state.schema, state.ops);
|
|
82
|
+
const fileBacked = state.options.mode
|
|
83
|
+
? state.options.mode === 'file'
|
|
84
|
+
: state.options.fileBacked !== false && state.options.tempFile !== false;
|
|
85
|
+
const handle = await (0, database_1.createDatabaseHandle)(fileBacked);
|
|
86
|
+
try {
|
|
87
|
+
for (const statement of ddl) {
|
|
88
|
+
try {
|
|
89
|
+
await handle.db.query(statement, []);
|
|
90
|
+
}
|
|
91
|
+
catch (err) {
|
|
92
|
+
throw new database_1.TestSqliteError(`Failed to apply structured DDL from ${state.schema.source}: ${err.message}\n` +
|
|
93
|
+
`Statement:\n${statement}`);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
for (const entry of rawSql) {
|
|
97
|
+
try {
|
|
98
|
+
await executeRaw(handle, entry.sql);
|
|
99
|
+
}
|
|
100
|
+
catch (err) {
|
|
101
|
+
throw new database_1.TestSqliteError(`Failed to apply raw ${entry.label} from ${state.schema.source}: ${err.message}`);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
await (0, loadData_1.insertRows)((sql, params) => handle.db.query(sql, params), state.schema, effective);
|
|
105
|
+
return handle;
|
|
106
|
+
}
|
|
107
|
+
catch (err) {
|
|
108
|
+
await handle.dispose();
|
|
109
|
+
throw err;
|
|
110
|
+
}
|
|
111
|
+
},
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
function testSqlite(schema, options = {}) {
|
|
115
|
+
const normalized = isNormalizedSchema(schema)
|
|
116
|
+
? schema
|
|
117
|
+
: (0, parser_1.parseTestSchema)(schema, options);
|
|
118
|
+
if (normalized.dialect !== 'sqlite') {
|
|
119
|
+
throw new database_1.TestSqliteError(`testSqlite requires a schema with dialect: sqlite (received: ${normalized.dialect})`);
|
|
120
|
+
}
|
|
121
|
+
return makeBuilder({
|
|
122
|
+
schema: normalized,
|
|
123
|
+
ops: [],
|
|
124
|
+
options: Object.freeze({ ...options }),
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
function isNormalizedSchema(input) {
|
|
128
|
+
return (!!input &&
|
|
129
|
+
typeof input === 'object' &&
|
|
130
|
+
Array.isArray(input.tables) &&
|
|
131
|
+
Array.isArray(input.sql) &&
|
|
132
|
+
typeof input.source === 'string');
|
|
133
|
+
}
|
|
134
|
+
//# sourceMappingURL=builder.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"builder.js","sourceRoot":"","sources":["../../../../src/testing/sqlite/builder.ts"],"names":[],"mappings":"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","sourcesContent":["import * as fs from 'node:fs';\nimport { generateDdl } from '../schema/ddl';\nimport { parseTestSchema } from '../schema/parser';\nimport type { NormalizedTestSchema, ParseTestSchemaOptions } from '../schema/types';\nimport { createDatabaseHandle, TestSqliteDatabase, TestSqliteError } from './database';\nimport { composeRows, DataOperation, insertRows, Row, toOperations } from './loadData';\n\nexport interface TestSqliteOptions extends ParseTestSchemaOptions {\n /**\n * Storage for the test database. Defaults to `'file'`: an owned temporary\n * file, removed on `dispose()`. Pass `'memory'` to opt back into an isolated\n * `:memory:` database — only worth it for a test that opens many databases\n * and never touches restart behavior.\n */\n mode?: 'memory' | 'file';\n /** Legacy alias for `mode`. `false` selects `:memory:`. */\n fileBacked?: boolean;\n /** Legacy alias for `mode`. `false` selects `:memory:`. */\n tempFile?: boolean;\n}\n\nexport interface TestSqliteBuilder {\n readonly schema: NormalizedTestSchema;\n readonly ops: readonly DataOperation[];\n data(rows: Record<string, Row[]>): TestSqliteBuilder;\n build(): Promise<TestSqliteDatabase>;\n}\n\ninterface BuilderState {\n readonly schema: NormalizedTestSchema;\n readonly ops: readonly DataOperation[];\n readonly options: Readonly<TestSqliteOptions>;\n}\n\nfunction readRawSql(schema: NormalizedTestSchema): { label: string; sql: string }[] {\n return schema.sql.map((entry) => {\n if (entry.kind === 'inline') {\n return { label: `sql entry #${entry.index} (inline)`, sql: entry.sql };\n }\n try {\n return {\n label: `sql entry #${entry.index} (${entry.sourcePath})`,\n sql: fs.readFileSync(entry.resolvedPath, 'utf8'),\n };\n } catch (err) {\n throw new TestSqliteError(\n `Cannot read raw SQL file for sql entry #${entry.index}: ${entry.resolvedPath} ` +\n `(authored as \"${entry.sourcePath}\" in ${schema.source}): ${(err as Error).message}`\n );\n }\n });\n}\n\nasync function executeRaw(handle: TestSqliteDatabase, sql: string): Promise<void> {\n const conn = handle.conn as { exec?: (sql: string) => unknown };\n if (typeof conn?.exec === 'function') {\n await conn.exec(sql);\n } else {\n await handle.db.query(sql, []);\n }\n}\n\nfunction makeBuilder(state: BuilderState): TestSqliteBuilder {\n return {\n schema: state.schema,\n ops: state.ops,\n data(rows: Record<string, Row[]>): TestSqliteBuilder {\n return makeBuilder({\n ...state,\n ops: [...state.ops, ...toOperations(state.schema, rows)],\n });\n },\n async build(): Promise<TestSqliteDatabase> {\n const rawSql = readRawSql(state.schema);\n const ddl = generateDdl(state.schema);\n const effective = composeRows(state.schema, state.ops);\n const fileBacked = state.options.mode\n ? state.options.mode === 'file'\n : state.options.fileBacked !== false && state.options.tempFile !== false;\n const handle = await createDatabaseHandle(fileBacked);\n try {\n for (const statement of ddl) {\n try {\n await handle.db.query(statement, []);\n } catch (err) {\n throw new TestSqliteError(\n `Failed to apply structured DDL from ${state.schema.source}: ${(err as Error).message}\\n` +\n `Statement:\\n${statement}`\n );\n }\n }\n for (const entry of rawSql) {\n try {\n await executeRaw(handle, entry.sql);\n } catch (err) {\n throw new TestSqliteError(\n `Failed to apply raw ${entry.label} from ${state.schema.source}: ${(err as Error).message}`\n );\n }\n }\n await insertRows((sql, params) => handle.db.query(sql, params), state.schema, effective);\n return handle;\n } catch (err) {\n await handle.dispose();\n throw err;\n }\n },\n };\n}\n\nexport function testSqlite(\n schema: string | object,\n options: TestSqliteOptions = {}\n): TestSqliteBuilder {\n const normalized = isNormalizedSchema(schema)\n ? schema\n : parseTestSchema(schema as string | object, options);\n if (normalized.dialect !== 'sqlite') {\n throw new TestSqliteError(\n `testSqlite requires a schema with dialect: sqlite (received: ${normalized.dialect})`\n );\n }\n return makeBuilder({\n schema: normalized,\n ops: [],\n options: Object.freeze({ ...options }),\n });\n}\n\nfunction isNormalizedSchema(input: unknown): input is NormalizedTestSchema {\n return (\n !!input &&\n typeof input === 'object' &&\n Array.isArray((input as NormalizedTestSchema).tables) &&\n Array.isArray((input as NormalizedTestSchema).sql) &&\n typeof (input as NormalizedTestSchema).source === 'string'\n );\n}\n"]}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { SqlStackError } from '../../core/errors';
|
|
2
|
+
import type { Database } from '../../registry';
|
|
3
|
+
export declare class TestSqliteError extends SqlStackError {
|
|
4
|
+
constructor(message: string);
|
|
5
|
+
}
|
|
6
|
+
export interface TestSqliteDatabase {
|
|
7
|
+
/** The live sqlstack SQLite adapter, suitable for direct injection. */
|
|
8
|
+
readonly db: Database;
|
|
9
|
+
/** The underlying better-sqlite3 (or sqlite3) connection. */
|
|
10
|
+
readonly conn: unknown;
|
|
11
|
+
/** Absolute database path in file mode; undefined for :memory:. */
|
|
12
|
+
readonly path?: string;
|
|
13
|
+
query(sql: string, params?: readonly unknown[]): Promise<unknown>;
|
|
14
|
+
/** Close the connection and remove owned temporary files. Idempotent. */
|
|
15
|
+
dispose(): Promise<void>;
|
|
16
|
+
}
|
|
17
|
+
export declare function createDatabaseHandle(fileBacked: boolean): Promise<TestSqliteDatabase>;
|
|
18
|
+
//# sourceMappingURL=database.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"database.d.ts","sourceRoot":"","sources":["../../../../src/testing/sqlite/database.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAClD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAE/C,qBAAa,eAAgB,SAAQ,aAAa;gBACpC,OAAO,EAAE,MAAM;CAI5B;AAED,MAAM,WAAW,kBAAkB;IACjC,uEAAuE;IACvE,QAAQ,CAAC,EAAE,EAAE,QAAQ,CAAC;IACtB,6DAA6D;IAC7D,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IACvB,mEAAmE;IACnE,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,SAAS,OAAO,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAClE,yEAAyE;IACzE,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAC1B;AAyBD,wBAAsB,oBAAoB,CACxC,UAAU,EAAE,OAAO,GAClB,OAAO,CAAC,kBAAkB,CAAC,CA+C7B"}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.TestSqliteError = void 0;
|
|
37
|
+
exports.createDatabaseHandle = createDatabaseHandle;
|
|
38
|
+
const fs = __importStar(require("node:fs"));
|
|
39
|
+
const os = __importStar(require("node:os"));
|
|
40
|
+
const path = __importStar(require("node:path"));
|
|
41
|
+
const sqlite_1 = require("../../adapters/sqlite");
|
|
42
|
+
const errors_1 = require("../../core/errors");
|
|
43
|
+
class TestSqliteError extends errors_1.SqlStackError {
|
|
44
|
+
constructor(message) {
|
|
45
|
+
super(message);
|
|
46
|
+
this.name = 'TestSqliteError';
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
exports.TestSqliteError = TestSqliteError;
|
|
50
|
+
/**
|
|
51
|
+
* Temp directories for file-backed handles that were never disposed. Tests are
|
|
52
|
+
* expected to call `dispose()`, but a failed assertion can skip it; this keeps
|
|
53
|
+
* an abandoned directory from outliving the worker process.
|
|
54
|
+
*/
|
|
55
|
+
const abandonedDirectories = new Set();
|
|
56
|
+
let exitCleanupRegistered = false;
|
|
57
|
+
function trackDirectory(directory) {
|
|
58
|
+
abandonedDirectories.add(directory);
|
|
59
|
+
if (exitCleanupRegistered)
|
|
60
|
+
return;
|
|
61
|
+
exitCleanupRegistered = true;
|
|
62
|
+
process.once('exit', () => {
|
|
63
|
+
for (const dir of abandonedDirectories) {
|
|
64
|
+
try {
|
|
65
|
+
fs.rmSync(dir, { recursive: true, force: true });
|
|
66
|
+
}
|
|
67
|
+
catch {
|
|
68
|
+
/* best-effort cleanup */
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
async function createDatabaseHandle(fileBacked) {
|
|
74
|
+
let directory;
|
|
75
|
+
let filename = ':memory:';
|
|
76
|
+
if (fileBacked) {
|
|
77
|
+
directory = fs.mkdtempSync(path.join(os.tmpdir(), 'sqlstack-test-'));
|
|
78
|
+
filename = path.join(directory, 'database.sqlite');
|
|
79
|
+
trackDirectory(directory);
|
|
80
|
+
}
|
|
81
|
+
let db;
|
|
82
|
+
try {
|
|
83
|
+
db = await (0, sqlite_1.createSqliteDb)(filename);
|
|
84
|
+
}
|
|
85
|
+
catch (err) {
|
|
86
|
+
if (directory) {
|
|
87
|
+
fs.rmSync(directory, { recursive: true, force: true });
|
|
88
|
+
abandonedDirectories.delete(directory);
|
|
89
|
+
}
|
|
90
|
+
const message = err.message;
|
|
91
|
+
if (message.includes('No SQLite driver available')) {
|
|
92
|
+
throw new TestSqliteError('testSqlite requires the optional peer "better-sqlite3".');
|
|
93
|
+
}
|
|
94
|
+
throw new TestSqliteError(`Failed to create SQLite test database: ${message}`);
|
|
95
|
+
}
|
|
96
|
+
let disposed = false;
|
|
97
|
+
return {
|
|
98
|
+
db,
|
|
99
|
+
conn: db.conn,
|
|
100
|
+
path: fileBacked ? filename : undefined,
|
|
101
|
+
async query(sql, params = []) {
|
|
102
|
+
if (disposed)
|
|
103
|
+
throw new TestSqliteError('Test SQLite database has been disposed.');
|
|
104
|
+
return db.query(sql, [...params]);
|
|
105
|
+
},
|
|
106
|
+
async dispose() {
|
|
107
|
+
if (disposed)
|
|
108
|
+
return;
|
|
109
|
+
disposed = true;
|
|
110
|
+
try {
|
|
111
|
+
await db.close();
|
|
112
|
+
}
|
|
113
|
+
catch {
|
|
114
|
+
/* connection may already be closed */
|
|
115
|
+
}
|
|
116
|
+
if (directory) {
|
|
117
|
+
fs.rmSync(directory, { recursive: true, force: true });
|
|
118
|
+
abandonedDirectories.delete(directory);
|
|
119
|
+
}
|
|
120
|
+
},
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
//# sourceMappingURL=database.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"database.js","sourceRoot":"","sources":["../../../../src/testing/sqlite/database.ts"],"names":[],"mappings":"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","sourcesContent":["import * as fs from 'node:fs';\nimport * as os from 'node:os';\nimport * as path from 'node:path';\nimport { createSqliteDb } from '../../adapters/sqlite';\nimport { SqlStackError } from '../../core/errors';\nimport type { Database } from '../../registry';\n\nexport class TestSqliteError extends SqlStackError {\n constructor(message: string) {\n super(message);\n this.name = 'TestSqliteError';\n }\n}\n\nexport interface TestSqliteDatabase {\n /** The live sqlstack SQLite adapter, suitable for direct injection. */\n readonly db: Database;\n /** The underlying better-sqlite3 (or sqlite3) connection. */\n readonly conn: unknown;\n /** Absolute database path in file mode; undefined for :memory:. */\n readonly path?: string;\n query(sql: string, params?: readonly unknown[]): Promise<unknown>;\n /** Close the connection and remove owned temporary files. Idempotent. */\n dispose(): Promise<void>;\n}\n\n/**\n * Temp directories for file-backed handles that were never disposed. Tests are\n * expected to call `dispose()`, but a failed assertion can skip it; this keeps\n * an abandoned directory from outliving the worker process.\n */\nconst abandonedDirectories = new Set<string>();\nlet exitCleanupRegistered = false;\n\nfunction trackDirectory(directory: string): void {\n abandonedDirectories.add(directory);\n if (exitCleanupRegistered) return;\n exitCleanupRegistered = true;\n process.once('exit', () => {\n for (const dir of abandonedDirectories) {\n try {\n fs.rmSync(dir, { recursive: true, force: true });\n } catch {\n /* best-effort cleanup */\n }\n }\n });\n}\n\nexport async function createDatabaseHandle(\n fileBacked: boolean\n): Promise<TestSqliteDatabase> {\n let directory: string | undefined;\n let filename = ':memory:';\n if (fileBacked) {\n directory = fs.mkdtempSync(path.join(os.tmpdir(), 'sqlstack-test-'));\n filename = path.join(directory, 'database.sqlite');\n trackDirectory(directory);\n }\n\n let db: Database;\n try {\n db = await createSqliteDb(filename);\n } catch (err) {\n if (directory) {\n fs.rmSync(directory, { recursive: true, force: true });\n abandonedDirectories.delete(directory);\n }\n const message = (err as Error).message;\n if (message.includes('No SQLite driver available')) {\n throw new TestSqliteError('testSqlite requires the optional peer \"better-sqlite3\".');\n }\n throw new TestSqliteError(`Failed to create SQLite test database: ${message}`);\n }\n\n let disposed = false;\n return {\n db,\n conn: db.conn,\n path: fileBacked ? filename : undefined,\n async query(sql: string, params: readonly unknown[] = []) {\n if (disposed) throw new TestSqliteError('Test SQLite database has been disposed.');\n return db.query(sql, [...params]);\n },\n async dispose() {\n if (disposed) return;\n disposed = true;\n try {\n await db.close();\n } catch {\n /* connection may already be closed */\n }\n if (directory) {\n fs.rmSync(directory, { recursive: true, force: true });\n abandonedDirectories.delete(directory);\n }\n },\n };\n}\n"]}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { NormalizedTestSchema } from '../schema/types';
|
|
2
|
+
import { TestDataError, composeRows, planInsertOrder, toOperations } from '../postgres/loadData';
|
|
3
|
+
import type { DataOperation, Row } from '../postgres/loadData';
|
|
4
|
+
export { TestDataError, composeRows, planInsertOrder, toOperations };
|
|
5
|
+
export type { DataOperation, Row };
|
|
6
|
+
export declare function insertRows(query: (sql: string, params: unknown[]) => Promise<unknown>, schema: NormalizedTestSchema, effective: Map<string, Row[]>): Promise<void>;
|
|
7
|
+
//# sourceMappingURL=loadData.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"loadData.d.ts","sourceRoot":"","sources":["../../../../src/testing/sqlite/loadData.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AAC5D,OAAO,EACL,aAAa,EACb,WAAW,EACX,eAAe,EACf,YAAY,EACb,MAAM,sBAAsB,CAAC;AAC9B,OAAO,KAAK,EAAE,aAAa,EAAE,GAAG,EAAE,MAAM,sBAAsB,CAAC;AAE/D,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,eAAe,EAAE,YAAY,EAAE,CAAC;AACrE,YAAY,EAAE,aAAa,EAAE,GAAG,EAAE,CAAC;AAEnC,wBAAsB,UAAU,CAC9B,KAAK,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,OAAO,CAAC,OAAO,CAAC,EAC3D,MAAM,EAAE,oBAAoB,EAC5B,SAAS,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,GAC5B,OAAO,CAAC,IAAI,CAAC,CAwCf"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.toOperations = exports.planInsertOrder = exports.composeRows = exports.TestDataError = void 0;
|
|
4
|
+
exports.insertRows = insertRows;
|
|
5
|
+
const ddl_1 = require("../schema/ddl");
|
|
6
|
+
const loadData_1 = require("../postgres/loadData");
|
|
7
|
+
Object.defineProperty(exports, "TestDataError", { enumerable: true, get: function () { return loadData_1.TestDataError; } });
|
|
8
|
+
Object.defineProperty(exports, "composeRows", { enumerable: true, get: function () { return loadData_1.composeRows; } });
|
|
9
|
+
Object.defineProperty(exports, "planInsertOrder", { enumerable: true, get: function () { return loadData_1.planInsertOrder; } });
|
|
10
|
+
Object.defineProperty(exports, "toOperations", { enumerable: true, get: function () { return loadData_1.toOperations; } });
|
|
11
|
+
async function insertRows(query, schema, effective) {
|
|
12
|
+
const order = (0, loadData_1.planInsertOrder)(schema, [...effective.keys()]);
|
|
13
|
+
const declared = new Map(schema.tables.map((table) => [
|
|
14
|
+
table.name,
|
|
15
|
+
table.columns.map((column) => column.name),
|
|
16
|
+
]));
|
|
17
|
+
for (const tableName of order) {
|
|
18
|
+
const rows = effective.get(tableName);
|
|
19
|
+
const columnOrder = declared.get(tableName);
|
|
20
|
+
const batches = new Map();
|
|
21
|
+
for (const row of rows) {
|
|
22
|
+
const columns = columnOrder.filter((column) => row[column] !== undefined);
|
|
23
|
+
const signature = columns.join(',');
|
|
24
|
+
const batch = batches.get(signature) ?? { columns, rows: [] };
|
|
25
|
+
batch.rows.push(row);
|
|
26
|
+
batches.set(signature, batch);
|
|
27
|
+
}
|
|
28
|
+
for (const { columns, rows: batchRows } of batches.values()) {
|
|
29
|
+
if (columns.length === 0) {
|
|
30
|
+
for (let i = 0; i < batchRows.length; i++) {
|
|
31
|
+
await run(query, tableName, `INSERT INTO ${(0, ddl_1.quoteIdent)(tableName)} DEFAULT VALUES`, []);
|
|
32
|
+
}
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
const params = [];
|
|
36
|
+
const tuples = batchRows.map((row) => {
|
|
37
|
+
const placeholders = columns.map((column) => {
|
|
38
|
+
params.push(row[column]);
|
|
39
|
+
return '?';
|
|
40
|
+
});
|
|
41
|
+
return `(${placeholders.join(', ')})`;
|
|
42
|
+
});
|
|
43
|
+
await run(query, tableName, `INSERT INTO ${(0, ddl_1.quoteIdent)(tableName)} (${columns.map(ddl_1.quoteIdent).join(', ')}) VALUES ${tuples.join(', ')}`, params);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
async function run(query, tableName, sql, params) {
|
|
48
|
+
try {
|
|
49
|
+
await query(sql, params);
|
|
50
|
+
}
|
|
51
|
+
catch (err) {
|
|
52
|
+
throw new loadData_1.TestDataError(`Failed to insert scenario rows into table "${tableName}": ${err.message}`);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=loadData.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"loadData.js","sourceRoot":"","sources":["../../../../src/testing/sqlite/loadData.ts"],"names":[],"mappings":"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","sourcesContent":["import { quoteIdent } from '../schema/ddl';\nimport type { NormalizedTestSchema } from '../schema/types';\nimport {\n TestDataError,\n composeRows,\n planInsertOrder,\n toOperations,\n} from '../postgres/loadData';\nimport type { DataOperation, Row } from '../postgres/loadData';\n\nexport { TestDataError, composeRows, planInsertOrder, toOperations };\nexport type { DataOperation, Row };\n\nexport async function insertRows(\n query: (sql: string, params: unknown[]) => Promise<unknown>,\n schema: NormalizedTestSchema,\n effective: Map<string, Row[]>\n): Promise<void> {\n const order = planInsertOrder(schema, [...effective.keys()]);\n const declared = new Map(schema.tables.map((table) => [\n table.name,\n table.columns.map((column) => column.name),\n ]));\n for (const tableName of order) {\n const rows = effective.get(tableName)!;\n const columnOrder = declared.get(tableName)!;\n const batches = new Map<string, { columns: string[]; rows: Row[] }>();\n for (const row of rows) {\n const columns = columnOrder.filter((column) => row[column] !== undefined);\n const signature = columns.join(',');\n const batch = batches.get(signature) ?? { columns, rows: [] };\n batch.rows.push(row);\n batches.set(signature, batch);\n }\n for (const { columns, rows: batchRows } of batches.values()) {\n if (columns.length === 0) {\n for (let i = 0; i < batchRows.length; i++) {\n await run(query, tableName, `INSERT INTO ${quoteIdent(tableName)} DEFAULT VALUES`, []);\n }\n continue;\n }\n const params: unknown[] = [];\n const tuples = batchRows.map((row) => {\n const placeholders = columns.map((column) => {\n params.push(row[column]);\n return '?';\n });\n return `(${placeholders.join(', ')})`;\n });\n await run(\n query,\n tableName,\n `INSERT INTO ${quoteIdent(tableName)} (${columns.map(quoteIdent).join(', ')}) VALUES ${tuples.join(', ')}`,\n params\n );\n }\n }\n}\n\nasync function run(\n query: (sql: string, params: unknown[]) => Promise<unknown>,\n tableName: string,\n sql: string,\n params: unknown[]\n): Promise<void> {\n try {\n await query(sql, params);\n } catch (err) {\n throw new TestDataError(\n `Failed to insert scenario rows into table \"${tableName}\": ${(err as Error).message}`\n );\n }\n}\n"]}
|
package/dist/esm/adapters/pg.js
CHANGED
|
@@ -29,6 +29,10 @@ function stripLeadingComments(sql) {
|
|
|
29
29
|
}
|
|
30
30
|
return sql.slice(i);
|
|
31
31
|
}
|
|
32
|
+
function producesRows(sql) {
|
|
33
|
+
const text = stripLeadingComments(String(sql)).trimStart().toUpperCase();
|
|
34
|
+
return text.startsWith('SELECT') || text.startsWith('WITH') || /\bRETURNING\b/.test(text);
|
|
35
|
+
}
|
|
32
36
|
export function createPgDb(urlOrPool) {
|
|
33
37
|
let pool;
|
|
34
38
|
let own = false;
|
|
@@ -45,16 +49,16 @@ export function createPgDb(urlOrPool) {
|
|
|
45
49
|
dialect: 'postgres',
|
|
46
50
|
async query(sql, params) {
|
|
47
51
|
const res = await pool.query(sql, params);
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
//
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
base.returning = res.rows;
|
|
52
|
+
// Rows are rows: any statement that produced a result set (SELECT, CTE,
|
|
53
|
+
// or INSERT/UPDATE/DELETE ... RETURNING) returns the row array so result
|
|
54
|
+
// shape decorators apply uniformly. `res.fields` is the driver's own
|
|
55
|
+
// signal that a result set exists; fall back to sniffing the leading
|
|
56
|
+
// token only for drivers/fakes that do not report fields.
|
|
57
|
+
if (Array.isArray(res.fields) ? res.fields.length > 0 : producesRows(sql)) {
|
|
58
|
+
return Array.isArray(res.rows) ? res.rows : [];
|
|
56
59
|
}
|
|
57
|
-
|
|
60
|
+
// No result set: plain write result
|
|
61
|
+
return { rowsAffected: Number(res.rowCount ?? 0) };
|
|
58
62
|
},
|
|
59
63
|
conn: pool,
|
|
60
64
|
async close() {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pg.js","sourceRoot":"","sources":["../../../src/adapters/pg.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAExD,SAAS,oBAAoB,CAAC,GAAW;IACvC,IAAI,CAAC,GAAG,CAAC,CAAC;IACV,MAAM,CAAC,GAAG,GAAG,CAAC,MAAM,CAAC;IACrB,YAAY;IACZ,IAAI,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,KAAK,MAAM;QAAE,CAAC,GAAG,CAAC,CAAC;IACxC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;QACb,kBAAkB;QAClB,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAE,CAAC;YAAE,CAAC,EAAE,CAAC;QACxC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,KAAK,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,GAAG,EAAE,CAAC;YACtD,wBAAwB;YACxB,CAAC,IAAI,CAAC,CAAC;YACP,OAAO,CAAC,GAAG,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,KAAK,IAAI;gBAAE,CAAC,EAAE,CAAC;YACrC,SAAS;QACX,CAAC;QACD,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,KAAK,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,GAAG,EAAE,CAAC;YACtD,0BAA0B;YAC1B,CAAC,IAAI,CAAC,CAAC;YACP,IAAI,GAAG,GAAG,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;YAC/B,IAAI,GAAG,KAAK,CAAC,CAAC;gBAAE,OAAO,EAAE,CAAC;YAC1B,CAAC,GAAG,GAAG,GAAG,CAAC,CAAC;YACZ,SAAS;QACX,CAAC;QACD,MAAM;IACR,CAAC;IACD,OAAO,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AACtB,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,SAAuB;IAChD,IAAI,IAAS,CAAC;IACd,IAAI,GAAG,GAAG,KAAK,CAAC;IAEhB,IAAI,OAAO,SAAS,KAAK,QAAQ,EAAE,CAAC;QAClC,uEAAuE;QACvE,MAAM,EAAE,IAAI,EAAE,GAAG,cAAc,CAAC,IAAI,CAAC,CAAC;QACtC,IAAI,GAAG,IAAI,IAAI,CAAC,EAAE,gBAAgB,EAAE,SAAS,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC;QACzD,GAAG,GAAG,IAAI,CAAC;IACb,CAAC;SAAM,CAAC;QACN,IAAI,GAAG,SAAS,CAAC;IACnB,CAAC;IAED,OAAO;QACL,OAAO,EAAE,UAAU;QACnB,KAAK,CAAC,KAAK,CAAC,GAAW,EAAE,MAAiB;YACxC,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;YAC1C,
|
|
1
|
+
{"version":3,"file":"pg.js","sourceRoot":"","sources":["../../../src/adapters/pg.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAExD,SAAS,oBAAoB,CAAC,GAAW;IACvC,IAAI,CAAC,GAAG,CAAC,CAAC;IACV,MAAM,CAAC,GAAG,GAAG,CAAC,MAAM,CAAC;IACrB,YAAY;IACZ,IAAI,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,KAAK,MAAM;QAAE,CAAC,GAAG,CAAC,CAAC;IACxC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;QACb,kBAAkB;QAClB,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAE,CAAC;YAAE,CAAC,EAAE,CAAC;QACxC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,KAAK,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,GAAG,EAAE,CAAC;YACtD,wBAAwB;YACxB,CAAC,IAAI,CAAC,CAAC;YACP,OAAO,CAAC,GAAG,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,KAAK,IAAI;gBAAE,CAAC,EAAE,CAAC;YACrC,SAAS;QACX,CAAC;QACD,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,KAAK,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,GAAG,EAAE,CAAC;YACtD,0BAA0B;YAC1B,CAAC,IAAI,CAAC,CAAC;YACP,IAAI,GAAG,GAAG,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;YAC/B,IAAI,GAAG,KAAK,CAAC,CAAC;gBAAE,OAAO,EAAE,CAAC;YAC1B,CAAC,GAAG,GAAG,GAAG,CAAC,CAAC;YACZ,SAAS;QACX,CAAC;QACD,MAAM;IACR,CAAC;IACD,OAAO,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AACtB,CAAC;AAED,SAAS,YAAY,CAAC,GAAW;IAC/B,MAAM,IAAI,GAAG,oBAAoB,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS,EAAE,CAAC,WAAW,EAAE,CAAC;IACzE,OAAO,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,IAAI,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,IAAI,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC5F,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,SAAuB;IAChD,IAAI,IAAS,CAAC;IACd,IAAI,GAAG,GAAG,KAAK,CAAC;IAEhB,IAAI,OAAO,SAAS,KAAK,QAAQ,EAAE,CAAC;QAClC,uEAAuE;QACvE,MAAM,EAAE,IAAI,EAAE,GAAG,cAAc,CAAC,IAAI,CAAC,CAAC;QACtC,IAAI,GAAG,IAAI,IAAI,CAAC,EAAE,gBAAgB,EAAE,SAAS,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC;QACzD,GAAG,GAAG,IAAI,CAAC;IACb,CAAC;SAAM,CAAC;QACN,IAAI,GAAG,SAAS,CAAC;IACnB,CAAC;IAED,OAAO;QACL,OAAO,EAAE,UAAU;QACnB,KAAK,CAAC,KAAK,CAAC,GAAW,EAAE,MAAiB;YACxC,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;YAC1C,wEAAwE;YACxE,yEAAyE;YACzE,qEAAqE;YACrE,qEAAqE;YACrE,0DAA0D;YAC1D,IAAI,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,GAAG,CAAC,EAAE,CAAC;gBAC1E,OAAO,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;YACjD,CAAC;YACD,oCAAoC;YACpC,OAAO,EAAE,YAAY,EAAE,MAAM,CAAC,GAAG,CAAC,QAAQ,IAAI,CAAC,CAAC,EAAE,CAAC;QACrD,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\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\nfunction producesRows(sql: string): boolean {\n const text = stripLeadingComments(String(sql)).trimStart().toUpperCase();\n return text.startsWith('SELECT') || text.startsWith('WITH') || /\\bRETURNING\\b/.test(text);\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 // Rows are rows: any statement that produced a result set (SELECT, CTE,\n // or INSERT/UPDATE/DELETE ... RETURNING) returns the row array so result\n // shape decorators apply uniformly. `res.fields` is the driver's own\n // signal that a result set exists; fall back to sniffing the leading\n // token only for drivers/fakes that do not report fields.\n if (Array.isArray(res.fields) ? res.fields.length > 0 : producesRows(sql)) {\n return Array.isArray(res.rows) ? res.rows : [];\n }\n // No result set: plain write result\n return { rowsAffected: Number(res.rowCount ?? 0) };\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"]}
|