@danceroutine/tango-orm 1.3.0 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/PostgresAdapter-BFdo_nIt.js +4 -0
- package/dist/{PostgresAdapter-C9a1XJRx.js → PostgresAdapter-CMiEpHya.js} +4 -52
- package/dist/PostgresAdapter-CMiEpHya.js.map +1 -0
- package/dist/PostgresClient-BQJZfEOT.js +68 -0
- package/dist/PostgresClient-BQJZfEOT.js.map +1 -0
- package/dist/SqliteAdapter-A-P9zUhP.js +4 -0
- package/dist/SqliteAdapter-CeqhyrPC.js +44 -0
- package/dist/SqliteAdapter-CeqhyrPC.js.map +1 -0
- package/dist/{SqliteAdapter-Dp6VRXmz.js → SqliteClient-CjOK9-ki.js} +20 -41
- package/dist/SqliteClient-CjOK9-ki.js.map +1 -0
- package/dist/connection/clients/DBClient.d.ts +6 -0
- package/dist/connection/clients/dialects/PostgresClient.d.ts +21 -5
- package/dist/connection/clients/dialects/SqliteClient.d.ts +13 -1
- package/dist/connection/index.js +5 -3
- package/dist/{connection-CVvycXus.js → connection-B_K2ZAf7.js} +7 -5
- package/dist/{connection-CVvycXus.js.map → connection-B_K2ZAf7.js.map} +1 -1
- package/dist/defaultRuntime-BPK9kWEW.js +447 -0
- package/dist/defaultRuntime-BPK9kWEW.js.map +1 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.js +12 -9
- package/dist/manager/ModelManager.d.ts +2 -0
- package/dist/manager/index.js +6 -6
- package/dist/manager/internal/RuntimeBoundClient.d.ts +5 -1
- package/dist/{manager-D6tU8xTO.js → manager-C6oJ2tAF.js} +2 -2
- package/dist/{manager-D6tU8xTO.js.map → manager-C6oJ2tAF.js.map} +1 -1
- package/dist/query/index.js +1 -1
- package/dist/{query-6GeNOf-w.js → query-CWZ1cfjo.js} +1 -1
- package/dist/{query-6GeNOf-w.js.map → query-CWZ1cfjo.js.map} +1 -1
- package/dist/{registerModelObjects-CXSI5ndy.js → registerModelObjects-Bva_f-Qh.js} +26 -118
- package/dist/registerModelObjects-Bva_f-Qh.js.map +1 -0
- package/dist/runtime/TangoRuntime.d.ts +17 -4
- package/dist/runtime/index.js +6 -6
- package/dist/runtime/internal/DBClientProvider.d.ts +12 -0
- package/dist/runtime/internal/PostgresDBClientProvider.d.ts +12 -0
- package/dist/runtime/internal/SqliteDBClientProvider.d.ts +16 -0
- package/dist/runtime/internal/createDBClientProvider.d.ts +5 -0
- package/dist/{runtime-7U5_XDad.js → runtime-ByXbpVBS.js} +3 -2
- package/dist/{runtime-7U5_XDad.js.map → runtime-ByXbpVBS.js.map} +1 -1
- package/dist/transaction/AtomicTransaction.d.ts +32 -0
- package/dist/transaction/UnitOfWork.d.ts +3 -0
- package/dist/transaction/atomic.d.ts +2 -0
- package/dist/transaction/index.d.ts +2 -0
- package/dist/transaction/index.js +5 -2
- package/dist/transaction/internal/context/AsyncLocalTransactionEngine.d.ts +21 -0
- package/dist/transaction/internal/context/CallbackRecord.d.ts +5 -0
- package/dist/transaction/internal/context/FrameBoundTransaction.d.ts +20 -0
- package/dist/transaction/internal/context/FrameTransactionHandle.d.ts +4 -0
- package/dist/transaction/internal/context/TransactionEngine.d.ts +16 -0
- package/dist/transaction/internal/context/TransactionFrame.d.ts +7 -0
- package/dist/transaction/internal/context/TransactionState.d.ts +10 -0
- package/dist/transaction/internal/context/index.d.ts +1 -0
- package/dist/{transaction-DooTMuAl.js → transaction-Cs0Z9tbW.js} +15 -3
- package/dist/transaction-Cs0Z9tbW.js.map +1 -0
- package/package.json +6 -6
- package/dist/PostgresAdapter-C9a1XJRx.js.map +0 -1
- package/dist/PostgresAdapter-CBc1u8eT.js +0 -3
- package/dist/SqliteAdapter-BJKNxCvS.js +0 -3
- package/dist/SqliteAdapter-Dp6VRXmz.js.map +0 -1
- package/dist/registerModelObjects-CXSI5ndy.js.map +0 -1
- package/dist/transaction-DooTMuAl.js.map +0 -1
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { InternalRelationKind, OrmSqlSafetyAdapter, QuerySet } from "./query-CWZ1cfjo.js";
|
|
2
|
+
import { RuntimeBoundClient, TransactionEngine, getTangoRuntime } from "./defaultRuntime-BPK9kWEW.js";
|
|
3
3
|
import { NotFoundError } from "@danceroutine/tango-core";
|
|
4
4
|
import { ModelRegistry, registerModelAugmentor } from "@danceroutine/tango-schema";
|
|
5
|
-
import { loadConfig, loadConfigFromProjectRoot } from "@danceroutine/tango-config";
|
|
6
5
|
|
|
7
6
|
//#region src/manager/internal/MutationCompiler.ts
|
|
8
7
|
var MutationCompiler = class {
|
|
@@ -48,34 +47,6 @@ var MutationCompiler = class {
|
|
|
48
47
|
}
|
|
49
48
|
};
|
|
50
49
|
|
|
51
|
-
//#endregion
|
|
52
|
-
//#region src/manager/internal/RuntimeBoundClient.ts
|
|
53
|
-
var RuntimeBoundClient = class {
|
|
54
|
-
constructor(runtime) {
|
|
55
|
-
this.runtime = runtime;
|
|
56
|
-
}
|
|
57
|
-
async query(sql, params) {
|
|
58
|
-
const client = await this.runtime.getClient();
|
|
59
|
-
return client.query(sql, params);
|
|
60
|
-
}
|
|
61
|
-
async begin() {
|
|
62
|
-
const client = await this.runtime.getClient();
|
|
63
|
-
await client.begin();
|
|
64
|
-
}
|
|
65
|
-
async commit() {
|
|
66
|
-
const client = await this.runtime.getClient();
|
|
67
|
-
await client.commit();
|
|
68
|
-
}
|
|
69
|
-
async rollback() {
|
|
70
|
-
const client = await this.runtime.getClient();
|
|
71
|
-
await client.rollback();
|
|
72
|
-
}
|
|
73
|
-
async close() {
|
|
74
|
-
const client = await this.runtime.getClient();
|
|
75
|
-
await client.close();
|
|
76
|
-
}
|
|
77
|
-
};
|
|
78
|
-
|
|
79
50
|
//#endregion
|
|
80
51
|
//#region src/manager/ModelManager.ts
|
|
81
52
|
const sqlSafetyAdapter = new OrmSqlSafetyAdapter();
|
|
@@ -87,8 +58,10 @@ var ModelManager = class ModelManager {
|
|
|
87
58
|
model;
|
|
88
59
|
client;
|
|
89
60
|
dialect;
|
|
61
|
+
runtime;
|
|
90
62
|
constructor(model, runtime) {
|
|
91
63
|
this.model = model;
|
|
64
|
+
this.runtime = runtime;
|
|
92
65
|
this.client = new RuntimeBoundClient(runtime);
|
|
93
66
|
this.dialect = runtime.getDialect();
|
|
94
67
|
this.mutationCompiler = new MutationCompiler(this.dialect);
|
|
@@ -174,7 +147,8 @@ var ModelManager = class ModelManager {
|
|
|
174
147
|
await this.model.hooks?.afterCreate?.({
|
|
175
148
|
record: created,
|
|
176
149
|
model: this.model,
|
|
177
|
-
manager: this
|
|
150
|
+
manager: this,
|
|
151
|
+
transaction: this.getHookTransaction()
|
|
178
152
|
});
|
|
179
153
|
return created;
|
|
180
154
|
}
|
|
@@ -197,7 +171,8 @@ var ModelManager = class ModelManager {
|
|
|
197
171
|
previous: current,
|
|
198
172
|
record: updated,
|
|
199
173
|
model: this.model,
|
|
200
|
-
manager: this
|
|
174
|
+
manager: this,
|
|
175
|
+
transaction: this.getHookTransaction()
|
|
201
176
|
});
|
|
202
177
|
return updated;
|
|
203
178
|
}
|
|
@@ -207,7 +182,8 @@ var ModelManager = class ModelManager {
|
|
|
207
182
|
id,
|
|
208
183
|
current,
|
|
209
184
|
model: this.model,
|
|
210
|
-
manager: this
|
|
185
|
+
manager: this,
|
|
186
|
+
transaction: this.getHookTransaction()
|
|
211
187
|
});
|
|
212
188
|
const validatedPlan = sqlSafetyAdapter.validate({
|
|
213
189
|
kind: "delete",
|
|
@@ -219,7 +195,8 @@ var ModelManager = class ModelManager {
|
|
|
219
195
|
id,
|
|
220
196
|
previous: current,
|
|
221
197
|
model: this.model,
|
|
222
|
-
manager: this
|
|
198
|
+
manager: this,
|
|
199
|
+
transaction: this.getHookTransaction()
|
|
223
200
|
});
|
|
224
201
|
}
|
|
225
202
|
async bulkCreate(inputs) {
|
|
@@ -228,7 +205,8 @@ var ModelManager = class ModelManager {
|
|
|
228
205
|
const batchPrepared = await this.model.hooks?.beforeBulkCreate?.({
|
|
229
206
|
rows: perRowPrepared,
|
|
230
207
|
model: this.model,
|
|
231
|
-
manager: this
|
|
208
|
+
manager: this,
|
|
209
|
+
transaction: this.getHookTransaction()
|
|
232
210
|
}) ?? perRowPrepared;
|
|
233
211
|
const preparedKeys = Object.keys(batchPrepared[0] ?? {});
|
|
234
212
|
if (preparedKeys.length === 0) throw new Error(`Cannot create ${this.model.metadata.name} without any values.`);
|
|
@@ -243,12 +221,14 @@ var ModelManager = class ModelManager {
|
|
|
243
221
|
await Promise.all(result.rows.map((record) => this.model.hooks?.afterCreate?.({
|
|
244
222
|
record,
|
|
245
223
|
model: this.model,
|
|
246
|
-
manager: this
|
|
224
|
+
manager: this,
|
|
225
|
+
transaction: this.getHookTransaction()
|
|
247
226
|
})));
|
|
248
227
|
await this.model.hooks?.afterBulkCreate?.({
|
|
249
228
|
records: result.rows,
|
|
250
229
|
model: this.model,
|
|
251
|
-
manager: this
|
|
230
|
+
manager: this,
|
|
231
|
+
transaction: this.getHookTransaction()
|
|
252
232
|
});
|
|
253
233
|
return result.rows;
|
|
254
234
|
}
|
|
@@ -256,7 +236,8 @@ var ModelManager = class ModelManager {
|
|
|
256
236
|
return await this.model.hooks?.beforeCreate?.({
|
|
257
237
|
data,
|
|
258
238
|
model: this.model,
|
|
259
|
-
manager: this
|
|
239
|
+
manager: this,
|
|
240
|
+
transaction: this.getHookTransaction()
|
|
260
241
|
}) ?? data;
|
|
261
242
|
}
|
|
262
243
|
async runBeforeUpdate(id, patch, current) {
|
|
@@ -265,88 +246,15 @@ var ModelManager = class ModelManager {
|
|
|
265
246
|
patch,
|
|
266
247
|
current,
|
|
267
248
|
model: this.model,
|
|
268
|
-
manager: this
|
|
249
|
+
manager: this,
|
|
250
|
+
transaction: this.getHookTransaction()
|
|
269
251
|
}) ?? patch;
|
|
270
252
|
}
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
//#endregion
|
|
274
|
-
//#region src/runtime/TangoRuntime.ts
|
|
275
|
-
var TangoRuntime = class TangoRuntime {
|
|
276
|
-
static BRAND = "tango.orm.runtime";
|
|
277
|
-
__tangoBrand = TangoRuntime.BRAND;
|
|
278
|
-
loadedConfig;
|
|
279
|
-
clientPromise = null;
|
|
280
|
-
constructor(loadLoadedConfig) {
|
|
281
|
-
this.loadedConfig = loadLoadedConfig();
|
|
282
|
-
}
|
|
283
|
-
/**
|
|
284
|
-
* Narrow an unknown value to `TangoRuntime`.
|
|
285
|
-
*/
|
|
286
|
-
static isTangoRuntime(value) {
|
|
287
|
-
return typeof value === "object" && value !== null && value.__tangoBrand === TangoRuntime.BRAND;
|
|
288
|
-
}
|
|
289
|
-
/**
|
|
290
|
-
* Return the loaded Tango config snapshot for the active environment.
|
|
291
|
-
*/
|
|
292
|
-
getConfig() {
|
|
293
|
-
return this.loadedConfig;
|
|
294
|
-
}
|
|
295
|
-
/**
|
|
296
|
-
* Return the configured SQL dialect for the current runtime.
|
|
297
|
-
*/
|
|
298
|
-
getDialect() {
|
|
299
|
-
return this.loadedConfig.current.db.adapter;
|
|
300
|
-
}
|
|
301
|
-
/**
|
|
302
|
-
* Return the shared DB client, creating it once on first access.
|
|
303
|
-
*/
|
|
304
|
-
async getClient() {
|
|
305
|
-
if (!this.clientPromise) {
|
|
306
|
-
const db = this.loadedConfig.current.db;
|
|
307
|
-
this.clientPromise = connectDB({
|
|
308
|
-
adapter: db.adapter,
|
|
309
|
-
url: db.url,
|
|
310
|
-
host: db.host,
|
|
311
|
-
port: db.port,
|
|
312
|
-
database: db.database,
|
|
313
|
-
user: db.user,
|
|
314
|
-
password: db.password,
|
|
315
|
-
filename: db.filename,
|
|
316
|
-
maxConnections: db.maxConnections
|
|
317
|
-
});
|
|
318
|
-
}
|
|
319
|
-
return this.clientPromise;
|
|
320
|
-
}
|
|
321
|
-
/**
|
|
322
|
-
* Close and clear the cached DB client so tests can start fresh.
|
|
323
|
-
*/
|
|
324
|
-
async reset() {
|
|
325
|
-
if (!this.clientPromise) return;
|
|
326
|
-
const client = await this.clientPromise;
|
|
327
|
-
this.clientPromise = null;
|
|
328
|
-
await client.close();
|
|
253
|
+
getHookTransaction() {
|
|
254
|
+
return TransactionEngine.forRuntime(this.runtime).getActiveTransaction();
|
|
329
255
|
}
|
|
330
256
|
};
|
|
331
257
|
|
|
332
|
-
//#endregion
|
|
333
|
-
//#region src/runtime/defaultRuntime.ts
|
|
334
|
-
let defaultRuntime = null;
|
|
335
|
-
function initializeTangoRuntime(fromFile) {
|
|
336
|
-
defaultRuntime = new TangoRuntime(() => loadConfig(fromFile));
|
|
337
|
-
return defaultRuntime;
|
|
338
|
-
}
|
|
339
|
-
function getTangoRuntime() {
|
|
340
|
-
if (!defaultRuntime) defaultRuntime = new TangoRuntime(() => loadConfigFromProjectRoot());
|
|
341
|
-
return defaultRuntime;
|
|
342
|
-
}
|
|
343
|
-
async function resetTangoRuntime() {
|
|
344
|
-
if (!defaultRuntime) return;
|
|
345
|
-
const runtime = defaultRuntime;
|
|
346
|
-
defaultRuntime = null;
|
|
347
|
-
await runtime.reset();
|
|
348
|
-
}
|
|
349
|
-
|
|
350
258
|
//#endregion
|
|
351
259
|
//#region src/manager/registerModelObjects.ts
|
|
352
260
|
const managerCache = new WeakMap();
|
|
@@ -375,5 +283,5 @@ function registerModelObjects() {
|
|
|
375
283
|
}
|
|
376
284
|
|
|
377
285
|
//#endregion
|
|
378
|
-
export { ModelManager,
|
|
379
|
-
//# sourceMappingURL=registerModelObjects-
|
|
286
|
+
export { ModelManager, registerModelObjects };
|
|
287
|
+
//# sourceMappingURL=registerModelObjects-Bva_f-Qh.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registerModelObjects-Bva_f-Qh.js","names":["dialect: Dialect","plan: ValidatedInsertSqlPlan","values: readonly unknown[]","plan: ValidatedUpdateSqlPlan","id: unknown","plan: ValidatedDeleteSqlPlan","valueRows: ReadonlyArray<ReadonlyArray<unknown>>","count: number","index: number","model: ModelLike<TModelRow>","runtime: TangoRuntime","value: unknown","rawMeta: TableMeta","id: TModelRow[keyof TModelRow]","input: Partial<TModelRow>","patch: Partial<TModelRow>","inputs: Partial<TModelRow>[]","batchPrepared: Partial<TModelRow>[]","data: Partial<TModelRow>","current: TModelRow","model: SchemaModel<TSchema, TKey>"],"sources":["../src/manager/internal/MutationCompiler.ts","../src/manager/ModelManager.ts","../src/manager/registerModelObjects.ts"],"sourcesContent":["import type { CompiledQuery, Dialect } from '../../query/domain/index';\nimport type {\n ValidatedDeleteSqlPlan,\n ValidatedInsertSqlPlan,\n ValidatedUpdateSqlPlan,\n} from '../../validation/SQLValidationEngine';\n\n/**\n * Internal compiler for manager-owned INSERT/UPDATE/DELETE statements.\n */\nexport class MutationCompiler {\n constructor(private readonly dialect: Dialect) {}\n\n compileInsert(plan: ValidatedInsertSqlPlan, values: readonly unknown[]): CompiledQuery {\n return {\n sql: `INSERT INTO ${plan.meta.table} (${plan.writeKeys.join(', ')}) VALUES (${this.buildValuePlaceholders(plan.writeKeys.length)}) RETURNING *`,\n params: values,\n };\n }\n\n compileUpdate(plan: ValidatedUpdateSqlPlan, values: readonly unknown[], id: unknown): CompiledQuery {\n const sets = plan.writeKeys.map((key, index) => `${key} = ${this.placeholder(index + 1)}`).join(', ');\n const whereParam = this.placeholder(plan.writeKeys.length + 1);\n\n return {\n sql: `UPDATE ${plan.meta.table} SET ${sets} WHERE ${plan.meta.pk} = ${whereParam} RETURNING *`,\n params: [...values, id],\n };\n }\n\n compileDelete(plan: ValidatedDeleteSqlPlan, id: unknown): CompiledQuery {\n return {\n sql: `DELETE FROM ${plan.meta.table} WHERE ${plan.meta.pk} = ${this.placeholder(1)}`,\n params: [id],\n };\n }\n\n compileBulkInsert(plan: ValidatedInsertSqlPlan, valueRows: ReadonlyArray<ReadonlyArray<unknown>>): CompiledQuery {\n const columnCount = plan.writeKeys.length;\n const placeholders = valueRows\n .map((_row, rowIndex) => {\n const offset = rowIndex * columnCount;\n return `(${plan.writeKeys.map((_, colIndex) => this.placeholder(offset + colIndex + 1)).join(', ')})`;\n })\n .join(', ');\n\n return {\n sql: `INSERT INTO ${plan.meta.table} (${plan.writeKeys.join(', ')}) VALUES ${placeholders} RETURNING *`,\n params: valueRows.flat(),\n };\n }\n\n private buildValuePlaceholders(count: number): string {\n return Array.from({ length: count }, (_value, index) => this.placeholder(index + 1)).join(', ');\n }\n\n private placeholder(index: number): string {\n return this.dialect === 'postgres' ? `$${index}` : '?';\n }\n}\n","import { NotFoundError } from '@danceroutine/tango-core';\nimport { ModelRegistry, type ModelWriteHooks } from '@danceroutine/tango-schema';\nimport type { FilterInput, RelationMeta, TableMeta } from '../query/domain/index';\nimport { InternalRelationKind } from '../query/domain/internal/InternalRelationKind';\nimport type { QuerySet } from '../query/index';\nimport { QuerySet as QuerySetClass } from '../query/index';\nimport type { Dialect, QueryExecutor } from '../query/index';\nimport type { TangoRuntime } from '../runtime/TangoRuntime';\nimport { OrmSqlSafetyAdapter } from '../validation';\nimport { TransactionEngine } from '../transaction/internal/context';\nimport type { ManagerLike } from './ManagerLike';\nimport { MutationCompiler } from './internal/MutationCompiler';\nimport { RuntimeBoundClient } from './internal/RuntimeBoundClient';\n\nconst sqlSafetyAdapter = new OrmSqlSafetyAdapter();\n\ntype FieldLike = {\n name: string;\n type: string;\n primaryKey?: boolean;\n};\n\ntype ModelLike<TModelRow extends Record<string, unknown>> = {\n metadata: {\n key?: string;\n name: string;\n table: string;\n fields: FieldLike[];\n };\n schema: {\n parse(input: unknown): TModelRow;\n };\n hooks?: ModelWriteHooks<TModelRow>;\n};\n\n/**\n * Model-backed data access API exposed as `Model.objects`.\n */\nexport class ModelManager<TModelRow extends Record<string, unknown>, TSourceModel = unknown>\n implements ManagerLike<TModelRow, TSourceModel>\n{\n static readonly BRAND = 'tango.orm.model_manager' as const;\n readonly __tangoBrand: typeof ModelManager.BRAND = ModelManager.BRAND;\n private readonly queryExecutor: QueryExecutor<TModelRow>;\n private readonly mutationCompiler: MutationCompiler;\n private readonly model: ModelLike<TModelRow>;\n private readonly client: RuntimeBoundClient;\n private readonly dialect: Dialect;\n private readonly runtime: TangoRuntime;\n\n constructor(model: ModelLike<TModelRow>, runtime: TangoRuntime) {\n this.model = model;\n this.runtime = runtime;\n this.client = new RuntimeBoundClient(runtime);\n this.dialect = runtime.getDialect() as Dialect;\n this.mutationCompiler = new MutationCompiler(this.dialect);\n this.queryExecutor = {\n get meta() {\n return ModelManager.createTableMeta(model);\n },\n client: this.client,\n dialect: this.dialect,\n run: async (compiled) => {\n const result = await this.client.query<TModelRow>(compiled.sql, compiled.params);\n return result.rows;\n },\n };\n }\n\n get meta(): TableMeta {\n return ModelManager.createTableMeta(this.model);\n }\n\n /**\n * Narrow an unknown value to `ModelManager`.\n */\n static isModelManager<TModelRow extends Record<string, unknown>>(value: unknown): value is ModelManager<TModelRow> {\n return (\n typeof value === 'object' &&\n value !== null &&\n (value as { __tangoBrand?: unknown }).__tangoBrand === ModelManager.BRAND\n );\n }\n\n private static createTableMeta<TModelRow extends Record<string, unknown>>(model: ModelLike<TModelRow>): TableMeta {\n const pkField = model.metadata.fields.find((field) => field.primaryKey);\n if (!pkField) {\n throw new Error(`Model '${model.metadata.name}' cannot attach a manager without a primary key field.`);\n }\n\n const rawMeta: TableMeta = {\n table: model.metadata.table,\n pk: pkField.name,\n columns: Object.fromEntries(model.metadata.fields.map((field) => [field.name, field.type])),\n };\n\n if (model.metadata.key) {\n const owner = ModelRegistry.getOwner(model as never);\n const relations = owner.getResolvedRelationGraph().byModel.get(model.metadata.key);\n if (relations && relations.size > 0) {\n // This metadata is recomputed on access today. Most application tables have a small relation set, so\n // keeping this path direct is acceptable until profiling points to a dedicated manager-meta cache.\n rawMeta.relations = Object.fromEntries(\n Array.from(relations.entries())\n .filter(([, relation]) => relation.kind !== InternalRelationKind.MANY_TO_MANY)\n .map(([name, relation]) => {\n const targetModel = owner.getByKey(relation.targetModelKey)!;\n // Query planning needs the target model's SQL-facing shape so joined and prefetched rows can be\n // selected, normalized, and attached without leaking internal alias columns.\n const targetColumns = Object.fromEntries(\n targetModel.metadata.fields.map((field) => [field.name, field.type])\n );\n // Forward relations join from this table's FK to the target key. Reverse relations flip that:\n // this table's primary key matches the target model's FK back to this model.\n const sourceKey =\n relation.kind === InternalRelationKind.BELONGS_TO\n ? relation.localFieldName\n : relation.targetFieldName;\n const targetKey =\n relation.kind === InternalRelationKind.BELONGS_TO\n ? relation.targetFieldName\n : relation.localFieldName;\n\n return [\n name,\n {\n kind: relation.kind as RelationMeta['kind'],\n table: targetModel.metadata.table,\n sourceKey: sourceKey as string,\n targetKey: targetKey as string,\n targetColumns,\n alias: relation.alias,\n },\n ];\n })\n );\n }\n }\n\n const validatedMeta = sqlSafetyAdapter.validate({\n kind: 'insert',\n meta: rawMeta,\n writeKeys: Object.keys(rawMeta.columns),\n }).meta;\n\n if (rawMeta.relations) {\n validatedMeta.relations = rawMeta.relations;\n }\n\n return validatedMeta;\n }\n\n query(): QuerySet<TModelRow, TModelRow, TSourceModel> {\n return new QuerySetClass<TModelRow, TModelRow, TSourceModel>(this.queryExecutor, {});\n }\n\n async findById(id: TModelRow[keyof TModelRow]): Promise<TModelRow | null> {\n const filter = { [this.meta.pk]: id } as unknown as FilterInput<TModelRow>;\n return this.query().filter(filter).fetchOne();\n }\n\n async getOrThrow(id: TModelRow[keyof TModelRow]): Promise<TModelRow> {\n const result = await this.findById(id);\n if (!result) {\n throw new NotFoundError(`${this.model.metadata.name} with ${this.meta.pk}=${String(id)} not found`);\n }\n return result;\n }\n\n async create(input: Partial<TModelRow>): Promise<TModelRow> {\n const prepared = await this.runBeforeCreate(input);\n const preparedKeys = Object.keys(prepared);\n if (preparedKeys.length === 0) {\n throw new Error(`Cannot create ${this.model.metadata.name} without any values.`);\n }\n\n const validatedPlan = sqlSafetyAdapter.validate({\n kind: 'insert',\n meta: this.meta,\n writeKeys: preparedKeys,\n });\n const compiled = this.mutationCompiler.compileInsert(\n validatedPlan,\n preparedKeys.map((key) => prepared[key as keyof TModelRow])\n );\n const result = await this.queryExecutor.client.query<TModelRow>(compiled.sql, compiled.params);\n const created = result.rows[0]!;\n await this.model.hooks?.afterCreate?.({\n record: created,\n model: this.model,\n manager: this,\n transaction: this.getHookTransaction(),\n });\n return created;\n }\n\n async update(id: TModelRow[keyof TModelRow], patch: Partial<TModelRow>): Promise<TModelRow> {\n const current = await this.getOrThrow(id);\n const prepared = await this.runBeforeUpdate(id, patch, current);\n const preparedKeys = Object.keys(prepared);\n if (preparedKeys.length === 0) {\n throw new Error(`Cannot update ${this.model.metadata.name} without any values.`);\n }\n\n const validatedPlan = sqlSafetyAdapter.validate({\n kind: 'update',\n meta: this.meta,\n writeKeys: preparedKeys,\n });\n const compiled = this.mutationCompiler.compileUpdate(\n validatedPlan,\n preparedKeys.map((key) => prepared[key as keyof TModelRow]),\n id\n );\n const result = await this.queryExecutor.client.query<TModelRow>(compiled.sql, compiled.params);\n const updated = result.rows[0]!;\n await this.model.hooks?.afterUpdate?.({\n id,\n patch: prepared,\n previous: current,\n record: updated,\n model: this.model,\n manager: this,\n transaction: this.getHookTransaction(),\n });\n return updated;\n }\n\n async delete(id: TModelRow[keyof TModelRow]): Promise<void> {\n const current = await this.getOrThrow(id);\n await this.model.hooks?.beforeDelete?.({\n id,\n current,\n model: this.model,\n manager: this,\n transaction: this.getHookTransaction(),\n });\n const validatedPlan = sqlSafetyAdapter.validate({\n kind: 'delete',\n meta: this.meta,\n });\n const compiled = this.mutationCompiler.compileDelete(validatedPlan, id);\n await this.queryExecutor.client.query(compiled.sql, compiled.params);\n await this.model.hooks?.afterDelete?.({\n id,\n previous: current,\n model: this.model,\n manager: this,\n transaction: this.getHookTransaction(),\n });\n }\n\n async bulkCreate(inputs: Partial<TModelRow>[]): Promise<TModelRow[]> {\n if (inputs.length === 0) {\n return [];\n }\n\n const perRowPrepared = await Promise.all(inputs.map((input) => this.runBeforeCreate(input)));\n const batchPrepared: Partial<TModelRow>[] =\n (await this.model.hooks?.beforeBulkCreate?.({\n rows: perRowPrepared,\n model: this.model,\n manager: this,\n transaction: this.getHookTransaction(),\n })) ?? perRowPrepared;\n const preparedKeys = Object.keys(batchPrepared[0] ?? {});\n if (preparedKeys.length === 0) {\n throw new Error(`Cannot create ${this.model.metadata.name} without any values.`);\n }\n\n const validatedPlan = sqlSafetyAdapter.validate({\n kind: 'insert',\n meta: this.meta,\n writeKeys: preparedKeys,\n });\n const valueRows = batchPrepared.map((input) => preparedKeys.map((key) => input[key as keyof TModelRow]));\n const compiled = this.mutationCompiler.compileBulkInsert(validatedPlan, valueRows);\n const result = await this.queryExecutor.client.query<TModelRow>(compiled.sql, compiled.params);\n await Promise.all(\n result.rows.map((record) =>\n this.model.hooks?.afterCreate?.({\n record,\n model: this.model,\n manager: this,\n transaction: this.getHookTransaction(),\n })\n )\n );\n await this.model.hooks?.afterBulkCreate?.({\n records: result.rows,\n model: this.model,\n manager: this,\n transaction: this.getHookTransaction(),\n });\n return result.rows;\n }\n\n private async runBeforeCreate(data: Partial<TModelRow>): Promise<Partial<TModelRow>> {\n return (\n (await this.model.hooks?.beforeCreate?.({\n data,\n model: this.model,\n manager: this,\n transaction: this.getHookTransaction(),\n })) ?? data\n );\n }\n\n private async runBeforeUpdate(\n id: TModelRow[keyof TModelRow],\n patch: Partial<TModelRow>,\n current: TModelRow\n ): Promise<Partial<TModelRow>> {\n return (\n (await this.model.hooks?.beforeUpdate?.({\n id,\n patch,\n current,\n model: this.model,\n manager: this,\n transaction: this.getHookTransaction(),\n })) ?? patch\n );\n }\n\n private getHookTransaction() {\n return TransactionEngine.forRuntime(this.runtime).getActiveTransaction();\n }\n}\n","import type { z } from 'zod';\nimport type { Model as SchemaModel, PersistedModelOutput } from '@danceroutine/tango-schema/domain';\nimport { registerModelAugmentor } from '@danceroutine/tango-schema';\nimport { ModelManager } from './ModelManager';\nimport type { TangoRuntime } from '../runtime/TangoRuntime';\nimport { getTangoRuntime } from '../runtime/defaultRuntime';\n\nconst managerCache = new WeakMap<object, { runtime: TangoRuntime; manager: ModelManager<Record<string, unknown>> }>();\nlet hasRegisteredModelObjects = false;\n\ntype AugmentableSchemaModel<TSchema extends z.ZodObject<z.ZodRawShape>> = {\n metadata: {\n key?: string;\n name: string;\n table: string;\n fields: Array<{ name: string; type: string; primaryKey?: boolean }>;\n };\n schema: {\n parse(input: unknown): PersistedModelOutput<TSchema>;\n };\n hooks?: SchemaModel<TSchema>['hooks'];\n};\n\nfunction defineObjectsProperty<TSchema extends z.ZodObject<z.ZodRawShape>, TKey extends string>(\n model: SchemaModel<TSchema, TKey>\n): void {\n Object.defineProperty(model, 'objects', {\n configurable: true,\n enumerable: true,\n get() {\n const runtime = getTangoRuntime();\n const cached = managerCache.get(model);\n if (cached && cached.runtime === runtime) {\n return cached.manager;\n }\n\n const manager = new ModelManager<PersistedModelOutput<TSchema>, SchemaModel<TSchema, TKey>>(\n model as unknown as AugmentableSchemaModel<TSchema>,\n runtime\n );\n managerCache.set(model, {\n runtime,\n manager: manager as ModelManager<Record<string, unknown>>,\n });\n return manager;\n },\n });\n}\n\n/**\n * Install the schema model augmentor that exposes `Model.objects`.\n * This registration is idempotent so multiple Tango entrypoints can safely call it.\n */\nexport function registerModelObjects(): void {\n if (hasRegisteredModelObjects) {\n return;\n }\n\n registerModelAugmentor(defineObjectsProperty);\n hasRegisteredModelObjects = true;\n}\n"],"mappings":"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"}
|
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
import type { LoadedConfig } from '@danceroutine/tango-config';
|
|
2
2
|
import type { DBClient } from '../connection/index';
|
|
3
3
|
import type { Dialect } from '../query/domain/index';
|
|
4
|
+
import type { TransactionClientLease } from './internal/DBClientProvider';
|
|
4
5
|
/**
|
|
5
6
|
* Framework-owned database runtime that resolves Tango config and lazily
|
|
6
|
-
* creates the shared
|
|
7
|
+
* creates the shared connection provider used by manager-backed models.
|
|
7
8
|
*/
|
|
8
9
|
export declare class TangoRuntime {
|
|
9
10
|
static readonly BRAND: "tango.orm.runtime";
|
|
10
11
|
readonly __tangoBrand: typeof TangoRuntime.BRAND;
|
|
11
12
|
private readonly loadedConfig;
|
|
12
|
-
private
|
|
13
|
+
private providerPromise;
|
|
14
|
+
private runtimeClientPromise;
|
|
13
15
|
constructor(loadLoadedConfig: () => LoadedConfig);
|
|
14
16
|
/**
|
|
15
17
|
* Narrow an unknown value to `TangoRuntime`.
|
|
@@ -24,11 +26,22 @@ export declare class TangoRuntime {
|
|
|
24
26
|
*/
|
|
25
27
|
getDialect(): Dialect;
|
|
26
28
|
/**
|
|
27
|
-
* Return the
|
|
29
|
+
* Return the runtime-bound DB client facade used by manager-backed code.
|
|
28
30
|
*/
|
|
29
31
|
getClient(): Promise<DBClient>;
|
|
30
32
|
/**
|
|
31
|
-
*
|
|
33
|
+
* Execute SQL through the autocommit path owned by this runtime.
|
|
34
|
+
*/
|
|
35
|
+
query<T = unknown>(sql: string, params?: readonly unknown[]): Promise<{
|
|
36
|
+
rows: T[];
|
|
37
|
+
}>;
|
|
38
|
+
/**
|
|
39
|
+
* Lease a transaction-scoped client for `transaction.atomic(...)`.
|
|
40
|
+
*/
|
|
41
|
+
leaseTransactionClient(): Promise<TransactionClientLease>;
|
|
42
|
+
/**
|
|
43
|
+
* Close and clear the cached runtime resources so tests can start fresh.
|
|
32
44
|
*/
|
|
33
45
|
reset(): Promise<void>;
|
|
46
|
+
private getProvider;
|
|
34
47
|
}
|
package/dist/runtime/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import "../
|
|
2
|
-
import "../
|
|
3
|
-
import "../
|
|
4
|
-
import "../
|
|
5
|
-
import {
|
|
6
|
-
import "../runtime-
|
|
1
|
+
import "../PostgresClient-BQJZfEOT.js";
|
|
2
|
+
import "../SqliteClient-CjOK9-ki.js";
|
|
3
|
+
import "../query-CWZ1cfjo.js";
|
|
4
|
+
import { TangoRuntime, getTangoRuntime, initializeTangoRuntime, resetTangoRuntime } from "../defaultRuntime-BPK9kWEW.js";
|
|
5
|
+
import { registerModelObjects } from "../registerModelObjects-Bva_f-Qh.js";
|
|
6
|
+
import "../runtime-ByXbpVBS.js";
|
|
7
7
|
|
|
8
8
|
export { TangoRuntime, getTangoRuntime, initializeTangoRuntime, registerModelObjects, resetTangoRuntime };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { DBClient } from '../../connection/clients/DBClient';
|
|
2
|
+
export interface TransactionClientLease {
|
|
3
|
+
readonly client: DBClient;
|
|
4
|
+
release(): Promise<void>;
|
|
5
|
+
}
|
|
6
|
+
export interface DBClientProvider {
|
|
7
|
+
query<T = unknown>(sql: string, params?: readonly unknown[]): Promise<{
|
|
8
|
+
rows: T[];
|
|
9
|
+
}>;
|
|
10
|
+
leaseTransactionClient(): Promise<TransactionClientLease>;
|
|
11
|
+
reset(): Promise<void>;
|
|
12
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { AdapterConfig } from '../../connection/adapters/Adapter';
|
|
2
|
+
import type { DBClientProvider, TransactionClientLease } from './DBClientProvider';
|
|
3
|
+
export declare class PostgresDBClientProvider implements DBClientProvider {
|
|
4
|
+
private readonly pool;
|
|
5
|
+
private activeLeaseCount;
|
|
6
|
+
constructor(config: AdapterConfig);
|
|
7
|
+
query<T = unknown>(sql: string, params?: readonly unknown[]): Promise<{
|
|
8
|
+
rows: T[];
|
|
9
|
+
}>;
|
|
10
|
+
leaseTransactionClient(): Promise<TransactionClientLease>;
|
|
11
|
+
reset(): Promise<void>;
|
|
12
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { AdapterConfig } from '../../connection/adapters/Adapter';
|
|
2
|
+
import type { DBClientProvider, TransactionClientLease } from './DBClientProvider';
|
|
3
|
+
export declare class SqliteDBClientProvider implements DBClientProvider {
|
|
4
|
+
private readonly filename;
|
|
5
|
+
private readonly Database;
|
|
6
|
+
private readonly autocommitClient;
|
|
7
|
+
private activeLeaseCount;
|
|
8
|
+
constructor(config?: AdapterConfig);
|
|
9
|
+
query<T = unknown>(sql: string, params?: readonly unknown[]): Promise<{
|
|
10
|
+
rows: T[];
|
|
11
|
+
}>;
|
|
12
|
+
leaseTransactionClient(): Promise<TransactionClientLease>;
|
|
13
|
+
reset(): Promise<void>;
|
|
14
|
+
private openClient;
|
|
15
|
+
private getDatabaseCtor;
|
|
16
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { __export } from "./chunk-DLY2FNSh.js";
|
|
2
|
-
import { TangoRuntime, getTangoRuntime, initializeTangoRuntime,
|
|
2
|
+
import { TangoRuntime, getTangoRuntime, initializeTangoRuntime, resetTangoRuntime } from "./defaultRuntime-BPK9kWEW.js";
|
|
3
|
+
import { registerModelObjects } from "./registerModelObjects-Bva_f-Qh.js";
|
|
3
4
|
|
|
4
5
|
//#region src/runtime/index.ts
|
|
5
6
|
var runtime_exports = {};
|
|
@@ -14,4 +15,4 @@ registerModelObjects();
|
|
|
14
15
|
|
|
15
16
|
//#endregion
|
|
16
17
|
export { runtime_exports };
|
|
17
|
-
//# sourceMappingURL=runtime-
|
|
18
|
+
//# sourceMappingURL=runtime-ByXbpVBS.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runtime-
|
|
1
|
+
{"version":3,"file":"runtime-ByXbpVBS.js","names":[],"sources":["../src/runtime/index.ts"],"sourcesContent":["import type { z } from 'zod';\nimport type { ModelManager } from '../manager/ModelManager';\nimport type { Model, PersistedModelOutput } from '@danceroutine/tango-schema/domain';\nimport { registerModelObjects } from '../manager/registerModelObjects';\n\n/**\n * Domain boundary barrel: centralizes Tango runtime ownership APIs.\n */\n\ndeclare global {\n interface TangoSchemaModelAugmentations<\n TSchema extends z.ZodObject<z.ZodRawShape> = z.ZodObject<z.ZodRawShape>,\n TKey extends string = string,\n > {\n readonly objects: ModelManager<PersistedModelOutput<TSchema>, Model<TSchema, TKey>>;\n }\n}\n\nregisterModelObjects();\n\nexport { registerModelObjects } from '../manager/registerModelObjects';\nexport { TangoRuntime } from './TangoRuntime';\nexport { getTangoRuntime, initializeTangoRuntime, resetTangoRuntime } from './defaultRuntime';\n"],"mappings":";;;;;;;;;;;;;AAkBA,sBAAsB"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export interface OnCommitOptions {
|
|
2
|
+
robust?: boolean;
|
|
3
|
+
}
|
|
4
|
+
export interface SavepointOptions {
|
|
5
|
+
throwOnError?: boolean;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Result returned by `tx.savepoint(...)`.
|
|
9
|
+
*/
|
|
10
|
+
export type SavepointResult<T> = {
|
|
11
|
+
ok: true;
|
|
12
|
+
value: T;
|
|
13
|
+
} | {
|
|
14
|
+
ok: false;
|
|
15
|
+
error: unknown;
|
|
16
|
+
};
|
|
17
|
+
export interface AtomicTransaction {
|
|
18
|
+
/**
|
|
19
|
+
* Register a callback that should run only after the outermost transaction commit succeeds.
|
|
20
|
+
*/
|
|
21
|
+
onCommit(callback: () => void, options?: OnCommitOptions): void;
|
|
22
|
+
/**
|
|
23
|
+
* Run work inside a nested savepoint and, by default, capture rollback as a result object instead of throwing.
|
|
24
|
+
*/
|
|
25
|
+
savepoint<T>(work: (tx: AtomicTransaction) => Promise<T> | T): Promise<SavepointResult<T>>;
|
|
26
|
+
savepoint<T>(work: (tx: AtomicTransaction) => Promise<T> | T, options: {
|
|
27
|
+
throwOnError: false;
|
|
28
|
+
}): Promise<SavepointResult<T>>;
|
|
29
|
+
savepoint<T>(work: (tx: AtomicTransaction) => Promise<T> | T, options: {
|
|
30
|
+
throwOnError: true;
|
|
31
|
+
}): Promise<T>;
|
|
32
|
+
}
|
|
@@ -15,6 +15,9 @@ import type { DBClient } from '../connection/clients/DBClient';
|
|
|
15
15
|
* throw error;
|
|
16
16
|
* }
|
|
17
17
|
* ```
|
|
18
|
+
*
|
|
19
|
+
* @deprecated Use `transaction.atomic(async (tx) => { ... })` for application
|
|
20
|
+
* transaction workflows. `UnitOfWork` remains exported only for compatibility.
|
|
18
21
|
*/
|
|
19
22
|
export declare class UnitOfWork {
|
|
20
23
|
static readonly BRAND: "tango.orm.unit_of_work";
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Domain boundary barrel: centralizes this subdomain's public contract.
|
|
3
3
|
*/
|
|
4
|
+
export { atomic } from './atomic';
|
|
5
|
+
export type { AtomicTransaction, OnCommitOptions, SavepointOptions, SavepointResult } from './AtomicTransaction';
|
|
4
6
|
export { UnitOfWork } from './UnitOfWork';
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
-
import
|
|
1
|
+
import "../PostgresClient-BQJZfEOT.js";
|
|
2
|
+
import "../SqliteClient-CjOK9-ki.js";
|
|
3
|
+
import "../defaultRuntime-BPK9kWEW.js";
|
|
4
|
+
import { UnitOfWork, atomic } from "../transaction-Cs0Z9tbW.js";
|
|
2
5
|
|
|
3
|
-
export { UnitOfWork };
|
|
6
|
+
export { UnitOfWork, atomic };
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { TangoRuntime } from '../../../runtime/TangoRuntime';
|
|
2
|
+
import type { TransactionClientLease } from '../../../runtime/internal/DBClientProvider';
|
|
3
|
+
import type { AtomicTransaction, SavepointOptions, SavepointResult } from '../../AtomicTransaction';
|
|
4
|
+
import type { TransactionState } from './TransactionState';
|
|
5
|
+
export declare class AsyncLocalTransactionEngine {
|
|
6
|
+
private readonly logger;
|
|
7
|
+
private readonly storage;
|
|
8
|
+
assertNoActiveAtomicTransaction(): void;
|
|
9
|
+
getActiveTransaction(runtime: TangoRuntime): AtomicTransaction | undefined;
|
|
10
|
+
getActiveLease(runtime: TangoRuntime): TransactionClientLease | undefined;
|
|
11
|
+
atomic<T>(runtime: TangoRuntime, work: (tx: AtomicTransaction) => Promise<T> | T): Promise<T>;
|
|
12
|
+
runSavepoint<T>(state: TransactionState, work: (tx: AtomicTransaction) => Promise<T> | T, options: SavepointOptions): Promise<T | SavepointResult<T>>;
|
|
13
|
+
private runNested;
|
|
14
|
+
private pushFrame;
|
|
15
|
+
private popFrame;
|
|
16
|
+
private rollbackOuter;
|
|
17
|
+
private runCommittedCallbacks;
|
|
18
|
+
private deactivateAllFrames;
|
|
19
|
+
private attachCause;
|
|
20
|
+
private isErrorValue;
|
|
21
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { AtomicTransaction, OnCommitOptions, SavepointResult } from '../../AtomicTransaction';
|
|
2
|
+
import type { AsyncLocalTransactionEngine } from './AsyncLocalTransactionEngine';
|
|
3
|
+
import type { TransactionFrame } from './TransactionFrame';
|
|
4
|
+
import type { TransactionState } from './TransactionState';
|
|
5
|
+
export declare class FrameBoundTransaction implements AtomicTransaction {
|
|
6
|
+
private readonly engine;
|
|
7
|
+
private readonly state;
|
|
8
|
+
private readonly frame;
|
|
9
|
+
private active;
|
|
10
|
+
constructor(engine: AsyncLocalTransactionEngine, state: TransactionState, frame: TransactionFrame);
|
|
11
|
+
onCommit(callback: () => void, options?: OnCommitOptions): void;
|
|
12
|
+
savepoint<T>(work: (tx: AtomicTransaction) => Promise<T> | T): Promise<SavepointResult<T>>;
|
|
13
|
+
savepoint<T>(work: (tx: AtomicTransaction) => Promise<T> | T, options: {
|
|
14
|
+
throwOnError: false;
|
|
15
|
+
}): Promise<SavepointResult<T>>;
|
|
16
|
+
savepoint<T>(work: (tx: AtomicTransaction) => Promise<T> | T, options: {
|
|
17
|
+
throwOnError: true;
|
|
18
|
+
}): Promise<T>;
|
|
19
|
+
deactivate(): void;
|
|
20
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { TangoRuntime } from '../../../runtime/TangoRuntime';
|
|
2
|
+
import type { TransactionClientLease } from '../../../runtime/internal/DBClientProvider';
|
|
3
|
+
import type { AtomicTransaction } from '../../AtomicTransaction';
|
|
4
|
+
/**
|
|
5
|
+
* Runtime-bound transaction facade used by internal ORM/runtime components.
|
|
6
|
+
*/
|
|
7
|
+
export declare class TransactionEngine {
|
|
8
|
+
private readonly runtime;
|
|
9
|
+
private static readonly engine;
|
|
10
|
+
private constructor();
|
|
11
|
+
static forRuntime(runtime: TangoRuntime): TransactionEngine;
|
|
12
|
+
static assertNoActiveAtomicTransaction(): void;
|
|
13
|
+
getActiveTransaction(): AtomicTransaction | undefined;
|
|
14
|
+
getActiveLease(): TransactionClientLease | undefined;
|
|
15
|
+
atomic<T>(work: (tx: AtomicTransaction) => Promise<T> | T): Promise<T>;
|
|
16
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { TangoRuntime } from '../../../runtime/TangoRuntime';
|
|
2
|
+
import type { TransactionClientLease } from '../../../runtime/internal/DBClientProvider';
|
|
3
|
+
import type { TransactionFrame } from './TransactionFrame';
|
|
4
|
+
export type TransactionState = {
|
|
5
|
+
runtime: TangoRuntime;
|
|
6
|
+
lease: TransactionClientLease;
|
|
7
|
+
frames: TransactionFrame[];
|
|
8
|
+
nextCallbackOrder: number;
|
|
9
|
+
nextSavepointId: number;
|
|
10
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { TransactionEngine } from './TransactionEngine';
|
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
import { __export } from "./chunk-DLY2FNSh.js";
|
|
2
|
+
import { TransactionEngine, getTangoRuntime } from "./defaultRuntime-BPK9kWEW.js";
|
|
2
3
|
|
|
4
|
+
//#region src/transaction/atomic.ts
|
|
5
|
+
async function atomic(work) {
|
|
6
|
+
return TransactionEngine.forRuntime(getTangoRuntime()).atomic(work);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
//#endregion
|
|
3
10
|
//#region src/transaction/UnitOfWork.ts
|
|
4
11
|
var UnitOfWork = class UnitOfWork {
|
|
5
12
|
static BRAND = "tango.orm.unit_of_work";
|
|
@@ -19,6 +26,7 @@ var UnitOfWork = class UnitOfWork {
|
|
|
19
26
|
* Convenience factory that constructs and begins a unit of work.
|
|
20
27
|
*/
|
|
21
28
|
static async start(client) {
|
|
29
|
+
TransactionEngine.assertNoActiveAtomicTransaction();
|
|
22
30
|
const uow = new UnitOfWork(client);
|
|
23
31
|
await uow.begin();
|
|
24
32
|
return uow;
|
|
@@ -27,6 +35,7 @@ var UnitOfWork = class UnitOfWork {
|
|
|
27
35
|
* Begin a transaction if one is not already active.
|
|
28
36
|
*/
|
|
29
37
|
async begin() {
|
|
38
|
+
TransactionEngine.assertNoActiveAtomicTransaction();
|
|
30
39
|
if (!this.isActive) {
|
|
31
40
|
await this.client.begin();
|
|
32
41
|
this.isActive = true;
|
|
@@ -61,8 +70,11 @@ var UnitOfWork = class UnitOfWork {
|
|
|
61
70
|
//#endregion
|
|
62
71
|
//#region src/transaction/index.ts
|
|
63
72
|
var transaction_exports = {};
|
|
64
|
-
__export(transaction_exports, {
|
|
73
|
+
__export(transaction_exports, {
|
|
74
|
+
UnitOfWork: () => UnitOfWork,
|
|
75
|
+
atomic: () => atomic
|
|
76
|
+
});
|
|
65
77
|
|
|
66
78
|
//#endregion
|
|
67
|
-
export { UnitOfWork, transaction_exports };
|
|
68
|
-
//# sourceMappingURL=transaction-
|
|
79
|
+
export { UnitOfWork, atomic, transaction_exports };
|
|
80
|
+
//# sourceMappingURL=transaction-Cs0Z9tbW.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transaction-Cs0Z9tbW.js","names":["work: (tx: AtomicTransaction) => Promise<T> | T","client: DBClient","value: unknown"],"sources":["../src/transaction/atomic.ts","../src/transaction/UnitOfWork.ts","../src/transaction/index.ts"],"sourcesContent":["import { getTangoRuntime } from '../runtime/defaultRuntime';\nimport type { AtomicTransaction } from './AtomicTransaction';\nimport { TransactionEngine } from './internal/context';\n\nexport async function atomic<T>(work: (tx: AtomicTransaction) => Promise<T> | T): Promise<T> {\n return TransactionEngine.forRuntime(getTangoRuntime()).atomic(work);\n}\n","import type { DBClient } from '../connection/clients/DBClient';\nimport { TransactionEngine } from './internal/context';\n\n/**\n * Unit of Work pattern implementation for managing database transactions.\n * Ensures that a set of operations either all succeed or all fail together.\n *\n * @example\n * ```typescript\n * const uow = await UnitOfWork.start(dbClient);\n * try {\n * await userRepo.create({ email: 'test@example.com' });\n * await postRepo.create({ title: 'Hello' });\n * await uow.commit();\n * } catch (error) {\n * await uow.rollback();\n * throw error;\n * }\n * ```\n *\n * @deprecated Use `transaction.atomic(async (tx) => { ... })` for application\n * transaction workflows. `UnitOfWork` remains exported only for compatibility.\n */\nexport class UnitOfWork {\n static readonly BRAND = 'tango.orm.unit_of_work' as const;\n readonly __tangoBrand: typeof UnitOfWork.BRAND = UnitOfWork.BRAND;\n protected client: DBClient;\n protected isActive = false;\n\n constructor(client: DBClient) {\n this.client = client;\n }\n\n /**\n * Narrow an unknown value to `UnitOfWork`.\n */\n static isUnitOfWork(value: unknown): value is UnitOfWork {\n return (\n typeof value === 'object' &&\n value !== null &&\n (value as { __tangoBrand?: unknown }).__tangoBrand === UnitOfWork.BRAND\n );\n }\n\n /**\n * Convenience factory that constructs and begins a unit of work.\n */\n static async start(client: DBClient): Promise<UnitOfWork> {\n TransactionEngine.assertNoActiveAtomicTransaction();\n const uow = new UnitOfWork(client);\n await uow.begin();\n return uow;\n }\n\n /**\n * Begin a transaction if one is not already active.\n */\n async begin(): Promise<void> {\n TransactionEngine.assertNoActiveAtomicTransaction();\n if (!this.isActive) {\n await this.client.begin();\n this.isActive = true;\n }\n }\n\n /**\n * Commit the active transaction.\n */\n async commit(): Promise<void> {\n if (this.isActive) {\n await this.client.commit();\n this.isActive = false;\n }\n }\n\n /**\n * Return the underlying database client.\n */\n getClient(): DBClient {\n return this.client;\n }\n\n /**\n * Roll back the active transaction.\n */\n async rollback(): Promise<void> {\n if (this.isActive) {\n await this.client.rollback();\n this.isActive = false;\n }\n }\n}\n","/**\n * Domain boundary barrel: centralizes this subdomain's public contract.\n */\n\nexport { atomic } from './atomic';\nexport type { AtomicTransaction, OnCommitOptions, SavepointOptions, SavepointResult } from './AtomicTransaction';\nexport { UnitOfWork } from './UnitOfWork';\n"],"mappings":";;;;AAIO,eAAe,OAAUA,MAA6D;AACzF,QAAO,kBAAkB,WAAW,iBAAiB,CAAC,CAAC,OAAO,KAAK;AACtE;;;;ICiBY,aAAN,MAAM,WAAW;CACpB,OAAgB,QAAQ;CACxB,eAAiD,WAAW;CAC5D;CACA,WAAqB;CAErB,YAAYC,QAAkB;AAC1B,OAAK,SAAS;CACjB;;;;CAKD,OAAO,aAAaC,OAAqC;AACrD,gBACW,UAAU,YACjB,UAAU,QACT,MAAqC,iBAAiB,WAAW;CAEzE;;;;CAKD,aAAa,MAAMD,QAAuC;AACtD,oBAAkB,iCAAiC;EACnD,MAAM,MAAM,IAAI,WAAW;AAC3B,QAAM,IAAI,OAAO;AACjB,SAAO;CACV;;;;CAKD,MAAM,QAAuB;AACzB,oBAAkB,iCAAiC;AACnD,OAAK,KAAK,UAAU;AAChB,SAAM,KAAK,OAAO,OAAO;AACzB,QAAK,WAAW;EACnB;CACJ;;;;CAKD,MAAM,SAAwB;AAC1B,MAAI,KAAK,UAAU;AACf,SAAM,KAAK,OAAO,QAAQ;AAC1B,QAAK,WAAW;EACnB;CACJ;;;;CAKD,YAAsB;AAClB,SAAO,KAAK;CACf;;;;CAKD,MAAM,WAA0B;AAC5B,MAAI,KAAK,UAAU;AACf,SAAM,KAAK,OAAO,UAAU;AAC5B,QAAK,WAAW;EACnB;CACJ;AACJ"}
|