sqlstack 2.0.0 → 3.1.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.
Files changed (156) hide show
  1. package/dist/cjs/codegen/generateManifest.d.ts +12 -0
  2. package/dist/cjs/codegen/generateManifest.d.ts.map +1 -1
  3. package/dist/cjs/codegen/generateManifest.js +21 -11
  4. package/dist/cjs/codegen/generateManifest.js.map +1 -1
  5. package/dist/cjs/core/resolver.d.ts +25 -12
  6. package/dist/cjs/core/resolver.d.ts.map +1 -1
  7. package/dist/cjs/core/resolver.js +66 -20
  8. package/dist/cjs/core/resolver.js.map +1 -1
  9. package/dist/cjs/core/sourceScanResolver.d.ts +25 -0
  10. package/dist/cjs/core/sourceScanResolver.d.ts.map +1 -0
  11. package/dist/cjs/core/sourceScanResolver.js +56 -0
  12. package/dist/cjs/core/sourceScanResolver.js.map +1 -0
  13. package/dist/cjs/decorators/query.d.ts.map +1 -1
  14. package/dist/cjs/decorators/query.js +276 -245
  15. package/dist/cjs/decorators/query.js.map +1 -1
  16. package/dist/cjs/families/sqliteFamily.d.ts +26 -0
  17. package/dist/cjs/families/sqliteFamily.d.ts.map +1 -0
  18. package/dist/cjs/families/sqliteFamily.js +187 -0
  19. package/dist/cjs/families/sqliteFamily.js.map +1 -0
  20. package/dist/cjs/family.d.ts +69 -0
  21. package/dist/cjs/family.d.ts.map +1 -0
  22. package/dist/cjs/family.js +206 -0
  23. package/dist/cjs/family.js.map +1 -0
  24. package/dist/cjs/familyBinding.d.ts +22 -0
  25. package/dist/cjs/familyBinding.d.ts.map +1 -0
  26. package/dist/cjs/familyBinding.js +22 -0
  27. package/dist/cjs/familyBinding.js.map +1 -0
  28. package/dist/cjs/index.d.ts +10 -1
  29. package/dist/cjs/index.d.ts.map +1 -1
  30. package/dist/cjs/index.js +17 -2
  31. package/dist/cjs/index.js.map +1 -1
  32. package/dist/cjs/ioc.d.ts +22 -0
  33. package/dist/cjs/ioc.d.ts.map +1 -0
  34. package/dist/cjs/ioc.js +35 -0
  35. package/dist/cjs/ioc.js.map +1 -0
  36. package/dist/cjs/run.d.ts +29 -0
  37. package/dist/cjs/run.d.ts.map +1 -0
  38. package/dist/cjs/run.js +38 -0
  39. package/dist/cjs/run.js.map +1 -0
  40. package/dist/cjs/runtime.d.ts +25 -0
  41. package/dist/cjs/runtime.d.ts.map +1 -0
  42. package/dist/cjs/runtime.js +133 -0
  43. package/dist/cjs/runtime.js.map +1 -0
  44. package/dist/cjs/stack.d.ts +101 -0
  45. package/dist/cjs/stack.d.ts.map +1 -0
  46. package/dist/cjs/stack.js +288 -0
  47. package/dist/cjs/stack.js.map +1 -0
  48. package/dist/cjs/testing/builder.d.ts +29 -0
  49. package/dist/cjs/testing/builder.d.ts.map +1 -0
  50. package/dist/cjs/testing/builder.js +131 -0
  51. package/dist/cjs/testing/builder.js.map +1 -0
  52. package/dist/cjs/testing/index.d.ts +14 -0
  53. package/dist/cjs/testing/index.d.ts.map +1 -0
  54. package/dist/cjs/testing/index.js +22 -0
  55. package/dist/cjs/testing/index.js.map +1 -0
  56. package/dist/cjs/testing/postgres/allocator.d.ts +17 -0
  57. package/dist/cjs/testing/postgres/allocator.d.ts.map +1 -0
  58. package/dist/cjs/testing/postgres/allocator.js +54 -0
  59. package/dist/cjs/testing/postgres/allocator.js.map +1 -0
  60. package/dist/cjs/testing/postgres/database.d.ts +21 -0
  61. package/dist/cjs/testing/postgres/database.d.ts.map +1 -0
  62. package/dist/cjs/testing/postgres/database.js +39 -0
  63. package/dist/cjs/testing/postgres/database.js.map +1 -0
  64. package/dist/cjs/testing/postgres/loadData.d.ts +31 -0
  65. package/dist/cjs/testing/postgres/loadData.d.ts.map +1 -0
  66. package/dist/cjs/testing/postgres/loadData.js +184 -0
  67. package/dist/cjs/testing/postgres/loadData.js.map +1 -0
  68. package/dist/cjs/testing/postgres/server.d.ts +19 -0
  69. package/dist/cjs/testing/postgres/server.d.ts.map +1 -0
  70. package/dist/cjs/testing/postgres/server.js +78 -0
  71. package/dist/cjs/testing/postgres/server.js.map +1 -0
  72. package/dist/cjs/testing/schema/ddl.d.ts +5 -0
  73. package/dist/cjs/testing/schema/ddl.d.ts.map +1 -0
  74. package/dist/cjs/testing/schema/ddl.js +70 -0
  75. package/dist/cjs/testing/schema/ddl.js.map +1 -0
  76. package/dist/cjs/testing/schema/normalize.d.ts +7 -0
  77. package/dist/cjs/testing/schema/normalize.d.ts.map +1 -0
  78. package/dist/cjs/testing/schema/normalize.js +202 -0
  79. package/dist/cjs/testing/schema/normalize.js.map +1 -0
  80. package/dist/cjs/testing/schema/parser.d.ts +9 -0
  81. package/dist/cjs/testing/schema/parser.d.ts.map +1 -0
  82. package/dist/cjs/testing/schema/parser.js +80 -0
  83. package/dist/cjs/testing/schema/parser.js.map +1 -0
  84. package/dist/cjs/testing/schema/types.d.ts +63 -0
  85. package/dist/cjs/testing/schema/types.d.ts.map +1 -0
  86. package/dist/cjs/testing/schema/types.js +6 -0
  87. package/dist/cjs/testing/schema/types.js.map +1 -0
  88. package/dist/cjs/testing/sqlite/builder.d.ts +24 -0
  89. package/dist/cjs/testing/sqlite/builder.d.ts.map +1 -0
  90. package/dist/cjs/testing/sqlite/builder.js +134 -0
  91. package/dist/cjs/testing/sqlite/builder.js.map +1 -0
  92. package/dist/cjs/testing/sqlite/database.d.ts +18 -0
  93. package/dist/cjs/testing/sqlite/database.d.ts.map +1 -0
  94. package/dist/cjs/testing/sqlite/database.js +123 -0
  95. package/dist/cjs/testing/sqlite/database.js.map +1 -0
  96. package/dist/cjs/testing/sqlite/loadData.d.ts +7 -0
  97. package/dist/cjs/testing/sqlite/loadData.d.ts.map +1 -0
  98. package/dist/cjs/testing/sqlite/loadData.js +55 -0
  99. package/dist/cjs/testing/sqlite/loadData.js.map +1 -0
  100. package/dist/cjs/transactions.d.ts +14 -5
  101. package/dist/cjs/transactions.d.ts.map +1 -1
  102. package/dist/cjs/transactions.js +40 -24
  103. package/dist/cjs/transactions.js.map +1 -1
  104. package/dist/esm/codegen/generateManifest.js +20 -11
  105. package/dist/esm/codegen/generateManifest.js.map +1 -1
  106. package/dist/esm/core/resolver.js +59 -19
  107. package/dist/esm/core/resolver.js.map +1 -1
  108. package/dist/esm/core/sourceScanResolver.js +51 -0
  109. package/dist/esm/core/sourceScanResolver.js.map +1 -0
  110. package/dist/esm/decorators/query.js +278 -247
  111. package/dist/esm/decorators/query.js.map +1 -1
  112. package/dist/esm/families/sqliteFamily.js +151 -0
  113. package/dist/esm/families/sqliteFamily.js.map +1 -0
  114. package/dist/esm/family.js +202 -0
  115. package/dist/esm/family.js.map +1 -0
  116. package/dist/esm/familyBinding.js +18 -0
  117. package/dist/esm/familyBinding.js.map +1 -0
  118. package/dist/esm/index.js +10 -1
  119. package/dist/esm/index.js.map +1 -1
  120. package/dist/esm/ioc.js +32 -0
  121. package/dist/esm/ioc.js.map +1 -0
  122. package/dist/esm/run.js +34 -0
  123. package/dist/esm/run.js.map +1 -0
  124. package/dist/esm/runtime.js +94 -0
  125. package/dist/esm/runtime.js.map +1 -0
  126. package/dist/esm/stack.js +251 -0
  127. package/dist/esm/stack.js.map +1 -0
  128. package/dist/esm/testing/builder.js +95 -0
  129. package/dist/esm/testing/builder.js.map +1 -0
  130. package/dist/esm/testing/index.js +9 -0
  131. package/dist/esm/testing/index.js.map +1 -0
  132. package/dist/esm/testing/postgres/allocator.js +49 -0
  133. package/dist/esm/testing/postgres/allocator.js.map +1 -0
  134. package/dist/esm/testing/postgres/database.js +36 -0
  135. package/dist/esm/testing/postgres/database.js.map +1 -0
  136. package/dist/esm/testing/postgres/loadData.js +176 -0
  137. package/dist/esm/testing/postgres/loadData.js.map +1 -0
  138. package/dist/esm/testing/postgres/server.js +69 -0
  139. package/dist/esm/testing/postgres/server.js.map +1 -0
  140. package/dist/esm/testing/schema/ddl.js +65 -0
  141. package/dist/esm/testing/schema/ddl.js.map +1 -0
  142. package/dist/esm/testing/schema/normalize.js +164 -0
  143. package/dist/esm/testing/schema/normalize.js.map +1 -0
  144. package/dist/esm/testing/schema/parser.js +43 -0
  145. package/dist/esm/testing/schema/parser.js.map +1 -0
  146. package/dist/esm/testing/schema/types.js +5 -0
  147. package/dist/esm/testing/schema/types.js.map +1 -0
  148. package/dist/esm/testing/sqlite/builder.js +98 -0
  149. package/dist/esm/testing/sqlite/builder.js.map +1 -0
  150. package/dist/esm/testing/sqlite/database.js +85 -0
  151. package/dist/esm/testing/sqlite/database.js.map +1 -0
  152. package/dist/esm/testing/sqlite/loadData.js +48 -0
  153. package/dist/esm/testing/sqlite/loadData.js.map +1 -0
  154. package/dist/esm/transactions.js +39 -24
  155. package/dist/esm/transactions.js.map +1 -1
  156. package/package.json +19 -2
@@ -0,0 +1,131 @@
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.testPostgres = testPostgres;
37
+ const fs = __importStar(require("fs"));
38
+ const parser_1 = require("./schema/parser");
39
+ const ddl_1 = require("./schema/ddl");
40
+ const allocator_1 = require("./postgres/allocator");
41
+ const database_1 = require("./postgres/database");
42
+ const allocator_2 = require("./postgres/allocator");
43
+ const server_1 = require("./postgres/server");
44
+ const loadData_1 = require("./postgres/loadData");
45
+ function readRawSql(schema) {
46
+ return schema.sql.map((entry) => {
47
+ if (entry.kind === 'inline') {
48
+ return { label: `sql entry #${entry.index} (inline)`, sql: entry.sql };
49
+ }
50
+ try {
51
+ return {
52
+ label: `sql entry #${entry.index} (${entry.sourcePath})`,
53
+ sql: fs.readFileSync(entry.resolvedPath, 'utf8'),
54
+ };
55
+ }
56
+ catch (err) {
57
+ throw new server_1.TestPostgresServerError(`Cannot read raw SQL file for sql entry #${entry.index}: ${entry.resolvedPath} ` +
58
+ `(authored as "${entry.sourcePath}" in ${schema.source}): ${err.message}`);
59
+ }
60
+ });
61
+ }
62
+ function makeBuilder(state) {
63
+ return {
64
+ schema: state.schema,
65
+ ops: state.ops,
66
+ data(rows) {
67
+ const newOps = (0, loadData_1.toOperations)(state.schema, rows);
68
+ return makeBuilder({ ...state, ops: [...state.ops, ...newOps] });
69
+ },
70
+ async build() {
71
+ // Fail on pure planning errors (cycles, raw SQL file readability)
72
+ // before touching the server where possible.
73
+ const rawSql = readRawSql(state.schema);
74
+ const ddl = (0, ddl_1.generateDdl)(state.schema);
75
+ const effective = (0, loadData_1.composeRows)(state.schema, state.ops);
76
+ const allocated = await (0, allocator_1.allocateDatabase)(state.options);
77
+ let handle;
78
+ try {
79
+ handle = await (0, database_1.createDatabaseHandle)(allocated, state.options);
80
+ for (const statement of ddl) {
81
+ try {
82
+ await handle._pool.query(statement);
83
+ }
84
+ catch (err) {
85
+ throw new server_1.TestPostgresServerError(`Failed to apply structured DDL from ${state.schema.source}: ${err.message}\n` +
86
+ `Statement:\n${statement}`);
87
+ }
88
+ }
89
+ for (const entry of rawSql) {
90
+ try {
91
+ // Simple-query protocol: the pg driver executes multi-statement
92
+ // scripts directly when no parameters are passed.
93
+ await handle._pool.query(entry.sql);
94
+ }
95
+ catch (err) {
96
+ throw new server_1.TestPostgresServerError(`Failed to apply raw ${entry.label} from ${state.schema.source}: ${err.message}`);
97
+ }
98
+ }
99
+ await (0, loadData_1.insertRows)((sql, params) => handle._pool.query(sql, params), state.schema, effective);
100
+ return handle;
101
+ }
102
+ catch (err) {
103
+ // Clean up partially created owned resources.
104
+ if (handle) {
105
+ await handle._pool.end().catch(() => undefined);
106
+ }
107
+ await (0, allocator_2.dropDatabase)(allocated.name, state.options).catch(() => undefined);
108
+ throw err;
109
+ }
110
+ },
111
+ };
112
+ }
113
+ /**
114
+ * Persistent immutable test database builder.
115
+ *
116
+ * `schema` may be a parsed schema object, a YAML string, or a path to a
117
+ * .yml/.yaml file (raw .sql entries then resolve relative to that file's
118
+ * directory unless options.sourceDir overrides it).
119
+ */
120
+ function testPostgres(schema, options = {}) {
121
+ const normalized = isNormalizedSchema(schema) ? schema : (0, parser_1.parseTestSchema)(schema, options);
122
+ return makeBuilder({ schema: normalized, ops: [], options: Object.freeze({ ...options }) });
123
+ }
124
+ function isNormalizedSchema(input) {
125
+ return (!!input &&
126
+ typeof input === 'object' &&
127
+ Array.isArray(input.tables) &&
128
+ Array.isArray(input.sql) &&
129
+ typeof input.source === 'string');
130
+ }
131
+ //# sourceMappingURL=builder.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"builder.js","sourceRoot":"","sources":["../../../src/testing/builder.ts"],"names":[],"mappings":"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","sourcesContent":["import * as fs from 'fs';\nimport { parseTestSchema } from './schema/parser';\nimport { generateDdl } from './schema/ddl';\nimport type { NormalizedTestSchema, ParseTestSchemaOptions } from './schema/types';\nimport { allocateDatabase } from './postgres/allocator';\nimport { createDatabaseHandle, TestPostgresDatabase } from './postgres/database';\nimport { dropDatabase } from './postgres/allocator';\nimport { TestPostgresServerError, TestServerOptions } from './postgres/server';\nimport { composeRows, DataOperation, insertRows, Row, toOperations } from './postgres/loadData';\n\nexport interface TestPostgresOptions extends TestServerOptions, ParseTestSchemaOptions {}\n\nexport interface TestPostgresBuilder {\n /** The normalized immutable schema (useful for assertions/diagnostics). */\n readonly schema: NormalizedTestSchema;\n /** @internal Accumulated row operations (exposed for pure tests). */\n readonly ops: readonly DataOperation[];\n /** Returns a NEW derived builder; this builder is unchanged. */\n data(rows: Record<string, Row[]>): TestPostgresBuilder;\n /**\n * Create a fresh isolated PostgreSQL database, apply DDL + raw SQL,\n * insert effective scenario rows, and return the handle. Does not\n * consume the builder: building twice yields two independent databases.\n */\n build(): Promise<TestPostgresDatabase>;\n}\n\ninterface BuilderState {\n readonly schema: NormalizedTestSchema;\n readonly ops: readonly DataOperation[];\n readonly options: Readonly<TestPostgresOptions>;\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 TestPostgresServerError(\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\nfunction makeBuilder(state: BuilderState): TestPostgresBuilder {\n return {\n schema: state.schema,\n ops: state.ops,\n data(rows: Record<string, Row[]>): TestPostgresBuilder {\n const newOps = toOperations(state.schema, rows);\n return makeBuilder({ ...state, ops: [...state.ops, ...newOps] });\n },\n async build(): Promise<TestPostgresDatabase> {\n // Fail on pure planning errors (cycles, raw SQL file readability)\n // before touching the server where possible.\n const rawSql = readRawSql(state.schema);\n const ddl = generateDdl(state.schema);\n const effective = composeRows(state.schema, state.ops);\n\n const allocated = await allocateDatabase(state.options);\n let handle: (TestPostgresDatabase & { _pool: any }) | undefined;\n try {\n handle = await createDatabaseHandle(allocated, state.options);\n for (const statement of ddl) {\n try {\n await handle._pool.query(statement);\n } catch (err) {\n throw new TestPostgresServerError(\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 // Simple-query protocol: the pg driver executes multi-statement\n // scripts directly when no parameters are passed.\n await handle._pool.query(entry.sql);\n } catch (err) {\n throw new TestPostgresServerError(\n `Failed to apply raw ${entry.label} from ${state.schema.source}: ${(err as Error).message}`\n );\n }\n }\n await insertRows((sql, params) => handle!._pool.query(sql, params), state.schema, effective);\n return handle;\n } catch (err) {\n // Clean up partially created owned resources.\n if (handle) {\n await handle._pool.end().catch(() => undefined);\n }\n await dropDatabase(allocated.name, state.options).catch(() => undefined);\n throw err;\n }\n },\n };\n}\n\n/**\n * Persistent immutable test database builder.\n *\n * `schema` may be a parsed schema object, a YAML string, or a path to a\n * .yml/.yaml file (raw .sql entries then resolve relative to that file's\n * directory unless options.sourceDir overrides it).\n */\nexport function testPostgres(\n schema: string | object,\n options: TestPostgresOptions = {}\n): TestPostgresBuilder {\n const normalized =\n isNormalizedSchema(schema) ? schema : parseTestSchema(schema as string | object, options);\n return makeBuilder({ schema: normalized, ops: [], options: Object.freeze({ ...options }) });\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,14 @@
1
+ export { testPostgres } from './builder';
2
+ export type { TestPostgresBuilder, TestPostgresOptions } from './builder';
3
+ export type { TestPostgresDatabase } from './postgres/database';
4
+ export { testSqlite } from './sqlite/builder';
5
+ export type { TestSqliteBuilder, TestSqliteOptions } from './sqlite/builder';
6
+ export { TestSqliteError } from './sqlite/database';
7
+ export type { TestSqliteDatabase } from './sqlite/database';
8
+ export { parseTestSchema } from './schema/parser';
9
+ export { normalizeTestSchema, TestSchemaError } from './schema/normalize';
10
+ export { generateDdl } from './schema/ddl';
11
+ export { TestDataError } from './postgres/loadData';
12
+ export { TestPostgresServerError, DEFAULT_ADMIN_URL } from './postgres/server';
13
+ export type { NormalizedTestSchema, NormalizedTable, NormalizedColumn, RawSqlEntry, ParseTestSchemaOptions, } from './schema/types';
14
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/testing/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AACzC,YAAY,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAC;AAC1E,YAAY,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAChE,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,YAAY,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAC7E,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,YAAY,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAC5D,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAClD,OAAO,EAAE,mBAAmB,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAC1E,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,uBAAuB,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAC/E,YAAY,EACV,oBAAoB,EACpB,eAAe,EACf,gBAAgB,EAChB,WAAW,EACX,sBAAsB,GACvB,MAAM,gBAAgB,CAAC"}
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DEFAULT_ADMIN_URL = exports.TestPostgresServerError = exports.TestDataError = exports.generateDdl = exports.TestSchemaError = exports.normalizeTestSchema = exports.parseTestSchema = exports.TestSqliteError = exports.testSqlite = exports.testPostgres = void 0;
4
+ var builder_1 = require("./builder");
5
+ Object.defineProperty(exports, "testPostgres", { enumerable: true, get: function () { return builder_1.testPostgres; } });
6
+ var builder_2 = require("./sqlite/builder");
7
+ Object.defineProperty(exports, "testSqlite", { enumerable: true, get: function () { return builder_2.testSqlite; } });
8
+ var database_1 = require("./sqlite/database");
9
+ Object.defineProperty(exports, "TestSqliteError", { enumerable: true, get: function () { return database_1.TestSqliteError; } });
10
+ var parser_1 = require("./schema/parser");
11
+ Object.defineProperty(exports, "parseTestSchema", { enumerable: true, get: function () { return parser_1.parseTestSchema; } });
12
+ var normalize_1 = require("./schema/normalize");
13
+ Object.defineProperty(exports, "normalizeTestSchema", { enumerable: true, get: function () { return normalize_1.normalizeTestSchema; } });
14
+ Object.defineProperty(exports, "TestSchemaError", { enumerable: true, get: function () { return normalize_1.TestSchemaError; } });
15
+ var ddl_1 = require("./schema/ddl");
16
+ Object.defineProperty(exports, "generateDdl", { enumerable: true, get: function () { return ddl_1.generateDdl; } });
17
+ var loadData_1 = require("./postgres/loadData");
18
+ Object.defineProperty(exports, "TestDataError", { enumerable: true, get: function () { return loadData_1.TestDataError; } });
19
+ var server_1 = require("./postgres/server");
20
+ Object.defineProperty(exports, "TestPostgresServerError", { enumerable: true, get: function () { return server_1.TestPostgresServerError; } });
21
+ Object.defineProperty(exports, "DEFAULT_ADMIN_URL", { enumerable: true, get: function () { return server_1.DEFAULT_ADMIN_URL; } });
22
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/testing/index.ts"],"names":[],"mappings":";;;AAAA,qCAAyC;AAAhC,uGAAA,YAAY,OAAA;AAGrB,4CAA8C;AAArC,qGAAA,UAAU,OAAA;AAEnB,8CAAoD;AAA3C,2GAAA,eAAe,OAAA;AAExB,0CAAkD;AAAzC,yGAAA,eAAe,OAAA;AACxB,gDAA0E;AAAjE,gHAAA,mBAAmB,OAAA;AAAE,4GAAA,eAAe,OAAA;AAC7C,oCAA2C;AAAlC,kGAAA,WAAW,OAAA;AACpB,gDAAoD;AAA3C,yGAAA,aAAa,OAAA;AACtB,4CAA+E;AAAtE,iHAAA,uBAAuB,OAAA;AAAE,2GAAA,iBAAiB,OAAA","sourcesContent":["export { testPostgres } from './builder';\nexport type { TestPostgresBuilder, TestPostgresOptions } from './builder';\nexport type { TestPostgresDatabase } from './postgres/database';\nexport { testSqlite } from './sqlite/builder';\nexport type { TestSqliteBuilder, TestSqliteOptions } from './sqlite/builder';\nexport { TestSqliteError } from './sqlite/database';\nexport type { TestSqliteDatabase } from './sqlite/database';\nexport { parseTestSchema } from './schema/parser';\nexport { normalizeTestSchema, TestSchemaError } from './schema/normalize';\nexport { generateDdl } from './schema/ddl';\nexport { TestDataError } from './postgres/loadData';\nexport { TestPostgresServerError, DEFAULT_ADMIN_URL } from './postgres/server';\nexport type {\n NormalizedTestSchema,\n NormalizedTable,\n NormalizedColumn,\n RawSqlEntry,\n ParseTestSchemaOptions,\n} from './schema/types';\n"]}
@@ -0,0 +1,17 @@
1
+ import { TestServerOptions } from './server';
2
+ /**
3
+ * Unique, safe, bounded-length database name. Randomness plus a process
4
+ * counter keeps parallel builds (including across worker processes)
5
+ * collision-free. Always well under PostgreSQL's 63-byte identifier limit.
6
+ */
7
+ export declare function nextDatabaseName(): string;
8
+ export interface AllocatedDatabase {
9
+ readonly name: string;
10
+ readonly url: string;
11
+ readonly adminUrl: string;
12
+ }
13
+ /** Create a fresh uniquely named database on the configured test server. */
14
+ export declare function allocateDatabase(options?: TestServerOptions): Promise<AllocatedDatabase>;
15
+ /** Drop exactly the named database. Safe to call for an already-dropped db. */
16
+ export declare function dropDatabase(name: string, options?: TestServerOptions): Promise<void>;
17
+ //# sourceMappingURL=allocator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"allocator.d.ts","sourceRoot":"","sources":["../../../../src/testing/postgres/allocator.ts"],"names":[],"mappings":"AACA,OAAO,EAKL,iBAAiB,EAElB,MAAM,UAAU,CAAC;AAKlB;;;;GAIG;AACH,wBAAgB,gBAAgB,IAAI,MAAM,CAGzC;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B;AAED,4EAA4E;AAC5E,wBAAsB,gBAAgB,CAAC,OAAO,GAAE,iBAAsB,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAgBlG;AAED,+EAA+E;AAC/E,wBAAsB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,iBAAsB,GAAG,OAAO,CAAC,IAAI,CAAC,CAiB/F"}
@@ -0,0 +1,54 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.nextDatabaseName = nextDatabaseName;
4
+ exports.allocateDatabase = allocateDatabase;
5
+ exports.dropDatabase = dropDatabase;
6
+ const crypto_1 = require("crypto");
7
+ const server_1 = require("./server");
8
+ const NAME_PREFIX = 'sqlstack_test_';
9
+ let counter = 0;
10
+ /**
11
+ * Unique, safe, bounded-length database name. Randomness plus a process
12
+ * counter keeps parallel builds (including across worker processes)
13
+ * collision-free. Always well under PostgreSQL's 63-byte identifier limit.
14
+ */
15
+ function nextDatabaseName() {
16
+ counter += 1;
17
+ return `${NAME_PREFIX}${(0, crypto_1.randomBytes)(4).toString('hex')}_${counter}`;
18
+ }
19
+ /** Create a fresh uniquely named database on the configured test server. */
20
+ async function allocateDatabase(options = {}) {
21
+ const adminUrl = (0, server_1.resolveAdminUrl)(options);
22
+ const name = nextDatabaseName();
23
+ const admin = await (0, server_1.connectAdmin)(options);
24
+ try {
25
+ await admin.query(`CREATE DATABASE "${name}"`);
26
+ }
27
+ catch (err) {
28
+ throw new server_1.TestPostgresServerError(`Failed to create test database "${name}" on ${(0, server_1.redact)(adminUrl)}: ${err.message}\n` +
29
+ `The admin connection must have CREATE DATABASE privilege ` +
30
+ `(set SQLSTACK_TEST_PG_URL or options.adminUrl; start the bundled server with npm run db:pg:up).`);
31
+ }
32
+ finally {
33
+ await admin.end().catch(() => undefined);
34
+ }
35
+ return { name, url: (0, server_1.withDatabase)(adminUrl, name), adminUrl };
36
+ }
37
+ /** Drop exactly the named database. Safe to call for an already-dropped db. */
38
+ async function dropDatabase(name, options = {}) {
39
+ if (!name.startsWith(NAME_PREFIX)) {
40
+ throw new server_1.TestPostgresServerError(`Refusing to drop database "${name}": not an sqlstack test database (expected prefix ${NAME_PREFIX})`);
41
+ }
42
+ const admin = await (0, server_1.connectAdmin)(options);
43
+ try {
44
+ // FORCE (PG 13+) terminates lingering sessions so parallel teardown is robust.
45
+ await admin.query(`DROP DATABASE IF EXISTS "${name}" WITH (FORCE)`);
46
+ }
47
+ catch (err) {
48
+ throw new server_1.TestPostgresServerError(`Failed to drop test database "${name}" on ${(0, server_1.redact)((0, server_1.resolveAdminUrl)(options))}: ${err.message}`);
49
+ }
50
+ finally {
51
+ await admin.end().catch(() => undefined);
52
+ }
53
+ }
54
+ //# sourceMappingURL=allocator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"allocator.js","sourceRoot":"","sources":["../../../../src/testing/postgres/allocator.ts"],"names":[],"mappings":";;AAkBA,4CAGC;AASD,4CAgBC;AAGD,oCAiBC;AAlED,mCAAqC;AACrC,qCAOkB;AAElB,MAAM,WAAW,GAAG,gBAAgB,CAAC;AACrC,IAAI,OAAO,GAAG,CAAC,CAAC;AAEhB;;;;GAIG;AACH,SAAgB,gBAAgB;IAC9B,OAAO,IAAI,CAAC,CAAC;IACb,OAAO,GAAG,WAAW,GAAG,IAAA,oBAAW,EAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,OAAO,EAAE,CAAC;AACtE,CAAC;AAQD,4EAA4E;AACrE,KAAK,UAAU,gBAAgB,CAAC,UAA6B,EAAE;IACpE,MAAM,QAAQ,GAAG,IAAA,wBAAe,EAAC,OAAO,CAAC,CAAC;IAC1C,MAAM,IAAI,GAAG,gBAAgB,EAAE,CAAC;IAChC,MAAM,KAAK,GAAG,MAAM,IAAA,qBAAY,EAAC,OAAO,CAAC,CAAC;IAC1C,IAAI,CAAC;QACH,MAAM,KAAK,CAAC,KAAK,CAAC,oBAAoB,IAAI,GAAG,CAAC,CAAC;IACjD,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,IAAI,gCAAuB,CAC/B,mCAAmC,IAAI,QAAQ,IAAA,eAAM,EAAC,QAAQ,CAAC,KAAM,GAAa,CAAC,OAAO,IAAI;YAC5F,2DAA2D;YAC3D,iGAAiG,CACpG,CAAC;IACJ,CAAC;YAAS,CAAC;QACT,MAAM,KAAK,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;IAC3C,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,IAAA,qBAAY,EAAC,QAAQ,EAAE,IAAI,CAAC,EAAE,QAAQ,EAAE,CAAC;AAC/D,CAAC;AAED,+EAA+E;AACxE,KAAK,UAAU,YAAY,CAAC,IAAY,EAAE,UAA6B,EAAE;IAC9E,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,WAAW,CAAC,EAAE,CAAC;QAClC,MAAM,IAAI,gCAAuB,CAC/B,8BAA8B,IAAI,qDAAqD,WAAW,GAAG,CACtG,CAAC;IACJ,CAAC;IACD,MAAM,KAAK,GAAG,MAAM,IAAA,qBAAY,EAAC,OAAO,CAAC,CAAC;IAC1C,IAAI,CAAC;QACH,+EAA+E;QAC/E,MAAM,KAAK,CAAC,KAAK,CAAC,4BAA4B,IAAI,gBAAgB,CAAC,CAAC;IACtE,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,IAAI,gCAAuB,CAC/B,iCAAiC,IAAI,QAAQ,IAAA,eAAM,EAAC,IAAA,wBAAe,EAAC,OAAO,CAAC,CAAC,KAAM,GAAa,CAAC,OAAO,EAAE,CAC3G,CAAC;IACJ,CAAC;YAAS,CAAC;QACT,MAAM,KAAK,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;IAC3C,CAAC;AACH,CAAC","sourcesContent":["import { randomBytes } from 'crypto';\nimport {\n connectAdmin,\n redact,\n resolveAdminUrl,\n TestPostgresServerError,\n TestServerOptions,\n withDatabase,\n} from './server';\n\nconst NAME_PREFIX = 'sqlstack_test_';\nlet counter = 0;\n\n/**\n * Unique, safe, bounded-length database name. Randomness plus a process\n * counter keeps parallel builds (including across worker processes)\n * collision-free. Always well under PostgreSQL's 63-byte identifier limit.\n */\nexport function nextDatabaseName(): string {\n counter += 1;\n return `${NAME_PREFIX}${randomBytes(4).toString('hex')}_${counter}`;\n}\n\nexport interface AllocatedDatabase {\n readonly name: string;\n readonly url: string;\n readonly adminUrl: string;\n}\n\n/** Create a fresh uniquely named database on the configured test server. */\nexport async function allocateDatabase(options: TestServerOptions = {}): Promise<AllocatedDatabase> {\n const adminUrl = resolveAdminUrl(options);\n const name = nextDatabaseName();\n const admin = await connectAdmin(options);\n try {\n await admin.query(`CREATE DATABASE \"${name}\"`);\n } catch (err) {\n throw new TestPostgresServerError(\n `Failed to create test database \"${name}\" on ${redact(adminUrl)}: ${(err as Error).message}\\n` +\n `The admin connection must have CREATE DATABASE privilege ` +\n `(set SQLSTACK_TEST_PG_URL or options.adminUrl; start the bundled server with npm run db:pg:up).`\n );\n } finally {\n await admin.end().catch(() => undefined);\n }\n return { name, url: withDatabase(adminUrl, name), adminUrl };\n}\n\n/** Drop exactly the named database. Safe to call for an already-dropped db. */\nexport async function dropDatabase(name: string, options: TestServerOptions = {}): Promise<void> {\n if (!name.startsWith(NAME_PREFIX)) {\n throw new TestPostgresServerError(\n `Refusing to drop database \"${name}\": not an sqlstack test database (expected prefix ${NAME_PREFIX})`\n );\n }\n const admin = await connectAdmin(options);\n try {\n // FORCE (PG 13+) terminates lingering sessions so parallel teardown is robust.\n await admin.query(`DROP DATABASE IF EXISTS \"${name}\" WITH (FORCE)`);\n } catch (err) {\n throw new TestPostgresServerError(\n `Failed to drop test database \"${name}\" on ${redact(resolveAdminUrl(options))}: ${(err as Error).message}`\n );\n } finally {\n await admin.end().catch(() => undefined);\n }\n}\n"]}
@@ -0,0 +1,21 @@
1
+ import { TestServerOptions } from './server';
2
+ /** The built test database handle. */
3
+ export interface TestPostgresDatabase {
4
+ /** Normal PostgreSQL connection string identifying only this database. */
5
+ readonly url: string;
6
+ query(sql: string, params?: readonly unknown[]): Promise<unknown>;
7
+ /** Close helper connections and drop the owned database. Idempotent. */
8
+ dispose(): Promise<void>;
9
+ }
10
+ export interface OwnedDatabase {
11
+ readonly name: string;
12
+ readonly url: string;
13
+ }
14
+ /**
15
+ * Connect a helper pool to an allocated database and produce the handle.
16
+ * The handle owns exactly this database and drops it on dispose.
17
+ */
18
+ export declare function createDatabaseHandle(owned: OwnedDatabase, options?: TestServerOptions): Promise<TestPostgresDatabase & {
19
+ _pool: any;
20
+ }>;
21
+ //# sourceMappingURL=database.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"database.d.ts","sourceRoot":"","sources":["../../../../src/testing/postgres/database.ts"],"names":[],"mappings":"AACA,OAAO,EAAmC,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAE9E,sCAAsC;AACtC,MAAM,WAAW,oBAAoB;IACnC,0EAA0E;IAC1E,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,SAAS,OAAO,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAClE,wEAAwE;IACxE,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAC1B;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;CACtB;AAED;;;GAGG;AACH,wBAAsB,oBAAoB,CACxC,KAAK,EAAE,aAAa,EACpB,OAAO,GAAE,iBAAsB,GAC9B,OAAO,CAAC,oBAAoB,GAAG;IAAE,KAAK,EAAE,GAAG,CAAA;CAAE,CAAC,CA4BhD"}
@@ -0,0 +1,39 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createDatabaseHandle = createDatabaseHandle;
4
+ const allocator_1 = require("./allocator");
5
+ const server_1 = require("./server");
6
+ /**
7
+ * Connect a helper pool to an allocated database and produce the handle.
8
+ * The handle owns exactly this database and drops it on dispose.
9
+ */
10
+ async function createDatabaseHandle(owned, options = {}) {
11
+ const pg = await (0, server_1.loadPg)();
12
+ const Pool = pg.Pool ?? pg.default?.Pool;
13
+ const pool = new Pool({ connectionString: owned.url, max: 4 });
14
+ let disposed = false;
15
+ return {
16
+ url: owned.url,
17
+ _pool: pool,
18
+ async query(sql, params = []) {
19
+ if (disposed) {
20
+ throw new server_1.TestPostgresServerError(`Test database "${owned.name}" has been disposed; no further queries are possible.`);
21
+ }
22
+ const res = await pool.query(sql, params);
23
+ return res.rows;
24
+ },
25
+ async dispose() {
26
+ if (disposed)
27
+ return;
28
+ disposed = true;
29
+ try {
30
+ await pool.end();
31
+ }
32
+ catch {
33
+ /* pool may already be closed */
34
+ }
35
+ await (0, allocator_1.dropDatabase)(owned.name, options);
36
+ },
37
+ };
38
+ }
39
+ //# sourceMappingURL=database.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"database.js","sourceRoot":"","sources":["../../../../src/testing/postgres/database.ts"],"names":[],"mappings":";;AAqBA,oDA+BC;AApDD,2CAA2C;AAC3C,qCAA8E;AAgB9E;;;GAGG;AACI,KAAK,UAAU,oBAAoB,CACxC,KAAoB,EACpB,UAA6B,EAAE;IAE/B,MAAM,EAAE,GAAG,MAAM,IAAA,eAAM,GAAE,CAAC;IAC1B,MAAM,IAAI,GAAG,EAAE,CAAC,IAAI,IAAI,EAAE,CAAC,OAAO,EAAE,IAAI,CAAC;IACzC,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,EAAE,gBAAgB,EAAE,KAAK,CAAC,GAAG,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC;IAC/D,IAAI,QAAQ,GAAG,KAAK,CAAC;IACrB,OAAO;QACL,GAAG,EAAE,KAAK,CAAC,GAAG;QACd,KAAK,EAAE,IAAI;QACX,KAAK,CAAC,KAAK,CAAC,GAAW,EAAE,SAA6B,EAAE;YACtD,IAAI,QAAQ,EAAE,CAAC;gBACb,MAAM,IAAI,gCAAuB,CAC/B,kBAAkB,KAAK,CAAC,IAAI,uDAAuD,CACpF,CAAC;YACJ,CAAC;YACD,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,EAAE,MAAmB,CAAC,CAAC;YACvD,OAAO,GAAG,CAAC,IAAI,CAAC;QAClB,CAAC;QACD,KAAK,CAAC,OAAO;YACX,IAAI,QAAQ;gBAAE,OAAO;YACrB,QAAQ,GAAG,IAAI,CAAC;YAChB,IAAI,CAAC;gBACH,MAAM,IAAI,CAAC,GAAG,EAAE,CAAC;YACnB,CAAC;YAAC,MAAM,CAAC;gBACP,gCAAgC;YAClC,CAAC;YACD,MAAM,IAAA,wBAAY,EAAC,KAAK,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;QAC1C,CAAC;KACF,CAAC;AACJ,CAAC","sourcesContent":["import { dropDatabase } from './allocator';\nimport { loadPg, TestPostgresServerError, TestServerOptions } from './server';\n\n/** The built test database handle. */\nexport interface TestPostgresDatabase {\n /** Normal PostgreSQL connection string identifying only this database. */\n readonly url: string;\n query(sql: string, params?: readonly unknown[]): Promise<unknown>;\n /** Close helper connections and drop the owned database. Idempotent. */\n dispose(): Promise<void>;\n}\n\nexport interface OwnedDatabase {\n readonly name: string;\n readonly url: string;\n}\n\n/**\n * Connect a helper pool to an allocated database and produce the handle.\n * The handle owns exactly this database and drops it on dispose.\n */\nexport async function createDatabaseHandle(\n owned: OwnedDatabase,\n options: TestServerOptions = {}\n): Promise<TestPostgresDatabase & { _pool: any }> {\n const pg = await loadPg();\n const Pool = pg.Pool ?? pg.default?.Pool;\n const pool = new Pool({ connectionString: owned.url, max: 4 });\n let disposed = false;\n return {\n url: owned.url,\n _pool: pool,\n async query(sql: string, params: readonly unknown[] = []) {\n if (disposed) {\n throw new TestPostgresServerError(\n `Test database \"${owned.name}\" has been disposed; no further queries are possible.`\n );\n }\n const res = await pool.query(sql, params as unknown[]);\n return res.rows;\n },\n async dispose() {\n if (disposed) return;\n disposed = true;\n try {\n await pool.end();\n } catch {\n /* pool may already be closed */\n }\n await dropDatabase(owned.name, options);\n },\n };\n}\n"]}
@@ -0,0 +1,31 @@
1
+ import { SqlStackError } from '../../core/errors';
2
+ import type { NormalizedTestSchema } from '../schema/types';
3
+ export declare class TestDataError extends SqlStackError {
4
+ constructor(message: string);
5
+ }
6
+ export type Row = Record<string, unknown>;
7
+ export interface DataOperation {
8
+ readonly table: string;
9
+ readonly row: Readonly<Row>;
10
+ }
11
+ /** Validate one `.data()` payload against the structured schema (fail-fast). */
12
+ export declare function toOperations(schema: NormalizedTestSchema, data: Record<string, Row[]>): DataOperation[];
13
+ /**
14
+ * Compose row operations into effective rows per table.
15
+ * PK tables: identity = ordered PK values; later writes replace, first
16
+ * occurrence keeps its position. No-PK tables: append in authored order.
17
+ */
18
+ export declare function composeRows(schema: NormalizedTestSchema, ops: readonly DataOperation[]): Map<string, Row[]>;
19
+ /**
20
+ * Deterministic insertion order for the tables that actually have rows,
21
+ * topologically sorted by declared references between those tables.
22
+ * Cycles fail with actionable guidance.
23
+ */
24
+ export declare function planInsertOrder(schema: NormalizedTestSchema, tablesWithRows: readonly string[]): string[];
25
+ /**
26
+ * Insert effective rows. Parameterized and batched per table by column
27
+ * signature; missing columns are omitted from the INSERT so PostgreSQL
28
+ * defaults apply, while explicit null is sent as NULL.
29
+ */
30
+ export declare function insertRows(query: (sql: string, params: unknown[]) => Promise<unknown>, schema: NormalizedTestSchema, effective: Map<string, Row[]>): Promise<void>;
31
+ //# sourceMappingURL=loadData.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loadData.d.ts","sourceRoot":"","sources":["../../../../src/testing/postgres/loadData.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAElD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AAE5D,qBAAa,aAAc,SAAQ,aAAa;gBAClC,OAAO,EAAE,MAAM;CAI5B;AAED,MAAM,MAAM,GAAG,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAE1C,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,GAAG,EAAE,QAAQ,CAAC,GAAG,CAAC,CAAC;CAC7B;AAED,gFAAgF;AAChF,wBAAgB,YAAY,CAAC,MAAM,EAAE,oBAAoB,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,GAAG,aAAa,EAAE,CAwCvG;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CACzB,MAAM,EAAE,oBAAoB,EAC5B,GAAG,EAAE,SAAS,aAAa,EAAE,GAC5B,GAAG,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,CA8BpB;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,oBAAoB,EAAE,cAAc,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,EAAE,CAqCzG;AAED;;;;GAIG;AACH,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,CAuCf"}
@@ -0,0 +1,184 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.TestDataError = void 0;
4
+ exports.toOperations = toOperations;
5
+ exports.composeRows = composeRows;
6
+ exports.planInsertOrder = planInsertOrder;
7
+ exports.insertRows = insertRows;
8
+ const errors_1 = require("../../core/errors");
9
+ const ddl_1 = require("../schema/ddl");
10
+ class TestDataError extends errors_1.SqlStackError {
11
+ constructor(message) {
12
+ super(message);
13
+ this.name = 'TestDataError';
14
+ }
15
+ }
16
+ exports.TestDataError = TestDataError;
17
+ /** Validate one `.data()` payload against the structured schema (fail-fast). */
18
+ function toOperations(schema, data) {
19
+ const ops = [];
20
+ const tables = new Map(schema.tables.map((t) => [t.name, t]));
21
+ for (const [tableName, rows] of Object.entries(data)) {
22
+ const table = tables.get(tableName);
23
+ if (!table) {
24
+ const known = schema.tables.map((t) => t.name).join(', ') || '(none)';
25
+ throw new TestDataError(`.data(): unknown table "${tableName}". Structured tables declared in ${schema.source}: ${known}. ` +
26
+ `Tables created only through raw SQL cannot receive .data() rows; insert them via raw SQL instead.`);
27
+ }
28
+ if (!Array.isArray(rows)) {
29
+ throw new TestDataError(`.data(): rows for table "${tableName}" must be an array`);
30
+ }
31
+ const columnNames = new Set(table.columns.map((c) => c.name));
32
+ rows.forEach((row, i) => {
33
+ if (!row || typeof row !== 'object' || Array.isArray(row)) {
34
+ throw new TestDataError(`.data(): row #${i} for table "${tableName}" must be an object`);
35
+ }
36
+ for (const key of Object.keys(row)) {
37
+ if (!columnNames.has(key)) {
38
+ throw new TestDataError(`.data(): unknown column "${key}" in row #${i} for table "${tableName}" ` +
39
+ `(declared columns: ${[...columnNames].join(', ')})`);
40
+ }
41
+ }
42
+ for (const pk of table.primaryKey) {
43
+ if (row[pk] === undefined) {
44
+ throw new TestDataError(`.data(): row #${i} for table "${tableName}" is missing primary-key column "${pk}" ` +
45
+ `(primary key: ${table.primaryKey.join(', ')})`);
46
+ }
47
+ }
48
+ ops.push({ table: tableName, row });
49
+ });
50
+ }
51
+ return ops;
52
+ }
53
+ /**
54
+ * Compose row operations into effective rows per table.
55
+ * PK tables: identity = ordered PK values; later writes replace, first
56
+ * occurrence keeps its position. No-PK tables: append in authored order.
57
+ */
58
+ function composeRows(schema, ops) {
59
+ const tables = new Map(schema.tables.map((t) => [t.name, t]));
60
+ const result = new Map();
61
+ const identityIndex = new Map();
62
+ for (const op of ops) {
63
+ const table = tables.get(op.table);
64
+ let rows = result.get(op.table);
65
+ if (!rows) {
66
+ rows = [];
67
+ result.set(op.table, rows);
68
+ }
69
+ if (table.primaryKey.length === 0) {
70
+ rows.push(op.row);
71
+ continue;
72
+ }
73
+ const identity = JSON.stringify(table.primaryKey.map((pk) => op.row[pk]));
74
+ let index = identityIndex.get(op.table);
75
+ if (!index) {
76
+ index = new Map();
77
+ identityIndex.set(op.table, index);
78
+ }
79
+ const existing = index.get(identity);
80
+ if (existing === undefined) {
81
+ index.set(identity, rows.length);
82
+ rows.push(op.row);
83
+ }
84
+ else {
85
+ rows[existing] = op.row;
86
+ }
87
+ }
88
+ return result;
89
+ }
90
+ /**
91
+ * Deterministic insertion order for the tables that actually have rows,
92
+ * topologically sorted by declared references between those tables.
93
+ * Cycles fail with actionable guidance.
94
+ */
95
+ function planInsertOrder(schema, tablesWithRows) {
96
+ const inPlan = new Set(tablesWithRows);
97
+ const tables = schema.tables.filter((t) => inPlan.has(t.name));
98
+ // deps[t] = set of tables (with rows) that must be inserted before t
99
+ const deps = new Map();
100
+ for (const t of tables) {
101
+ const set = new Set();
102
+ for (const c of t.columns) {
103
+ if (c.references && c.references.table !== t.name && inPlan.has(c.references.table)) {
104
+ set.add(c.references.table);
105
+ }
106
+ }
107
+ deps.set(t.name, set);
108
+ }
109
+ const order = [];
110
+ const done = new Set();
111
+ let progressed = true;
112
+ while (progressed && order.length < tables.length) {
113
+ progressed = false;
114
+ for (const t of tables) {
115
+ if (done.has(t.name))
116
+ continue;
117
+ const pending = [...deps.get(t.name)].filter((d) => !done.has(d));
118
+ if (pending.length === 0) {
119
+ order.push(t.name);
120
+ done.add(t.name);
121
+ progressed = true;
122
+ }
123
+ }
124
+ }
125
+ if (order.length < tables.length) {
126
+ const cycle = tables.filter((t) => !done.has(t.name)).map((t) => t.name);
127
+ throw new TestDataError(`Cannot derive an insertion order: declared references between tables [${cycle.join(', ')}] form a cycle. ` +
128
+ `Insert this data via a raw "sql" entry (e.g. with deferred constraints), or restructure the references.`);
129
+ }
130
+ return order;
131
+ }
132
+ /**
133
+ * Insert effective rows. Parameterized and batched per table by column
134
+ * signature; missing columns are omitted from the INSERT so PostgreSQL
135
+ * defaults apply, while explicit null is sent as NULL.
136
+ */
137
+ async function insertRows(query, schema, effective) {
138
+ const order = planInsertOrder(schema, [...effective.keys()]);
139
+ const declared = new Map(schema.tables.map((t) => [t.name, t.columns.map((c) => c.name)]));
140
+ for (const tableName of order) {
141
+ const rows = effective.get(tableName);
142
+ const columnOrder = declared.get(tableName);
143
+ // Group consecutive-compatible rows into batches sharing a column signature.
144
+ const batches = new Map();
145
+ for (const row of rows) {
146
+ const columns = columnOrder.filter((c) => row[c] !== undefined);
147
+ const sig = columns.join(',');
148
+ let batch = batches.get(sig);
149
+ if (!batch) {
150
+ batch = { columns, rows: [] };
151
+ batches.set(sig, batch);
152
+ }
153
+ batch.rows.push(row);
154
+ }
155
+ for (const { columns, rows: batchRows } of batches.values()) {
156
+ if (columns.length === 0) {
157
+ for (let i = 0; i < batchRows.length; i++) {
158
+ await run(query, tableName, `INSERT INTO ${(0, ddl_1.quoteIdent)(tableName)} DEFAULT VALUES`, []);
159
+ }
160
+ continue;
161
+ }
162
+ const params = [];
163
+ const tuples = batchRows.map((row) => {
164
+ const placeholders = columns.map((c) => {
165
+ params.push(row[c]);
166
+ return `$${params.length}`;
167
+ });
168
+ return `(${placeholders.join(', ')})`;
169
+ });
170
+ const sql = `INSERT INTO ${(0, ddl_1.quoteIdent)(tableName)} (${columns.map(ddl_1.quoteIdent).join(', ')}) ` +
171
+ `VALUES ${tuples.join(', ')}`;
172
+ await run(query, tableName, sql, params);
173
+ }
174
+ }
175
+ }
176
+ async function run(query, tableName, sql, params) {
177
+ try {
178
+ await query(sql, params);
179
+ }
180
+ catch (err) {
181
+ throw new TestDataError(`Failed to insert scenario rows into table "${tableName}": ${err.message}`);
182
+ }
183
+ }
184
+ //# sourceMappingURL=loadData.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loadData.js","sourceRoot":"","sources":["../../../../src/testing/postgres/loadData.ts"],"names":[],"mappings":"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","sourcesContent":["import { SqlStackError } from '../../core/errors';\nimport { quoteIdent } from '../schema/ddl';\nimport type { NormalizedTestSchema } from '../schema/types';\n\nexport class TestDataError extends SqlStackError {\n constructor(message: string) {\n super(message);\n this.name = 'TestDataError';\n }\n}\n\nexport type Row = Record<string, unknown>;\n\nexport interface DataOperation {\n readonly table: string;\n readonly row: Readonly<Row>;\n}\n\n/** Validate one `.data()` payload against the structured schema (fail-fast). */\nexport function toOperations(schema: NormalizedTestSchema, data: Record<string, Row[]>): DataOperation[] {\n const ops: DataOperation[] = [];\n const tables = new Map(schema.tables.map((t) => [t.name, t]));\n for (const [tableName, rows] of Object.entries(data)) {\n const table = tables.get(tableName);\n if (!table) {\n const known = schema.tables.map((t) => t.name).join(', ') || '(none)';\n throw new TestDataError(\n `.data(): unknown table \"${tableName}\". Structured tables declared in ${schema.source}: ${known}. ` +\n `Tables created only through raw SQL cannot receive .data() rows; insert them via raw SQL instead.`\n );\n }\n if (!Array.isArray(rows)) {\n throw new TestDataError(`.data(): rows for table \"${tableName}\" must be an array`);\n }\n const columnNames = new Set(table.columns.map((c) => c.name));\n rows.forEach((row, i) => {\n if (!row || typeof row !== 'object' || Array.isArray(row)) {\n throw new TestDataError(`.data(): row #${i} for table \"${tableName}\" must be an object`);\n }\n for (const key of Object.keys(row)) {\n if (!columnNames.has(key)) {\n throw new TestDataError(\n `.data(): unknown column \"${key}\" in row #${i} for table \"${tableName}\" ` +\n `(declared columns: ${[...columnNames].join(', ')})`\n );\n }\n }\n for (const pk of table.primaryKey) {\n if (row[pk] === undefined) {\n throw new TestDataError(\n `.data(): row #${i} for table \"${tableName}\" is missing primary-key column \"${pk}\" ` +\n `(primary key: ${table.primaryKey.join(', ')})`\n );\n }\n }\n ops.push({ table: tableName, row });\n });\n }\n return ops;\n}\n\n/**\n * Compose row operations into effective rows per table.\n * PK tables: identity = ordered PK values; later writes replace, first\n * occurrence keeps its position. No-PK tables: append in authored order.\n */\nexport function composeRows(\n schema: NormalizedTestSchema,\n ops: readonly DataOperation[]\n): Map<string, Row[]> {\n const tables = new Map(schema.tables.map((t) => [t.name, t]));\n const result = new Map<string, Row[]>();\n const identityIndex = new Map<string, Map<string, number>>();\n for (const op of ops) {\n const table = tables.get(op.table)!;\n let rows = result.get(op.table);\n if (!rows) {\n rows = [];\n result.set(op.table, rows);\n }\n if (table.primaryKey.length === 0) {\n rows.push(op.row);\n continue;\n }\n const identity = JSON.stringify(table.primaryKey.map((pk) => op.row[pk]));\n let index = identityIndex.get(op.table);\n if (!index) {\n index = new Map();\n identityIndex.set(op.table, index);\n }\n const existing = index.get(identity);\n if (existing === undefined) {\n index.set(identity, rows.length);\n rows.push(op.row);\n } else {\n rows[existing] = op.row;\n }\n }\n return result;\n}\n\n/**\n * Deterministic insertion order for the tables that actually have rows,\n * topologically sorted by declared references between those tables.\n * Cycles fail with actionable guidance.\n */\nexport function planInsertOrder(schema: NormalizedTestSchema, tablesWithRows: readonly string[]): string[] {\n const inPlan = new Set(tablesWithRows);\n const tables = schema.tables.filter((t) => inPlan.has(t.name));\n // deps[t] = set of tables (with rows) that must be inserted before t\n const deps = new Map<string, Set<string>>();\n for (const t of tables) {\n const set = new Set<string>();\n for (const c of t.columns) {\n if (c.references && c.references.table !== t.name && inPlan.has(c.references.table)) {\n set.add(c.references.table);\n }\n }\n deps.set(t.name, set);\n }\n const order: string[] = [];\n const done = new Set<string>();\n let progressed = true;\n while (progressed && order.length < tables.length) {\n progressed = false;\n for (const t of tables) {\n if (done.has(t.name)) continue;\n const pending = [...deps.get(t.name)!].filter((d) => !done.has(d));\n if (pending.length === 0) {\n order.push(t.name);\n done.add(t.name);\n progressed = true;\n }\n }\n }\n if (order.length < tables.length) {\n const cycle = tables.filter((t) => !done.has(t.name)).map((t) => t.name);\n throw new TestDataError(\n `Cannot derive an insertion order: declared references between tables [${cycle.join(', ')}] form a cycle. ` +\n `Insert this data via a raw \"sql\" entry (e.g. with deferred constraints), or restructure the references.`\n );\n }\n return order;\n}\n\n/**\n * Insert effective rows. Parameterized and batched per table by column\n * signature; missing columns are omitted from the INSERT so PostgreSQL\n * defaults apply, while explicit null is sent as NULL.\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((t) => [t.name, t.columns.map((c) => c.name)]));\n for (const tableName of order) {\n const rows = effective.get(tableName)!;\n const columnOrder = declared.get(tableName)!;\n // Group consecutive-compatible rows into batches sharing a column signature.\n const batches = new Map<string, { columns: string[]; rows: Row[] }>();\n for (const row of rows) {\n const columns = columnOrder.filter((c) => row[c] !== undefined);\n const sig = columns.join(',');\n let batch = batches.get(sig);\n if (!batch) {\n batch = { columns, rows: [] };\n batches.set(sig, batch);\n }\n batch.rows.push(row);\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((c) => {\n params.push(row[c]);\n return `$${params.length}`;\n });\n return `(${placeholders.join(', ')})`;\n });\n const sql =\n `INSERT INTO ${quoteIdent(tableName)} (${columns.map(quoteIdent).join(', ')}) ` +\n `VALUES ${tuples.join(', ')}`;\n await run(query, tableName, sql, params);\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"]}