@ductape/sdk 0.4.3 → 0.4.5

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 (41) hide show
  1. package/dist/context/execution-context.d.ts +6 -0
  2. package/dist/context/execution-context.js +1 -1
  3. package/dist/context/execution-context.js.map +1 -1
  4. package/dist/database/adapters/dynamodb.adapter.d.ts +2 -0
  5. package/dist/database/adapters/dynamodb.adapter.js +18 -1
  6. package/dist/database/adapters/dynamodb.adapter.js.map +1 -1
  7. package/dist/database/adapters/mongodb.adapter.d.ts +2 -0
  8. package/dist/database/adapters/mongodb.adapter.js +31 -8
  9. package/dist/database/adapters/mongodb.adapter.js.map +1 -1
  10. package/dist/database/adapters/mysql.adapter.d.ts +2 -0
  11. package/dist/database/adapters/mysql.adapter.js +89 -14
  12. package/dist/database/adapters/mysql.adapter.js.map +1 -1
  13. package/dist/database/adapters/postgresql.adapter.d.ts +2 -0
  14. package/dist/database/adapters/postgresql.adapter.js +23 -3
  15. package/dist/database/adapters/postgresql.adapter.js.map +1 -1
  16. package/dist/database/atomic-batch.d.ts +51 -0
  17. package/dist/database/atomic-batch.js +174 -0
  18. package/dist/database/atomic-batch.js.map +1 -0
  19. package/dist/database/databases.service.d.ts +4 -0
  20. package/dist/database/databases.service.js +116 -3
  21. package/dist/database/databases.service.js.map +1 -1
  22. package/dist/database/dynamodb-atomic-batch.d.ts +8 -0
  23. package/dist/database/dynamodb-atomic-batch.js +153 -0
  24. package/dist/database/dynamodb-atomic-batch.js.map +1 -0
  25. package/dist/database/index.d.ts +1 -0
  26. package/dist/database/index.js +4 -2
  27. package/dist/database/index.js.map +1 -1
  28. package/dist/database/transactions/transaction-manager.d.ts +5 -1
  29. package/dist/database/transactions/transaction-manager.js +13 -2
  30. package/dist/database/transactions/transaction-manager.js.map +1 -1
  31. package/dist/index.d.ts +3 -0
  32. package/dist/index.js +9 -2
  33. package/dist/index.js.map +1 -1
  34. package/dist/logs/logs.service.js +10 -0
  35. package/dist/logs/logs.service.js.map +1 -1
  36. package/dist/resilience/healthcheck.service.js +21 -8
  37. package/dist/resilience/healthcheck.service.js.map +1 -1
  38. package/dist/resilience/resilience-execution.utils.js +3 -2
  39. package/dist/resilience/resilience-execution.utils.js.map +1 -1
  40. package/dist/tsconfig.tsbuildinfo +1 -1
  41. package/package.json +1 -1
@@ -0,0 +1,51 @@
1
+ import type { ITransaction } from './types/transaction.interface';
2
+ export interface AtomicBatchOperation {
3
+ name: string;
4
+ operation: 'insert' | 'update' | 'delete' | 'upsert';
5
+ table: string;
6
+ data?: Record<string, unknown> | Record<string, unknown>[];
7
+ where?: Record<string, unknown>;
8
+ conflictKeys?: string[];
9
+ returning?: boolean;
10
+ /** Exact primitive count, not matched count. MongoDB update counts modified documents. */
11
+ expectedCount?: number;
12
+ /** Output label -> JSON pointer into this operation's actual primitive result. */
13
+ project?: Record<string, string>;
14
+ }
15
+ export interface AtomicBatchOptions {
16
+ product: string;
17
+ env: string;
18
+ database: string;
19
+ /** Original opaque session, forwarded unchanged to every operation. */
20
+ session?: string;
21
+ /** Explicit maintenance classification; does not grant or bypass access. */
22
+ system?: {
23
+ reason: string;
24
+ };
25
+ transaction?: ITransaction;
26
+ operations: AtomicBatchOperation[];
27
+ }
28
+ export interface AtomicBatchResult {
29
+ status: 'committed' | 'pending';
30
+ transactionId: string;
31
+ results: Record<string, unknown>;
32
+ }
33
+ export declare class AtomicBatchError extends Error {
34
+ readonly code: 'BATCH_VALIDATION' | 'BATCH_GUARD_FAILED' | 'BATCH_OPERATION_FAILED' | 'BATCH_COMMIT_UNKNOWN';
35
+ readonly outcome: 'not_started' | 'aborted' | 'rollback_unknown' | 'commit_unknown';
36
+ readonly operation?: string;
37
+ readonly transactionId?: string;
38
+ constructor(code: 'BATCH_VALIDATION' | 'BATCH_GUARD_FAILED' | 'BATCH_OPERATION_FAILED' | 'BATCH_COMMIT_UNKNOWN', outcome: 'not_started' | 'aborted' | 'rollback_unknown' | 'commit_unknown', operation?: string, transactionId?: string);
39
+ }
40
+ export interface AtomicBatchHost {
41
+ preflight(options: AtomicBatchOptions): Promise<void>;
42
+ executeNative?(options: AtomicBatchOptions): Promise<AtomicBatchResult | undefined>;
43
+ begin(options: AtomicBatchOptions): Promise<ITransaction>;
44
+ validateTransaction?(options: AtomicBatchOptions, transaction: ITransaction): Promise<void>;
45
+ execute(operation: AtomicBatchOperation, options: AtomicBatchOptions, transaction: ITransaction): Promise<{
46
+ count: number;
47
+ }>;
48
+ }
49
+ export declare function validateAtomicBatch(options: AtomicBatchOptions): void;
50
+ /** Sequential ACID executor. No automatic write replay and no provider callbacks. */
51
+ export declare function executeAtomicBatch(host: AtomicBatchHost, options: AtomicBatchOptions): Promise<AtomicBatchResult>;
@@ -0,0 +1,174 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AtomicBatchError = void 0;
4
+ exports.validateAtomicBatch = validateAtomicBatch;
5
+ exports.executeAtomicBatch = executeAtomicBatch;
6
+ class AtomicBatchError extends Error {
7
+ constructor(code, outcome, operation, transactionId) {
8
+ // Never copy provider errors here: they can contain parameters and credentials.
9
+ super(`${code}: ${outcome}${operation ? ` (${operation})` : ''}`);
10
+ this.code = code;
11
+ this.outcome = outcome;
12
+ this.operation = operation;
13
+ this.transactionId = transactionId;
14
+ this.name = 'AtomicBatchError';
15
+ }
16
+ }
17
+ exports.AtomicBatchError = AtomicBatchError;
18
+ const forbidden = new Set(['__proto__', 'constructor', 'prototype']);
19
+ function fail() { throw new AtomicBatchError('BATCH_VALIDATION', 'not_started'); }
20
+ function record(value) {
21
+ return !!value && typeof value === 'object' && !Array.isArray(value)
22
+ && (Object.getPrototypeOf(value) === Object.prototype || Object.getPrototypeOf(value) === null);
23
+ }
24
+ function keys(value, allowed) {
25
+ if (Object.keys(value).some(key => !allowed.includes(key) || forbidden.has(key)))
26
+ fail();
27
+ }
28
+ function pointer(value, path) {
29
+ if (path === '')
30
+ return value;
31
+ if (!path.startsWith('/') || /~(?![01])/u.test(path))
32
+ fail();
33
+ for (const encoded of path.slice(1).split('/')) {
34
+ const key = encoded.replace(/~1/g, '/').replace(/~0/g, '~');
35
+ if (forbidden.has(key) || value === null || typeof value !== 'object' || !Object.prototype.hasOwnProperty.call(value, key))
36
+ fail();
37
+ value = value[key];
38
+ }
39
+ return value;
40
+ }
41
+ function walk(value, visit, depth = 0) {
42
+ if (depth > 32)
43
+ fail();
44
+ if (Array.isArray(value))
45
+ return value.map(item => walk(item, visit, depth + 1));
46
+ if (record(value)) {
47
+ if (Object.prototype.hasOwnProperty.call(value, '$batchRef')) {
48
+ keys(value, ['$batchRef']);
49
+ const ref = value.$batchRef;
50
+ if (!record(ref))
51
+ fail();
52
+ keys(ref, ['operation', 'path']);
53
+ if (typeof ref.operation !== 'string' || typeof ref.path !== 'string')
54
+ fail();
55
+ return visit(ref);
56
+ }
57
+ return Object.fromEntries(Object.entries(value).map(([key, child]) => {
58
+ if (forbidden.has(key))
59
+ fail();
60
+ return [key, walk(child, visit, depth + 1)];
61
+ }));
62
+ }
63
+ if (value !== null && typeof value === 'object')
64
+ fail();
65
+ if (typeof value === 'function' || typeof value === 'symbol' || typeof value === 'bigint' || value === undefined)
66
+ fail();
67
+ if (typeof value === 'number' && !Number.isFinite(value))
68
+ fail();
69
+ return value;
70
+ }
71
+ function validateAtomicBatch(options) {
72
+ if (!record(options))
73
+ fail();
74
+ keys(options, ['product', 'env', 'database', 'session', 'system', 'transaction', 'operations']);
75
+ for (const value of [options.product, options.env, options.database])
76
+ if (typeof value !== 'string' || !value.trim())
77
+ fail();
78
+ if (options.session !== undefined && (typeof options.session !== 'string' || !options.session.trim()))
79
+ fail();
80
+ if (options.system !== undefined) {
81
+ if (!record(options.system))
82
+ fail();
83
+ keys(options.system, ['reason']);
84
+ if (typeof options.system.reason !== 'string' || !options.system.reason.trim())
85
+ fail();
86
+ }
87
+ if (!!options.session === !!options.system)
88
+ fail();
89
+ if (!Array.isArray(options.operations) || !options.operations.length || options.operations.length > 100)
90
+ fail();
91
+ const prior = new Set();
92
+ for (const op of options.operations) {
93
+ if (!record(op))
94
+ fail();
95
+ keys(op, ['name', 'operation', 'table', 'data', 'where', 'conflictKeys', 'returning', 'expectedCount', 'project']);
96
+ if (typeof op.name !== 'string' || !/^[a-zA-Z][a-zA-Z0-9_-]{0,63}$/.test(op.name) || prior.has(op.name) || forbidden.has(op.name))
97
+ fail();
98
+ if (typeof op.table !== 'string' || !op.table.trim() || op.table.startsWith('_ductape_') || op.table.startsWith('system.'))
99
+ fail();
100
+ if (!['insert', 'update', 'delete', 'upsert'].includes(op.operation))
101
+ fail();
102
+ if (op.operation !== 'delete' && !record(op.data) && !(op.operation === 'insert' && Array.isArray(op.data) && op.data.length && op.data.every(record)))
103
+ fail();
104
+ if (['update', 'delete'].includes(op.operation) && (!record(op.where) || !Object.keys(op.where).length))
105
+ fail();
106
+ if (op.operation === 'upsert' && (!Array.isArray(op.conflictKeys) || !op.conflictKeys.length || op.conflictKeys.some(key => typeof key !== 'string' || !key || forbidden.has(key))))
107
+ fail();
108
+ if (op.returning !== undefined && typeof op.returning !== 'boolean')
109
+ fail();
110
+ if (op.expectedCount !== undefined && (!Number.isSafeInteger(op.expectedCount) || op.expectedCount < 0))
111
+ fail();
112
+ if (op.project !== undefined && (!record(op.project) || Object.values(op.project).some(path => typeof path !== 'string' || (path !== '' && !path.startsWith('/')))))
113
+ fail();
114
+ walk(op, ref => { if (!prior.has(ref.operation))
115
+ fail(); return null; });
116
+ prior.add(op.name);
117
+ }
118
+ if (Buffer.byteLength(JSON.stringify(options.operations)) > 1024 * 1024)
119
+ fail();
120
+ }
121
+ /** Sequential ACID executor. No automatic write replay and no provider callbacks. */
122
+ async function executeAtomicBatch(host, options) {
123
+ var _a, _b, _c;
124
+ validateAtomicBatch(options);
125
+ // Snapshot caller-owned inputs before awaiting; preserve the opaque session and transaction.
126
+ options = Object.assign(Object.assign({}, options), { operations: JSON.parse(JSON.stringify(options.operations)) });
127
+ // References are resolved later, so use a JSON snapshot retaining their declarative form.
128
+ await host.preflight(options);
129
+ const native = await ((_a = host.executeNative) === null || _a === void 0 ? void 0 : _a.call(host, options));
130
+ if (native)
131
+ return native;
132
+ const transaction = (_b = options.transaction) !== null && _b !== void 0 ? _b : await host.begin(options);
133
+ const results = Object.create(null);
134
+ const projected = Object.create(null);
135
+ let operation;
136
+ let guard = false;
137
+ try {
138
+ await ((_c = host.validateTransaction) === null || _c === void 0 ? void 0 : _c.call(host, options, transaction));
139
+ for (const source of options.operations) {
140
+ operation = source.name;
141
+ if (!transaction.isActive())
142
+ throw new Error('inactive transaction');
143
+ const op = walk(source, ref => pointer(results[ref.operation], ref.path));
144
+ const result = await host.execute(op, options, transaction);
145
+ if (op.expectedCount !== undefined && result.count !== op.expectedCount) {
146
+ guard = true;
147
+ throw new Error('count guard failed');
148
+ }
149
+ results[op.name] = result;
150
+ projected[op.name] = op.project ? Object.fromEntries(Object.entries(op.project).map(([key, path]) => [key, pointer(result, path)])) : result;
151
+ }
152
+ }
153
+ catch (_d) {
154
+ let outcome = transaction.status === 'rolled_back' ? 'aborted' : 'rollback_unknown';
155
+ try {
156
+ if (transaction.isActive()) {
157
+ await transaction.rollback();
158
+ outcome = 'aborted';
159
+ }
160
+ }
161
+ catch ( /* Preserve uncertainty, not the provider payload. */_e) { /* Preserve uncertainty, not the provider payload. */ }
162
+ throw new AtomicBatchError(guard ? 'BATCH_GUARD_FAILED' : 'BATCH_OPERATION_FAILED', outcome, operation, transaction.id);
163
+ }
164
+ if (options.transaction)
165
+ return { status: 'pending', transactionId: transaction.id, results: projected };
166
+ try {
167
+ await transaction.commit();
168
+ }
169
+ catch (_f) {
170
+ throw new AtomicBatchError('BATCH_COMMIT_UNKNOWN', 'commit_unknown', undefined, transaction.id);
171
+ }
172
+ return { status: 'committed', transactionId: transaction.id, results: projected };
173
+ }
174
+ //# sourceMappingURL=atomic-batch.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"atomic-batch.js","sourceRoot":"","sources":["../../src/database/atomic-batch.ts"],"names":[],"mappings":"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"}
@@ -10,6 +10,7 @@
10
10
  import { IConnectionConfig, IConnectionResult, IConnectionContext, IDatabaseConfig } from './types/connection.interface';
11
11
  import { IQueryOptions, IQueryResult, IRawQueryOptions, IRawQueryResult } from './types/query.interface';
12
12
  import { IInsertOptions, IInsertResult, IUpdateOptions, IUpdateResult, IDeleteOptions, IDeleteResult, IUpsertOptions, IUpsertResult } from './types/write.interface';
13
+ import { AtomicBatchOptions, AtomicBatchResult } from './atomic-batch';
13
14
  import { ITransactionOptions, ITransaction, ITransactionCallback } from './types/transaction.interface';
14
15
  import { IAggregateOptions, IAggregateResult, ICountOptions, ISumOptions, IAvgOptions, IMinMaxOptions, IGroupByOptions, IGroupByResult } from './types/aggregation.interface';
15
16
  import { ICreateTableOptions, ITableDefinition, IAlterTableOperation, ITableSchema, ITableInfo, ICreateIndexOptions, IDropIndexOptions, IListIndexesOptions, IIndexInfo, IIndexStatistics, SimpleFieldType, ISimpleFieldDefinition, ISimpleSchemaDefinition, ISimpleCreateOptions, ISimpleDropOptions, ISimpleIndexOptions } from './types/schema.interface';
@@ -67,6 +68,7 @@ export declare class DatabaseService {
67
68
  private currentContext;
68
69
  private adapterFactory;
69
70
  private transactionManager;
71
+ private readonly transactionScopes;
70
72
  private schemaManagers;
71
73
  private migrationEngines;
72
74
  private actionManager;
@@ -605,6 +607,8 @@ export declare class DatabaseService {
605
607
  * }
606
608
  */
607
609
  beginTransaction(options: ITransactionOptions): Promise<ITransaction>;
610
+ /** Bounded ACID batch. Unsupported adapters and configured triggers fail closed. */
611
+ atomicBatch(options: AtomicBatchOptions): Promise<AtomicBatchResult>;
608
612
  /**
609
613
  * Create a new table
610
614
  *
@@ -48,10 +48,17 @@ Object.defineProperty(exports, "__esModule", { value: true });
48
48
  exports.databaseService = exports.DatabaseConnection = exports.DatabaseService = void 0;
49
49
  const enums_1 = require("./types/enums");
50
50
  const presave_processor_1 = require("./presave/presave-processor");
51
+ const atomic_batch_1 = require("./atomic-batch");
52
+ const dynamodb_adapter_1 = require("./adapters/dynamodb.adapter");
53
+ const transaction_manager_1 = require("./transactions/transaction-manager");
54
+ const mongodb_adapter_1 = require("./adapters/mongodb.adapter");
55
+ const postgresql_adapter_1 = require("./adapters/postgresql.adapter");
56
+ const mysql_adapter_1 = require("./adapters/mysql.adapter");
57
+ const context_1 = require("../context");
51
58
  const adapter_factory_1 = require("./adapters/adapter.factory");
52
59
  const query_builder_1 = require("./operators/query-builder");
53
60
  const aggregation_builder_1 = require("./operators/aggregation-builder");
54
- const transaction_manager_1 = require("./transactions/transaction-manager");
61
+ const transaction_manager_2 = require("./transactions/transaction-manager");
55
62
  const schema_manager_1 = require("./schema/schema-manager");
56
63
  const migration_engine_1 = require("./migrations/migration-engine");
57
64
  const action_manager_1 = require("./actions/action-manager");
@@ -94,6 +101,7 @@ class DatabaseService {
94
101
  this.adapters = new Map();
95
102
  this.connectionContexts = new Map();
96
103
  this.currentContext = null;
104
+ this.transactionScopes = new WeakMap();
97
105
  this.schemaManagers = new Map();
98
106
  this.migrationEngines = new Map();
99
107
  /** Service configuration */
@@ -116,7 +124,7 @@ class DatabaseService {
116
124
  this.config = config || null;
117
125
  this.preInitializedProductBuilder = config === null || config === void 0 ? void 0 : config.preInitializedProductBuilder;
118
126
  this.adapterFactory = new adapter_factory_1.AdapterFactory();
119
- this.transactionManager = new transaction_manager_1.TransactionManager();
127
+ this.transactionManager = new transaction_manager_2.TransactionManager();
120
128
  this.actionManager = new action_manager_1.ActionManager(this);
121
129
  this._privateKey = (config === null || config === void 0 ? void 0 : config.private_key) || '';
122
130
  if ((config === null || config === void 0 ? void 0 : config.default_product) || (config === null || config === void 0 ? void 0 : config.default_env)) {
@@ -1973,7 +1981,109 @@ class DatabaseService {
1973
1981
  async beginTransaction(options) {
1974
1982
  await this.ensureAdapterFor(options);
1975
1983
  const adapter = this.getAdapter(options);
1976
- return this.transactionManager.beginTransaction(adapter, options);
1984
+ const transaction = await this.transactionManager.beginTransaction(adapter, options);
1985
+ this.transactionScopes.set(transaction, JSON.stringify([options.product, options.env, options.database]));
1986
+ return transaction;
1987
+ }
1988
+ /** Bounded ACID batch. Unsupported adapters and configured triggers fail closed. */
1989
+ async atomicBatch(options) {
1990
+ var _a;
1991
+ const logs = [];
1992
+ const batch = { id: (0, processor_utils_1.generateObjectId)(), logs, system: !!options.system };
1993
+ let outcome = 'not_started';
1994
+ try {
1995
+ const result = await (0, context_1.runWithExecutionContext)((0, context_1.createExecutionContext)({ database_batch: batch }), () => (0, atomic_batch_1.executeAtomicBatch)({
1996
+ preflight: async (scope) => {
1997
+ var _a;
1998
+ await this.ensureAdapterFor(scope);
1999
+ if (((_a = this.currentContext) === null || _a === void 0 ? void 0 : _a.product) && this.currentContext.product !== scope.product) {
2000
+ throw new Error('Atomic batch product must match the active database context');
2001
+ }
2002
+ if (scope.session && !(await this.getProductBuilder(scope.product)).fetchPrivateKey()) {
2003
+ throw new Error('Atomic batch cannot verify the provided user session');
2004
+ }
2005
+ const adapter = this.getAdapter(scope);
2006
+ if (!(adapter instanceof mongodb_adapter_1.MongoDBAdapter || adapter instanceof postgresql_adapter_1.PostgreSQLAdapter || adapter instanceof mysql_adapter_1.MySQLAdapter || adapter instanceof dynamodb_adapter_1.DynamoDBAdapter)) {
2007
+ throw new Error('This adapter cannot provide the required multi-table ACID contract; Cassandra logged batches are not a substitute');
2008
+ }
2009
+ if (scope.transaction && (!(scope.transaction instanceof transaction_manager_1.Transaction) || !scope.transaction.belongsTo(adapter) || !scope.transaction.isActive()
2010
+ || this.transactionScopes.get(scope.transaction) !== JSON.stringify([scope.product, scope.env, scope.database]))) {
2011
+ throw new Error('Atomic batch transaction does not belong to the active database connection');
2012
+ }
2013
+ if (scope.transaction && (adapter instanceof postgresql_adapter_1.PostgreSQLAdapter || adapter instanceof mysql_adapter_1.MySQLAdapter)
2014
+ && scope.transaction.isolationLevel !== enums_1.IsolationLevel.SERIALIZABLE) {
2015
+ throw new Error('Borrowed SQL batch transactions must use SERIALIZABLE isolation');
2016
+ }
2017
+ const triggers = await this.fetchTriggers(scope.product, scope.database);
2018
+ if (triggers.length)
2019
+ throw new Error('Atomic batch does not yet support configured triggers; use a transaction-local outbox');
2020
+ if (adapter instanceof mongodb_adapter_1.MongoDBAdapter)
2021
+ await adapter.assertAtomicBatchSupport([...new Set(scope.operations.map(op => op.table))]);
2022
+ },
2023
+ executeNative: async (scope) => {
2024
+ var _a, _b, _c, _d;
2025
+ const adapter = this.getAdapter(scope);
2026
+ if (!(adapter instanceof dynamodb_adapter_1.DynamoDBAdapter))
2027
+ return undefined;
2028
+ let resolved = scope;
2029
+ let sessionLogFields = {};
2030
+ if (scope.session) {
2031
+ const builder = await this.getProductBuilder(scope.product);
2032
+ const { processSessionForExecution } = await Promise.resolve().then(() => __importStar(require('../sessions')));
2033
+ const result = await processSessionForExecution(scope.session, builder.fetchPrivateKey(), scope, scope.env, {
2034
+ fetchSessionSelector: async (tag) => { var _a; return (_a = (await builder.fetchSession(tag))) === null || _a === void 0 ? void 0 : _a.selector; },
2035
+ });
2036
+ if (result.error)
2037
+ throw new atomic_batch_1.AtomicBatchError('BATCH_VALIDATION', 'not_started');
2038
+ resolved = Object.assign(Object.assign({}, scope), { operations: result.input.operations });
2039
+ sessionLogFields = result.logFields;
2040
+ }
2041
+ this.initializeLogService();
2042
+ const start = Date.now();
2043
+ try {
2044
+ const result = await adapter.atomicBatch(resolved);
2045
+ (_a = this.logService) === null || _a === void 0 ? void 0 : _a.add(Object.assign({ product_tag: scope.product, workspace_id: ((_b = this.config) === null || _b === void 0 ? void 0 : _b.workspace_id) || '', env: scope.env, app_env: scope.env, process_id: result.transactionId, type: logs_types_1.LogEventTypes.DATABASE, parent_tag: scope.database, child_tag: 'atomicBatch', start, end: Date.now(), message: 'Database atomic batch - success', status: logs_types_1.LogEventStatus.SUCCESS, successful_execution: true, data: { operations: scope.operations.map(op => ({ name: op.name, operation: op.operation, table: op.table })) } }, sessionLogFields));
2046
+ return result;
2047
+ }
2048
+ catch (error) {
2049
+ (_c = this.logService) === null || _c === void 0 ? void 0 : _c.add(Object.assign({ product_tag: scope.product, workspace_id: ((_d = this.config) === null || _d === void 0 ? void 0 : _d.workspace_id) || '', env: scope.env, app_env: scope.env, process_id: batch.id, type: logs_types_1.LogEventTypes.DATABASE, parent_tag: scope.database, child_tag: 'atomicBatch', start, end: Date.now(), message: 'Database atomic batch - failed', status: logs_types_1.LogEventStatus.FAIL, failed_execution: true, data: { outcome: error instanceof atomic_batch_1.AtomicBatchError ? error.outcome : 'not_started' } }, sessionLogFields));
2050
+ throw error;
2051
+ }
2052
+ },
2053
+ validateTransaction: async (scope, transaction) => {
2054
+ const adapter = this.getAdapter(scope);
2055
+ if (adapter instanceof postgresql_adapter_1.PostgreSQLAdapter || adapter instanceof mysql_adapter_1.MySQLAdapter) {
2056
+ await adapter.assertAtomicBatchSupport([...new Set(scope.operations.map(op => op.table))], transaction);
2057
+ }
2058
+ },
2059
+ begin: (scope) => this.beginTransaction({ product: scope.product, env: scope.env, database: scope.database, isolationLevel: enums_1.IsolationLevel.SERIALIZABLE }),
2060
+ execute: async (op, scope, transaction) => {
2061
+ const input = { product: scope.product, env: scope.env, database: scope.database, session: scope.session,
2062
+ table: op.table, transaction, returning: op.returning };
2063
+ switch (op.operation) {
2064
+ case 'insert': return this.insert(Object.assign(Object.assign({}, input), { data: op.data }));
2065
+ case 'update': return this.update(Object.assign(Object.assign({}, input), { where: op.where, data: op.data }));
2066
+ case 'delete': return this.delete(Object.assign(Object.assign({}, input), { where: op.where }));
2067
+ case 'upsert': return this.upsert(Object.assign(Object.assign({}, input), { data: op.data, conflictKeys: op.conflictKeys }));
2068
+ }
2069
+ },
2070
+ }, options));
2071
+ outcome = result.status;
2072
+ return result;
2073
+ }
2074
+ catch (error) {
2075
+ outcome = error instanceof atomic_batch_1.AtomicBatchError ? error.outcome : 'not_started';
2076
+ throw error;
2077
+ }
2078
+ finally {
2079
+ for (const emit of logs) {
2080
+ try {
2081
+ emit(outcome);
2082
+ }
2083
+ catch ( /* Telemetry cannot change transaction outcome. */_b) { /* Telemetry cannot change transaction outcome. */ }
2084
+ }
2085
+ void ((_a = this.logService) === null || _a === void 0 ? void 0 : _a.publish().catch(() => { }));
2086
+ }
1977
2087
  }
1978
2088
  // ==================== SCHEMA OPERATIONS ====================
1979
2089
  /**
@@ -2839,6 +2949,9 @@ class DatabaseService {
2839
2949
  return await fn(adapter);
2840
2950
  }
2841
2951
  catch (error) {
2952
+ // An uncertain transactional write must never be replayed on a fresh connection.
2953
+ if (options.transaction)
2954
+ throw error;
2842
2955
  if (!this.isConnectionError(error))
2843
2956
  throw error;
2844
2957
  await this.connect({