@ductape/sdk 0.4.4 → 0.4.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/context/execution-context.d.ts +6 -0
- package/dist/context/execution-context.js +1 -1
- package/dist/context/execution-context.js.map +1 -1
- package/dist/database/adapters/dynamodb.adapter.d.ts +2 -0
- package/dist/database/adapters/dynamodb.adapter.js +18 -1
- package/dist/database/adapters/dynamodb.adapter.js.map +1 -1
- package/dist/database/adapters/mongodb.adapter.d.ts +4 -0
- package/dist/database/adapters/mongodb.adapter.js +58 -14
- package/dist/database/adapters/mongodb.adapter.js.map +1 -1
- package/dist/database/adapters/mysql.adapter.d.ts +2 -0
- package/dist/database/adapters/mysql.adapter.js +90 -15
- package/dist/database/adapters/mysql.adapter.js.map +1 -1
- package/dist/database/adapters/postgresql.adapter.d.ts +2 -0
- package/dist/database/adapters/postgresql.adapter.js +24 -4
- package/dist/database/adapters/postgresql.adapter.js.map +1 -1
- package/dist/database/atomic-batch.d.ts +58 -0
- package/dist/database/atomic-batch.js +183 -0
- package/dist/database/atomic-batch.js.map +1 -0
- package/dist/database/atomic-pipeline.d.ts +27 -0
- package/dist/database/atomic-pipeline.js +118 -0
- package/dist/database/atomic-pipeline.js.map +1 -0
- package/dist/database/databases.service.d.ts +9 -0
- package/dist/database/databases.service.js +234 -3
- package/dist/database/databases.service.js.map +1 -1
- package/dist/database/dynamodb-atomic-batch.d.ts +8 -0
- package/dist/database/dynamodb-atomic-batch.js +153 -0
- package/dist/database/dynamodb-atomic-batch.js.map +1 -0
- package/dist/database/index.d.ts +2 -0
- package/dist/database/index.js +4 -2
- package/dist/database/index.js.map +1 -1
- package/dist/database/sql-context.d.ts +17 -0
- package/dist/database/sql-context.js +51 -0
- package/dist/database/sql-context.js.map +1 -0
- package/dist/database/transactions/transaction-manager.d.ts +5 -1
- package/dist/database/transactions/transaction-manager.js +13 -2
- package/dist/database/transactions/transaction-manager.js.map +1 -1
- package/dist/database/types/index.d.ts +1 -0
- package/dist/database/types/pipeline.interface.d.ts +19 -0
- package/dist/database/types/pipeline.interface.js +22 -0
- package/dist/database/types/pipeline.interface.js.map +1 -0
- package/dist/index.d.ts +9 -1
- package/dist/index.js +17 -2
- package/dist/index.js.map +1 -1
- package/dist/logs/logs.service.js +10 -0
- package/dist/logs/logs.service.js.map +1 -1
- package/dist/processor/services/processor.service.d.ts +2 -2
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,58 @@
|
|
|
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_PREFLIGHT_FAILED' | '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
|
+
readonly diagnostic?: {
|
|
39
|
+
phase: 'preflight';
|
|
40
|
+
providerCode?: number;
|
|
41
|
+
};
|
|
42
|
+
constructor(code: 'BATCH_VALIDATION' | 'BATCH_PREFLIGHT_FAILED' | 'BATCH_GUARD_FAILED' | 'BATCH_OPERATION_FAILED' | 'BATCH_COMMIT_UNKNOWN', outcome: 'not_started' | 'aborted' | 'rollback_unknown' | 'commit_unknown', operation?: string, transactionId?: string, diagnostic?: {
|
|
43
|
+
phase: 'preflight';
|
|
44
|
+
providerCode?: number;
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
export interface AtomicBatchHost {
|
|
48
|
+
preflight(options: AtomicBatchOptions): Promise<void>;
|
|
49
|
+
executeNative?(options: AtomicBatchOptions): Promise<AtomicBatchResult | undefined>;
|
|
50
|
+
begin(options: AtomicBatchOptions): Promise<ITransaction>;
|
|
51
|
+
validateTransaction?(options: AtomicBatchOptions, transaction: ITransaction): Promise<void>;
|
|
52
|
+
execute(operation: AtomicBatchOperation, options: AtomicBatchOptions, transaction: ITransaction): Promise<{
|
|
53
|
+
count: number;
|
|
54
|
+
}>;
|
|
55
|
+
}
|
|
56
|
+
export declare function validateAtomicBatch(options: AtomicBatchOptions): void;
|
|
57
|
+
/** Sequential ACID executor. No automatic write replay and no provider callbacks. */
|
|
58
|
+
export declare function executeAtomicBatch(host: AtomicBatchHost, options: AtomicBatchOptions): Promise<AtomicBatchResult>;
|
|
@@ -0,0 +1,183 @@
|
|
|
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, diagnostic) {
|
|
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.diagnostic = diagnostic;
|
|
15
|
+
this.name = 'AtomicBatchError';
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
exports.AtomicBatchError = AtomicBatchError;
|
|
19
|
+
const forbidden = new Set(['__proto__', 'constructor', 'prototype']);
|
|
20
|
+
function fail() { throw new AtomicBatchError('BATCH_VALIDATION', 'not_started'); }
|
|
21
|
+
function record(value) {
|
|
22
|
+
return !!value && typeof value === 'object' && !Array.isArray(value)
|
|
23
|
+
&& (Object.getPrototypeOf(value) === Object.prototype || Object.getPrototypeOf(value) === null);
|
|
24
|
+
}
|
|
25
|
+
function keys(value, allowed) {
|
|
26
|
+
if (Object.keys(value).some(key => !allowed.includes(key) || forbidden.has(key)))
|
|
27
|
+
fail();
|
|
28
|
+
}
|
|
29
|
+
function pointer(value, path) {
|
|
30
|
+
if (path === '')
|
|
31
|
+
return value;
|
|
32
|
+
if (!path.startsWith('/') || /~(?![01])/u.test(path))
|
|
33
|
+
fail();
|
|
34
|
+
for (const encoded of path.slice(1).split('/')) {
|
|
35
|
+
const key = encoded.replace(/~1/g, '/').replace(/~0/g, '~');
|
|
36
|
+
if (forbidden.has(key) || value === null || typeof value !== 'object' || !Object.prototype.hasOwnProperty.call(value, key))
|
|
37
|
+
fail();
|
|
38
|
+
value = value[key];
|
|
39
|
+
}
|
|
40
|
+
return value;
|
|
41
|
+
}
|
|
42
|
+
function walk(value, visit, depth = 0) {
|
|
43
|
+
if (depth > 32)
|
|
44
|
+
fail();
|
|
45
|
+
if (Array.isArray(value))
|
|
46
|
+
return value.map(item => walk(item, visit, depth + 1));
|
|
47
|
+
if (record(value)) {
|
|
48
|
+
if (Object.prototype.hasOwnProperty.call(value, '$batchRef')) {
|
|
49
|
+
keys(value, ['$batchRef']);
|
|
50
|
+
const ref = value.$batchRef;
|
|
51
|
+
if (!record(ref))
|
|
52
|
+
fail();
|
|
53
|
+
keys(ref, ['operation', 'path']);
|
|
54
|
+
if (typeof ref.operation !== 'string' || typeof ref.path !== 'string')
|
|
55
|
+
fail();
|
|
56
|
+
return visit(ref);
|
|
57
|
+
}
|
|
58
|
+
return Object.fromEntries(Object.entries(value).map(([key, child]) => {
|
|
59
|
+
if (forbidden.has(key))
|
|
60
|
+
fail();
|
|
61
|
+
return [key, walk(child, visit, depth + 1)];
|
|
62
|
+
}));
|
|
63
|
+
}
|
|
64
|
+
if (value !== null && typeof value === 'object')
|
|
65
|
+
fail();
|
|
66
|
+
if (typeof value === 'function' || typeof value === 'symbol' || typeof value === 'bigint' || value === undefined)
|
|
67
|
+
fail();
|
|
68
|
+
if (typeof value === 'number' && !Number.isFinite(value))
|
|
69
|
+
fail();
|
|
70
|
+
return value;
|
|
71
|
+
}
|
|
72
|
+
function validateAtomicBatch(options) {
|
|
73
|
+
if (!record(options))
|
|
74
|
+
fail();
|
|
75
|
+
keys(options, ['product', 'env', 'database', 'session', 'system', 'transaction', 'operations']);
|
|
76
|
+
for (const value of [options.product, options.env, options.database])
|
|
77
|
+
if (typeof value !== 'string' || !value.trim())
|
|
78
|
+
fail();
|
|
79
|
+
if (options.session !== undefined && (typeof options.session !== 'string' || !options.session.trim()))
|
|
80
|
+
fail();
|
|
81
|
+
if (options.system !== undefined) {
|
|
82
|
+
if (!record(options.system))
|
|
83
|
+
fail();
|
|
84
|
+
keys(options.system, ['reason']);
|
|
85
|
+
if (typeof options.system.reason !== 'string' || !options.system.reason.trim())
|
|
86
|
+
fail();
|
|
87
|
+
}
|
|
88
|
+
if (!!options.session === !!options.system)
|
|
89
|
+
fail();
|
|
90
|
+
if (!Array.isArray(options.operations) || !options.operations.length || options.operations.length > 100)
|
|
91
|
+
fail();
|
|
92
|
+
const prior = new Set();
|
|
93
|
+
for (const op of options.operations) {
|
|
94
|
+
if (!record(op))
|
|
95
|
+
fail();
|
|
96
|
+
keys(op, ['name', 'operation', 'table', 'data', 'where', 'conflictKeys', 'returning', 'expectedCount', 'project']);
|
|
97
|
+
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))
|
|
98
|
+
fail();
|
|
99
|
+
if (typeof op.table !== 'string' || !op.table.trim() || op.table.startsWith('_ductape_') || op.table.startsWith('system.'))
|
|
100
|
+
fail();
|
|
101
|
+
if (!['insert', 'update', 'delete', 'upsert'].includes(op.operation))
|
|
102
|
+
fail();
|
|
103
|
+
if (op.operation !== 'delete' && !record(op.data) && !(op.operation === 'insert' && Array.isArray(op.data) && op.data.length && op.data.every(record)))
|
|
104
|
+
fail();
|
|
105
|
+
if (['update', 'delete'].includes(op.operation) && (!record(op.where) || !Object.keys(op.where).length))
|
|
106
|
+
fail();
|
|
107
|
+
if (op.operation === 'upsert' && (!Array.isArray(op.conflictKeys) || !op.conflictKeys.length || op.conflictKeys.some(key => typeof key !== 'string' || !key || forbidden.has(key))))
|
|
108
|
+
fail();
|
|
109
|
+
if (op.returning !== undefined && typeof op.returning !== 'boolean')
|
|
110
|
+
fail();
|
|
111
|
+
if (op.expectedCount !== undefined && (!Number.isSafeInteger(op.expectedCount) || op.expectedCount < 0))
|
|
112
|
+
fail();
|
|
113
|
+
if (op.project !== undefined && (!record(op.project) || Object.values(op.project).some(path => typeof path !== 'string' || (path !== '' && !path.startsWith('/')))))
|
|
114
|
+
fail();
|
|
115
|
+
walk(op, ref => { if (!prior.has(ref.operation))
|
|
116
|
+
fail(); return null; });
|
|
117
|
+
prior.add(op.name);
|
|
118
|
+
}
|
|
119
|
+
if (Buffer.byteLength(JSON.stringify(options.operations)) > 1024 * 1024)
|
|
120
|
+
fail();
|
|
121
|
+
}
|
|
122
|
+
/** Sequential ACID executor. No automatic write replay and no provider callbacks. */
|
|
123
|
+
async function executeAtomicBatch(host, options) {
|
|
124
|
+
var _a, _b, _c, _d;
|
|
125
|
+
validateAtomicBatch(options);
|
|
126
|
+
// Snapshot caller-owned inputs before awaiting; preserve the opaque session and transaction.
|
|
127
|
+
options = Object.assign(Object.assign({}, options), { operations: JSON.parse(JSON.stringify(options.operations)) });
|
|
128
|
+
// References are resolved later, so use a JSON snapshot retaining their declarative form.
|
|
129
|
+
try {
|
|
130
|
+
await host.preflight(options);
|
|
131
|
+
}
|
|
132
|
+
catch (error) {
|
|
133
|
+
if (error instanceof AtomicBatchError)
|
|
134
|
+
throw error;
|
|
135
|
+
const code = error === null || error === void 0 ? void 0 : error.code;
|
|
136
|
+
throw new AtomicBatchError('BATCH_PREFLIGHT_FAILED', 'not_started', undefined, (_a = options.transaction) === null || _a === void 0 ? void 0 : _a.id, Object.assign({ phase: 'preflight' }, (typeof code === 'number' && Number.isSafeInteger(code) ? { providerCode: code } : {})));
|
|
137
|
+
}
|
|
138
|
+
const native = await ((_b = host.executeNative) === null || _b === void 0 ? void 0 : _b.call(host, options));
|
|
139
|
+
if (native)
|
|
140
|
+
return native;
|
|
141
|
+
const transaction = (_c = options.transaction) !== null && _c !== void 0 ? _c : await host.begin(options);
|
|
142
|
+
const results = Object.create(null);
|
|
143
|
+
const projected = Object.create(null);
|
|
144
|
+
let operation;
|
|
145
|
+
let guard = false;
|
|
146
|
+
try {
|
|
147
|
+
await ((_d = host.validateTransaction) === null || _d === void 0 ? void 0 : _d.call(host, options, transaction));
|
|
148
|
+
for (const source of options.operations) {
|
|
149
|
+
operation = source.name;
|
|
150
|
+
if (!transaction.isActive())
|
|
151
|
+
throw new Error('inactive transaction');
|
|
152
|
+
const op = walk(source, ref => pointer(results[ref.operation], ref.path));
|
|
153
|
+
const result = await host.execute(op, options, transaction);
|
|
154
|
+
if (op.expectedCount !== undefined && result.count !== op.expectedCount) {
|
|
155
|
+
guard = true;
|
|
156
|
+
throw new Error('count guard failed');
|
|
157
|
+
}
|
|
158
|
+
results[op.name] = result;
|
|
159
|
+
projected[op.name] = op.project ? Object.fromEntries(Object.entries(op.project).map(([key, path]) => [key, pointer(result, path)])) : result;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
catch (_e) {
|
|
163
|
+
let outcome = transaction.status === 'rolled_back' ? 'aborted' : 'rollback_unknown';
|
|
164
|
+
try {
|
|
165
|
+
if (transaction.isActive()) {
|
|
166
|
+
await transaction.rollback();
|
|
167
|
+
outcome = 'aborted';
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
catch ( /* Preserve uncertainty, not the provider payload. */_f) { /* Preserve uncertainty, not the provider payload. */ }
|
|
171
|
+
throw new AtomicBatchError(guard ? 'BATCH_GUARD_FAILED' : 'BATCH_OPERATION_FAILED', outcome, operation, transaction.id);
|
|
172
|
+
}
|
|
173
|
+
if (options.transaction)
|
|
174
|
+
return { status: 'pending', transactionId: transaction.id, results: projected };
|
|
175
|
+
try {
|
|
176
|
+
await transaction.commit();
|
|
177
|
+
}
|
|
178
|
+
catch (_g) {
|
|
179
|
+
throw new AtomicBatchError('BATCH_COMMIT_UNKNOWN', 'commit_unknown', undefined, transaction.id);
|
|
180
|
+
}
|
|
181
|
+
return { status: 'committed', transactionId: transaction.id, results: projected };
|
|
182
|
+
}
|
|
183
|
+
//# 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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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { AtomicBatchOptions, AtomicBatchResult } from './atomic-batch';
|
|
2
|
+
import { IMongoPipelineOptions, IMongoPipelineResult } from './types/pipeline.interface';
|
|
3
|
+
import { ITransaction } from './types/transaction.interface';
|
|
4
|
+
import { SqlContext } from './sql-context';
|
|
5
|
+
export interface AtomicPipelineOptions extends AtomicBatchOptions {
|
|
6
|
+
/** Exactly one bounded, ownership-filtered MongoDB aggregation. */
|
|
7
|
+
context: {
|
|
8
|
+
table: string;
|
|
9
|
+
pipeline?: IMongoPipelineOptions['pipeline'];
|
|
10
|
+
sql?: SqlContext;
|
|
11
|
+
maxTimeMS?: number;
|
|
12
|
+
expectedCount: number;
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
export interface AtomicPipelineResult<T> extends AtomicBatchResult {
|
|
16
|
+
context: IMongoPipelineResult<T>;
|
|
17
|
+
}
|
|
18
|
+
/** Intentionally constrained initial contract; no callbacks or hidden sequential reads. */
|
|
19
|
+
export declare function validateAtomicContext(context: AtomicPipelineOptions['context']): void;
|
|
20
|
+
/** Resolve explicit context references only; batch references remain for the write executor. */
|
|
21
|
+
export declare function resolveContext(value: unknown, context: unknown): any;
|
|
22
|
+
export declare function executeAtomicPipeline<T>(options: AtomicPipelineOptions, host: {
|
|
23
|
+
preflight(options: AtomicPipelineOptions): Promise<void>;
|
|
24
|
+
begin(): Promise<ITransaction>;
|
|
25
|
+
read(transaction: ITransaction): Promise<IMongoPipelineResult<T>>;
|
|
26
|
+
write(options: AtomicBatchOptions): Promise<AtomicBatchResult>;
|
|
27
|
+
}): Promise<AtomicPipelineResult<T>>;
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __rest = (this && this.__rest) || function (s, e) {
|
|
3
|
+
var t = {};
|
|
4
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
5
|
+
t[p] = s[p];
|
|
6
|
+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
7
|
+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
8
|
+
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
9
|
+
t[p[i]] = s[p[i]];
|
|
10
|
+
}
|
|
11
|
+
return t;
|
|
12
|
+
};
|
|
13
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
14
|
+
exports.validateAtomicContext = validateAtomicContext;
|
|
15
|
+
exports.resolveContext = resolveContext;
|
|
16
|
+
exports.executeAtomicPipeline = executeAtomicPipeline;
|
|
17
|
+
const atomic_batch_1 = require("./atomic-batch");
|
|
18
|
+
const pipeline_interface_1 = require("./types/pipeline.interface");
|
|
19
|
+
const sql_context_1 = require("./sql-context");
|
|
20
|
+
/** Intentionally constrained initial contract; no callbacks or hidden sequential reads. */
|
|
21
|
+
function validateAtomicContext(context) {
|
|
22
|
+
if (!context || !context.table || !Number.isSafeInteger(context.expectedCount) || context.expectedCount < 0 || context.expectedCount > 100)
|
|
23
|
+
throw new atomic_batch_1.AtomicBatchError('BATCH_VALIDATION', 'not_started');
|
|
24
|
+
if (context.sql) {
|
|
25
|
+
if (context.pipeline)
|
|
26
|
+
throw new Error('Choose SQL context or MongoDB pipeline, not both');
|
|
27
|
+
(0, sql_context_1.compileSqlContext)(context.table, context.sql, true);
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
(0, pipeline_interface_1.validateReadPipeline)(context.pipeline);
|
|
31
|
+
const validate = (pipeline, depth) => {
|
|
32
|
+
if (depth > 4 || !pipeline.length || pipeline.length > 32)
|
|
33
|
+
throw new Error('Pipeline stage/depth limit exceeded');
|
|
34
|
+
const match = pipeline[0].$match;
|
|
35
|
+
if (!match || typeof match !== 'object' || !Object.keys(match).length)
|
|
36
|
+
throw new Error('Each context/lookup requires an explicit ownership match');
|
|
37
|
+
if (!pipeline.some(stage => stage.$project && typeof stage.$project === 'object'))
|
|
38
|
+
throw new Error('Each context/lookup requires a projection');
|
|
39
|
+
const limit = pipeline[pipeline.length - 1].$limit;
|
|
40
|
+
if (!Number.isInteger(limit) || Number(limit) < 1 || Number(limit) > 100)
|
|
41
|
+
throw new Error('Each context/lookup must end with a limit of 1-100');
|
|
42
|
+
for (const stage of pipeline) {
|
|
43
|
+
const key = Object.keys(stage)[0];
|
|
44
|
+
if (!['$match', '$project', '$limit', '$sort', '$lookup', '$set', '$addFields', '$unwind'].includes(key))
|
|
45
|
+
throw new Error(`Unsupported atomic context stage: ${key}`);
|
|
46
|
+
if (key === '$lookup') {
|
|
47
|
+
const lookup = stage.$lookup;
|
|
48
|
+
if (!Array.isArray(lookup === null || lookup === void 0 ? void 0 : lookup.pipeline))
|
|
49
|
+
throw new Error('Lookup requires a filtered pipeline');
|
|
50
|
+
validate(lookup.pipeline, depth + 1);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
validate(context.pipeline, 0);
|
|
55
|
+
}
|
|
56
|
+
/** Resolve explicit context references only; batch references remain for the write executor. */
|
|
57
|
+
function resolveContext(value, context) {
|
|
58
|
+
if (Array.isArray(value))
|
|
59
|
+
return value.map(item => resolveContext(item, context));
|
|
60
|
+
if (!value || typeof value !== 'object')
|
|
61
|
+
return value;
|
|
62
|
+
const object = value;
|
|
63
|
+
if ('$contextRef' in object) {
|
|
64
|
+
if (Object.keys(object).length !== 1 || typeof object.$contextRef !== 'string' || !object.$contextRef.startsWith('/'))
|
|
65
|
+
throw new Error('Invalid context reference');
|
|
66
|
+
let result = context;
|
|
67
|
+
for (const part of object.$contextRef.split('/').slice(1)) {
|
|
68
|
+
if (/~(?![01])/.test(part))
|
|
69
|
+
throw new Error('Invalid context pointer');
|
|
70
|
+
const key = part.replace(/~1/g, '/').replace(/~0/g, '~');
|
|
71
|
+
if (['__proto__', 'prototype', 'constructor'].includes(key) || !result || !Object.prototype.hasOwnProperty.call(result, key))
|
|
72
|
+
throw new Error('Missing context reference');
|
|
73
|
+
result = result[key];
|
|
74
|
+
}
|
|
75
|
+
return result;
|
|
76
|
+
}
|
|
77
|
+
return Object.fromEntries(Object.entries(object).map(([key, child]) => [key, resolveContext(child, context)]));
|
|
78
|
+
}
|
|
79
|
+
async function executeAtomicPipeline(options, host) {
|
|
80
|
+
var _a;
|
|
81
|
+
const { context } = options, batch = __rest(options, ["context"]);
|
|
82
|
+
(0, atomic_batch_1.validateAtomicBatch)(batch);
|
|
83
|
+
validateAtomicContext(context);
|
|
84
|
+
if (Buffer.byteLength(JSON.stringify({ context, operations: batch.operations })) > 1024 * 1024)
|
|
85
|
+
throw new atomic_batch_1.AtomicBatchError('BATCH_VALIDATION', 'not_started');
|
|
86
|
+
await host.preflight(options);
|
|
87
|
+
const transaction = (_a = options.transaction) !== null && _a !== void 0 ? _a : await host.begin();
|
|
88
|
+
let result;
|
|
89
|
+
let read;
|
|
90
|
+
try {
|
|
91
|
+
read = await host.read(transaction);
|
|
92
|
+
if (read.count !== context.expectedCount)
|
|
93
|
+
throw new atomic_batch_1.AtomicBatchError('BATCH_GUARD_FAILED', 'not_started');
|
|
94
|
+
const operations = resolveContext(batch.operations, read);
|
|
95
|
+
result = await host.write(Object.assign(Object.assign({}, batch), { operations, transaction }));
|
|
96
|
+
}
|
|
97
|
+
catch (error) {
|
|
98
|
+
let outcome = transaction.status === 'rolled_back' ? 'aborted' : 'rollback_unknown';
|
|
99
|
+
try {
|
|
100
|
+
if (transaction.isActive()) {
|
|
101
|
+
await transaction.rollback();
|
|
102
|
+
outcome = 'aborted';
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
catch ( /* No replay. */_b) { /* No replay. */ }
|
|
106
|
+
throw new atomic_batch_1.AtomicBatchError(error instanceof atomic_batch_1.AtomicBatchError ? error.code : 'BATCH_OPERATION_FAILED', outcome, undefined, transaction.id);
|
|
107
|
+
}
|
|
108
|
+
if (!options.transaction) {
|
|
109
|
+
try {
|
|
110
|
+
await transaction.commit();
|
|
111
|
+
}
|
|
112
|
+
catch (_c) {
|
|
113
|
+
throw new atomic_batch_1.AtomicBatchError('BATCH_COMMIT_UNKNOWN', 'commit_unknown', undefined, transaction.id);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
return Object.assign(Object.assign({}, result), { status: options.transaction ? 'pending' : 'committed', context: read });
|
|
117
|
+
}
|
|
118
|
+
//# sourceMappingURL=atomic-pipeline.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"atomic-pipeline.js","sourceRoot":"","sources":["../../src/database/atomic-pipeline.ts"],"names":[],"mappings":"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"}
|
|
@@ -8,8 +8,11 @@
|
|
|
8
8
|
* Supported databases: PostgreSQL, MySQL, MongoDB, DynamoDB, MariaDB, Cassandra
|
|
9
9
|
*/
|
|
10
10
|
import { IConnectionConfig, IConnectionResult, IConnectionContext, IDatabaseConfig } from './types/connection.interface';
|
|
11
|
+
import { IMongoPipelineOptions, IMongoPipelineResult } from './types/pipeline.interface';
|
|
12
|
+
import { AtomicPipelineOptions, AtomicPipelineResult } from './atomic-pipeline';
|
|
11
13
|
import { IQueryOptions, IQueryResult, IRawQueryOptions, IRawQueryResult } from './types/query.interface';
|
|
12
14
|
import { IInsertOptions, IInsertResult, IUpdateOptions, IUpdateResult, IDeleteOptions, IDeleteResult, IUpsertOptions, IUpsertResult } from './types/write.interface';
|
|
15
|
+
import { AtomicBatchOptions, AtomicBatchResult } from './atomic-batch';
|
|
13
16
|
import { ITransactionOptions, ITransaction, ITransactionCallback } from './types/transaction.interface';
|
|
14
17
|
import { IAggregateOptions, IAggregateResult, ICountOptions, ISumOptions, IAvgOptions, IMinMaxOptions, IGroupByOptions, IGroupByResult } from './types/aggregation.interface';
|
|
15
18
|
import { ICreateTableOptions, ITableDefinition, IAlterTableOperation, ITableSchema, ITableInfo, ICreateIndexOptions, IDropIndexOptions, IListIndexesOptions, IIndexInfo, IIndexStatistics, SimpleFieldType, ISimpleFieldDefinition, ISimpleSchemaDefinition, ISimpleCreateOptions, ISimpleDropOptions, ISimpleIndexOptions } from './types/schema.interface';
|
|
@@ -67,6 +70,7 @@ export declare class DatabaseService {
|
|
|
67
70
|
private currentContext;
|
|
68
71
|
private adapterFactory;
|
|
69
72
|
private transactionManager;
|
|
73
|
+
private readonly transactionScopes;
|
|
70
74
|
private schemaManagers;
|
|
71
75
|
private migrationEngines;
|
|
72
76
|
private actionManager;
|
|
@@ -542,6 +546,8 @@ export declare class DatabaseService {
|
|
|
542
546
|
* where: { status: 'completed' },
|
|
543
547
|
* });
|
|
544
548
|
*/
|
|
549
|
+
atomicPipeline<T = Record<string, unknown>>(options: AtomicPipelineOptions): Promise<AtomicPipelineResult<T>>;
|
|
550
|
+
aggregatePipeline<T = Record<string, unknown>>(options: IMongoPipelineOptions): Promise<IMongoPipelineResult<T>>;
|
|
545
551
|
aggregate(options: IAggregateOptions): Promise<IAggregateResult>;
|
|
546
552
|
/**
|
|
547
553
|
* Group records and perform aggregations
|
|
@@ -605,6 +611,8 @@ export declare class DatabaseService {
|
|
|
605
611
|
* }
|
|
606
612
|
*/
|
|
607
613
|
beginTransaction(options: ITransactionOptions): Promise<ITransaction>;
|
|
614
|
+
/** Bounded ACID batch. Unsupported adapters and configured triggers fail closed. */
|
|
615
|
+
atomicBatch(options: AtomicBatchOptions): Promise<AtomicBatchResult>;
|
|
608
616
|
/**
|
|
609
617
|
* Create a new table
|
|
610
618
|
*
|
|
@@ -1391,6 +1399,7 @@ export declare class DatabaseConnection {
|
|
|
1391
1399
|
min(options: Omit<IMinMaxOptions, 'database' | 'env'>): Promise<any>;
|
|
1392
1400
|
max(options: Omit<IMinMaxOptions, 'database' | 'env'>): Promise<any>;
|
|
1393
1401
|
aggregate(options: Omit<IAggregateOptions, 'database' | 'env'>): Promise<IAggregateResult>;
|
|
1402
|
+
aggregatePipeline<T = Record<string, unknown>>(options: Omit<IMongoPipelineOptions, 'product' | 'database' | 'env'>): Promise<IMongoPipelineResult<T>>;
|
|
1394
1403
|
groupBy<T = any>(options: Omit<IGroupByOptions, 'database' | 'env'>): Promise<IGroupByResult<T>[]>;
|
|
1395
1404
|
/**
|
|
1396
1405
|
* Trigger operations scoped to this connection
|