sqlstack 3.3.0 → 3.5.0-dev.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 (128) hide show
  1. package/dist/cjs/adapters/sqlite.d.ts.map +1 -1
  2. package/dist/cjs/adapters/sqlite.js +4 -1
  3. package/dist/cjs/adapters/sqlite.js.map +1 -1
  4. package/dist/cjs/codegen/generateManifest.d.ts +3 -1
  5. package/dist/cjs/codegen/generateManifest.d.ts.map +1 -1
  6. package/dist/cjs/codegen/generateManifest.js +195 -3
  7. package/dist/cjs/codegen/generateManifest.js.map +1 -1
  8. package/dist/cjs/core/errors.d.ts +9 -0
  9. package/dist/cjs/core/errors.d.ts.map +1 -1
  10. package/dist/cjs/core/errors.js +10 -0
  11. package/dist/cjs/core/errors.js.map +1 -1
  12. package/dist/cjs/core/fileResolution.d.ts +7 -0
  13. package/dist/cjs/core/fileResolution.d.ts.map +1 -1
  14. package/dist/cjs/core/fileResolution.js +53 -14
  15. package/dist/cjs/core/fileResolution.js.map +1 -1
  16. package/dist/cjs/core/metadata.d.ts +5 -0
  17. package/dist/cjs/core/metadata.d.ts.map +1 -1
  18. package/dist/cjs/core/metadata.js.map +1 -1
  19. package/dist/cjs/core/resources.d.ts +36 -0
  20. package/dist/cjs/core/resources.d.ts.map +1 -0
  21. package/dist/cjs/core/resources.js +98 -0
  22. package/dist/cjs/core/resources.js.map +1 -0
  23. package/dist/cjs/core/sourceScanResolver.d.ts +8 -14
  24. package/dist/cjs/core/sourceScanResolver.d.ts.map +1 -1
  25. package/dist/cjs/core/sourceScanResolver.js +8 -20
  26. package/dist/cjs/core/sourceScanResolver.js.map +1 -1
  27. package/dist/cjs/decorators/query.d.ts +14 -0
  28. package/dist/cjs/decorators/query.d.ts.map +1 -1
  29. package/dist/cjs/decorators/query.js +208 -40
  30. package/dist/cjs/decorators/query.js.map +1 -1
  31. package/dist/cjs/family.d.ts.map +1 -1
  32. package/dist/cjs/family.js +3 -1
  33. package/dist/cjs/family.js.map +1 -1
  34. package/dist/cjs/index.d.ts +4 -1
  35. package/dist/cjs/index.d.ts.map +1 -1
  36. package/dist/cjs/index.js +7 -2
  37. package/dist/cjs/index.js.map +1 -1
  38. package/dist/cjs/run.d.ts +180 -6
  39. package/dist/cjs/run.d.ts.map +1 -1
  40. package/dist/cjs/run.js +158 -17
  41. package/dist/cjs/run.js.map +1 -1
  42. package/dist/cjs/runtime.d.ts +14 -0
  43. package/dist/cjs/runtime.d.ts.map +1 -1
  44. package/dist/cjs/runtime.js +30 -0
  45. package/dist/cjs/runtime.js.map +1 -1
  46. package/dist/cjs/sql.d.ts +19 -0
  47. package/dist/cjs/sql.d.ts.map +1 -1
  48. package/dist/cjs/sql.js +66 -28
  49. package/dist/cjs/sql.js.map +1 -1
  50. package/dist/cjs/stack.d.ts +16 -0
  51. package/dist/cjs/stack.d.ts.map +1 -1
  52. package/dist/cjs/stack.js +60 -27
  53. package/dist/cjs/stack.js.map +1 -1
  54. package/dist/cjs/testing/builder.d.ts.map +1 -1
  55. package/dist/cjs/testing/builder.js +14 -5
  56. package/dist/cjs/testing/builder.js.map +1 -1
  57. package/dist/cjs/testing/fixture-provenance.d.ts +3 -0
  58. package/dist/cjs/testing/fixture-provenance.d.ts.map +1 -0
  59. package/dist/cjs/testing/fixture-provenance.js +21 -0
  60. package/dist/cjs/testing/fixture-provenance.js.map +1 -0
  61. package/dist/cjs/testing/index.d.ts +2 -0
  62. package/dist/cjs/testing/index.d.ts.map +1 -1
  63. package/dist/cjs/testing/index.js +5 -1
  64. package/dist/cjs/testing/index.js.map +1 -1
  65. package/dist/cjs/testing/postgres/database.d.ts +9 -1
  66. package/dist/cjs/testing/postgres/database.d.ts.map +1 -1
  67. package/dist/cjs/testing/postgres/database.js +17 -13
  68. package/dist/cjs/testing/postgres/database.js.map +1 -1
  69. package/dist/cjs/testing/resource.d.ts +76 -0
  70. package/dist/cjs/testing/resource.d.ts.map +1 -0
  71. package/dist/cjs/testing/resource.js +117 -0
  72. package/dist/cjs/testing/resource.js.map +1 -0
  73. package/dist/cjs/testing/sqlite/builder.d.ts.map +1 -1
  74. package/dist/cjs/testing/sqlite/builder.js +10 -2
  75. package/dist/cjs/testing/sqlite/builder.js.map +1 -1
  76. package/dist/cjs/testing/sqlite/database.d.ts +11 -1
  77. package/dist/cjs/testing/sqlite/database.d.ts.map +1 -1
  78. package/dist/cjs/testing/sqlite/database.js +32 -18
  79. package/dist/cjs/testing/sqlite/database.js.map +1 -1
  80. package/dist/cjs/transactions.d.ts +66 -0
  81. package/dist/cjs/transactions.d.ts.map +1 -1
  82. package/dist/cjs/transactions.js +584 -3
  83. package/dist/cjs/transactions.js.map +1 -1
  84. package/dist/esm/adapters/sqlite.js +4 -1
  85. package/dist/esm/adapters/sqlite.js.map +1 -1
  86. package/dist/esm/codegen/generateManifest.js +195 -3
  87. package/dist/esm/codegen/generateManifest.js.map +1 -1
  88. package/dist/esm/core/errors.js +10 -0
  89. package/dist/esm/core/errors.js.map +1 -1
  90. package/dist/esm/core/fileResolution.js +52 -14
  91. package/dist/esm/core/fileResolution.js.map +1 -1
  92. package/dist/esm/core/metadata.js.map +1 -1
  93. package/dist/esm/core/resources.js +91 -0
  94. package/dist/esm/core/resources.js.map +1 -0
  95. package/dist/esm/core/sourceScanResolver.js +8 -19
  96. package/dist/esm/core/sourceScanResolver.js.map +1 -1
  97. package/dist/esm/decorators/query.js +214 -46
  98. package/dist/esm/decorators/query.js.map +1 -1
  99. package/dist/esm/family.js +3 -1
  100. package/dist/esm/family.js.map +1 -1
  101. package/dist/esm/index.js +4 -2
  102. package/dist/esm/index.js.map +1 -1
  103. package/dist/esm/run.js +153 -18
  104. package/dist/esm/run.js.map +1 -1
  105. package/dist/esm/runtime.js +28 -0
  106. package/dist/esm/runtime.js.map +1 -1
  107. package/dist/esm/sql.js +63 -27
  108. package/dist/esm/sql.js.map +1 -1
  109. package/dist/esm/stack.js +61 -28
  110. package/dist/esm/stack.js.map +1 -1
  111. package/dist/esm/testing/builder.js +14 -5
  112. package/dist/esm/testing/builder.js.map +1 -1
  113. package/dist/esm/testing/fixture-provenance.js +15 -0
  114. package/dist/esm/testing/fixture-provenance.js.map +1 -0
  115. package/dist/esm/testing/index.js +1 -0
  116. package/dist/esm/testing/index.js.map +1 -1
  117. package/dist/esm/testing/postgres/database.js +17 -13
  118. package/dist/esm/testing/postgres/database.js.map +1 -1
  119. package/dist/esm/testing/resource.js +106 -0
  120. package/dist/esm/testing/resource.js.map +1 -0
  121. package/dist/esm/testing/sqlite/builder.js +10 -2
  122. package/dist/esm/testing/sqlite/builder.js.map +1 -1
  123. package/dist/esm/testing/sqlite/database.js +32 -18
  124. package/dist/esm/testing/sqlite/database.js.map +1 -1
  125. package/dist/esm/transactions.js +579 -3
  126. package/dist/esm/transactions.js.map +1 -1
  127. package/package.json +1 -1
  128. package/readme.md +201 -171
@@ -40,6 +40,7 @@ const os = __importStar(require("node:os"));
40
40
  const path = __importStar(require("node:path"));
41
41
  const sqlite_1 = require("../../adapters/sqlite");
42
42
  const errors_1 = require("../../core/errors");
43
+ const resource_1 = require("../resource");
43
44
  class TestSqliteError extends errors_1.SqlStackError {
44
45
  constructor(message) {
45
46
  super(message);
@@ -70,6 +71,12 @@ function trackDirectory(directory) {
70
71
  }
71
72
  });
72
73
  }
74
+ /** Remove exactly the SQLStack-owned temp directory; never a caller path. */
75
+ function removeOwnedDirectory(directory) {
76
+ fs.rmSync(directory, { recursive: true, force: true });
77
+ // Keep failed paths in the owned inventory for the exit cleanup retry.
78
+ abandonedDirectories.delete(directory);
79
+ }
73
80
  async function createDatabaseHandle(fileBacked) {
74
81
  let directory;
75
82
  let filename = ':memory:';
@@ -84,8 +91,12 @@ async function createDatabaseHandle(fileBacked) {
84
91
  }
85
92
  catch (err) {
86
93
  if (directory) {
87
- fs.rmSync(directory, { recursive: true, force: true });
88
- abandonedDirectories.delete(directory);
94
+ try {
95
+ removeOwnedDirectory(directory);
96
+ }
97
+ catch (cleanup) {
98
+ throw Object.assign(new resource_1.TestDatabaseDisposeError('SQLite test allocation failed and owned cleanup failed', [err, cleanup]), { cause: err });
99
+ }
89
100
  }
90
101
  const message = err.message;
91
102
  if (message.includes('No SQLite driver available')) {
@@ -93,31 +104,34 @@ async function createDatabaseHandle(fileBacked) {
93
104
  }
94
105
  throw new TestSqliteError(`Failed to create SQLite test database: ${message}`);
95
106
  }
96
- let disposed = false;
97
- return {
107
+ const storage = fileBacked ? 'file' : 'memory';
108
+ const resourceId = (0, resource_1.mintResourceId)('sqlite');
109
+ const ownedDirectory = directory;
110
+ let disposal;
111
+ const handle = {
112
+ engine: 'sqlite',
113
+ resourceId,
114
+ storage,
98
115
  db,
99
116
  conn: db.conn,
100
117
  path: fileBacked ? filename : undefined,
101
118
  async query(sql, params = []) {
102
- if (disposed)
119
+ if (disposal)
103
120
  throw new TestSqliteError('Test SQLite database has been disposed.');
104
121
  return db.query(sql, [...params]);
105
122
  },
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);
123
+ dispose() {
124
+ if (!disposal) {
125
+ (0, resource_1.retireResource)(handle);
126
+ const steps = [() => db.close()];
127
+ if (ownedDirectory)
128
+ steps.push(() => removeOwnedDirectory(ownedDirectory));
129
+ disposal = Promise.resolve().then(() => (0, resource_1.runCleanupSteps)(`Test SQLite database (${storage})`, steps));
119
130
  }
131
+ return disposal;
120
132
  },
121
133
  };
134
+ (0, resource_1.registerResource)(handle, { engine: 'sqlite', resourceId, storage });
135
+ return handle;
122
136
  }
123
137
  //# sourceMappingURL=database.js.map
@@ -1 +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"]}
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';\nimport { mintResourceId, registerResource, retireResource, runCleanupSteps, TestDatabaseDisposeError } from '../resource';\n\nexport class TestSqliteError extends SqlStackError {\n constructor(message: string) {\n super(message);\n this.name = 'TestSqliteError';\n }\n}\n\nexport interface TestSqliteDatabase {\n /** Engine discriminator for SQLStack-minted disposable test databases. */\n readonly engine: 'sqlite';\n /** Fresh per-allocation identifier; never reused within a process. */\n readonly resourceId: string;\n /** Explicit storage: an owned temporary file, or an isolated `:memory:` db. */\n readonly storage: 'memory' | 'file';\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 /**\n * Close the connection and remove owned temporary files. Idempotent:\n * concurrent and repeated calls share one outcome. Every cleanup step is\n * attempted; failures are aggregated into a `TestDatabaseDisposeError`.\n */\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\n/** Remove exactly the SQLStack-owned temp directory; never a caller path. */\nfunction removeOwnedDirectory(directory: string): void {\n fs.rmSync(directory, { recursive: true, force: true });\n // Keep failed paths in the owned inventory for the exit cleanup retry.\n abandonedDirectories.delete(directory);\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 try { removeOwnedDirectory(directory); }\n catch (cleanup) {\n throw Object.assign(new TestDatabaseDisposeError('SQLite test allocation failed and owned cleanup failed', [err, cleanup]), { cause: err });\n }\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 const storage: 'memory' | 'file' = fileBacked ? 'file' : 'memory';\n const resourceId = mintResourceId('sqlite');\n const ownedDirectory = directory;\n let disposal: Promise<void> | undefined;\n\n const handle: TestSqliteDatabase = {\n engine: 'sqlite',\n resourceId,\n storage,\n db,\n conn: db.conn,\n path: fileBacked ? filename : undefined,\n async query(sql: string, params: readonly unknown[] = []) {\n if (disposal) throw new TestSqliteError('Test SQLite database has been disposed.');\n return db.query(sql, [...params]);\n },\n dispose() {\n if (!disposal) {\n retireResource(handle);\n const steps: (() => Promise<void> | void)[] = [() => db.close()];\n if (ownedDirectory) steps.push(() => removeOwnedDirectory(ownedDirectory));\n disposal = Promise.resolve().then(() => runCleanupSteps(`Test SQLite database (${storage})`, steps));\n }\n return disposal;\n },\n };\n registerResource(handle, { engine: 'sqlite', resourceId, storage });\n return handle;\n}\n"]}
@@ -5,6 +5,15 @@ export type TransactionOptions = {
5
5
  lazy?: boolean;
6
6
  nested?: 'join';
7
7
  };
8
+ /** Work registered with transaction.afterCommit / transaction.afterSettled. */
9
+ export type TransactionHook = () => unknown;
10
+ /** Observable transaction events (see onTransactionEvent). */
11
+ export type TransactionEvent = {
12
+ readonly type: 'sqlstack.transaction.multi_database';
13
+ /** Names of the databases the scope has opened, in open order. */
14
+ readonly databases: readonly string[];
15
+ };
16
+ export type TransactionEventListener = (event: TransactionEvent) => void;
8
17
  export declare class TransactionHandle {
9
18
  #private;
10
19
  readonly entry: DatabaseEntry;
@@ -27,6 +36,10 @@ declare class TxState {
27
36
  completed: boolean;
28
37
  rollbackOnly: boolean;
29
38
  rollbackError: unknown | undefined;
39
+ /** SQLite opening statement: name-based transactions keep plain BEGIN. */
40
+ sqliteBegin: 'BEGIN' | 'BEGIN IMMEDIATE';
41
+ /** Called once when the transaction releases its connection (SQLite queue turn). */
42
+ onRelease: (() => void) | undefined;
30
43
  private txDb;
31
44
  private releaseFn;
32
45
  constructor(entry: DatabaseEntry, options: TransactionOptions | undefined);
@@ -45,17 +58,70 @@ declare class TxState {
45
58
  * from the root's SqlStack (`stack.getEntry(name)`).
46
59
  */
47
60
  export declare function currentTransaction(db?: string): TransactionHandle | undefined;
61
+ /**
62
+ * Whether this async call carries any transaction records. This observation
63
+ * never resolves a database, opens a connection, or consults legacy defaults.
64
+ * It does not choose an owner: use currentTransactionFor(entry) for that.
65
+ */
66
+ export declare function hasTransactionContext(): boolean;
48
67
  /** Active transaction handle for a concrete database entry, if any. */
49
68
  export declare function currentTransactionFor(entry: DatabaseEntry): TransactionHandle | undefined;
50
69
  export declare function withTransaction<T>(fn: () => Promise<T>, options?: TransactionOptions): Promise<T>;
70
+ /**
71
+ * Observe transaction trace events. Today the only event is
72
+ * `sqlstack.transaction.multi_database`, emitted once per root scope the
73
+ * moment it opens its second database. Returns an unsubscribe function.
74
+ * Listener errors are ignored so observation never affects a transaction.
75
+ */
76
+ export declare function onTransactionEvent(listener: TransactionEventListener): () => void;
77
+ /**
78
+ * @internal The transaction a query on `entry` runs in: the one already open
79
+ * in this flow (including one a handle of the same database opened), or —
80
+ * while a @transaction scope is active — one opened on that entry by the
81
+ * query itself (on first execution, not on resolution). Undefined means "run
82
+ * on the database directly".
83
+ */
84
+ export declare function getTransactionTarget(entry: DatabaseEntry): ActiveTransaction | undefined;
85
+ /**
86
+ * @internal The transaction a query on a Database handle (run(db, ...)) runs
87
+ * in. A transaction already open in this flow on that exact object is joined
88
+ * (identity; registered or handle-opened). Inside a @transaction scope with
89
+ * none open, the query opens one on the handle on first execution, exactly as
90
+ * the name-resolved path does. Outside any scope the behaviour is unchanged:
91
+ * join by identity or run directly (undefined).
92
+ */
93
+ export declare function getTransactionTargetForHandle(db: {
94
+ query(sql: string, params: unknown[]): Promise<unknown>;
95
+ }): ActiveTransaction | undefined;
51
96
  export type TransactionDecorator = {
52
97
  (target: any, prop: string | symbol, desc: PropertyDescriptor): void;
53
98
  (options?: TransactionOptions): (target: any, prop: string | symbol, desc: PropertyDescriptor) => void;
99
+ /**
100
+ * Mark the innermost scope (or, with a database name or handle, only that
101
+ * database's open transaction) for rollback at scope exit. Never throws
102
+ * inside a scope for a valid target; throws SqlStackError outside any
103
+ * scope or when no open transaction matches the target.
104
+ */
105
+ rollback(target?: string | object): void;
106
+ /** Roll back the scope's work now; the next query opens fresh transactions. */
107
+ restart(): Promise<void>;
108
+ /** Run fn after the root scope commits (dropped on rollback). */
109
+ afterCommit(fn: TransactionHook): void;
110
+ /** Run fn after the root scope finishes, committed or not. */
111
+ afterSettled(fn: TransactionHook): void;
54
112
  };
55
113
  export declare const transaction: TransactionDecorator;
56
114
  export type ActiveTransaction = {
57
115
  query(sql: string, params: unknown[]): Promise<QueryResult>;
58
116
  };
59
117
  export declare function getActiveTransaction(entry: DatabaseEntry): ActiveTransaction | undefined;
118
+ /**
119
+ * Active transaction whose registered database is exactly `db` (object
120
+ * identity), if any. `run(db, ...)` uses this so a handle passed at call time
121
+ * joins a transaction opened on the same database (by `@transaction` or
122
+ * `withTransaction`) instead of bypassing it. A handle that was never
123
+ * registered with a SqlStack (or the legacy registry) never matches.
124
+ */
125
+ export declare function getActiveTransactionForDatabase(db: object): ActiveTransaction | undefined;
60
126
  export {};
61
127
  //# sourceMappingURL=transactions.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"transactions.d.ts","sourceRoot":"","sources":["../../src/transactions.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAGxD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAG7C,MAAM,MAAM,kBAAkB,GAAG;IAC/B,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC;AA6BF,qBAAa,iBAAiB;;IAC5B,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAC;gBAKlB,KAAK,EAAE,aAAa,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO;IAMhE,IAAI,MAAM,IAAI,MAAM,CAEnB;IAEK,aAAa;IAInB,YAAY,CAAC,YAAY,CAAC,EAAE,OAAO;IAO7B,MAAM;IAKN,QAAQ;IAKR,OAAO;IAKP,eAAe;IAMf,aAAa,CAAC,GAAG,EAAE,OAAO;CAKjC;AAED,cAAM,OAAO;IACX,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAC;IAC9B,QAAQ,CAAC,EAAE,EAAE,QAAQ,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,QAAQ,CAAC,IAAI,CAAC,kBAAkB,EAAE,MAAM,CAAC,CAAC,GAAG,IAAI,CAAC,kBAAkB,EAAE,QAAQ,CAAC,CAAC;IAClG,KAAK,SAAK;IACV,OAAO,UAAS;IAChB,SAAS,UAAS;IAClB,YAAY,UAAS;IACrB,aAAa,EAAE,OAAO,GAAG,SAAS,CAAC;IAEnC,OAAO,CAAC,IAAI,CAAuB;IACnC,OAAO,CAAC,SAAS,CAAoC;gBAEzC,KAAK,EAAE,aAAa,EAAE,OAAO,EAAE,kBAAkB,GAAG,SAAS;IASzE,IAAI,QAAQ,IAAI,QAAQ,CAEvB;IAEK,aAAa;IAuCb,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,WAAW,CAAC;IAK3D,eAAe;IAUf,iBAAiB;IAUjB,OAAO;IASP,eAAe;IAaf,aAAa,CAAC,GAAG,EAAE,OAAO;CAWjC;AAuBD;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,EAAE,CAAC,EAAE,MAAM,GAAG,iBAAiB,GAAG,SAAS,CAO7E;AAED,uEAAuE;AACvE,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,aAAa,GAAG,iBAAiB,GAAG,SAAS,CAKzF;AAED,wBAAsB,eAAe,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,EAAE,kBAAkB,GAAG,OAAO,CAAC,CAAC,CAAC,CAkBvG;AAED,MAAM,MAAM,oBAAoB,GAAG;IACjC,CAAC,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,IAAI,EAAE,kBAAkB,GAAG,IAAI,CAAC;IACrE,CAAC,OAAO,CAAC,EAAE,kBAAkB,GAAG,CAAC,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,IAAI,EAAE,kBAAkB,KAAK,IAAI,CAAC;CACxG,CAAC;AAEF,eAAO,MAAM,WAAW,EAAE,oBAYlB,CAAC;AAST,MAAM,MAAM,iBAAiB,GAAG;IAC9B,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC;CAC7D,CAAC;AAEF,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,aAAa,GAAG,iBAAiB,GAAG,SAAS,CAGxF"}
1
+ {"version":3,"file":"transactions.d.ts","sourceRoot":"","sources":["../../src/transactions.ts"],"names":[],"mappings":"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"}